Solidus Brazilian Adaptations
Essa gem tem como objetivo adaptar os modelos do Solidus para se adequar aos padrões brasileiros, tais como a criação dos atributos CPF/CNPJ (tax_id) em Spree::Order, e número (number) do imóvel e bairro (district) em Spree::Address.
Installation
Add solidus_brazilian_adaptations to your Gemfile:
gem 'solidus_brazilian_adaptations'
Bundle your dependencies and run the installation generator:
bin/rails generate solidus_brazilian_adaptations:install
Usage
Tradução e moeda padrão
Editar config/initializers/spree.rb
adicionando as seguintes configurações:
Mudar moeda e país padrão:
Spree.config do |config|
# ...
# Define BRL como moeda padrão
config.currency = "BRL"
# Preenche automaticamente o campo de pais que vamos remover do formulário de endereço:
config.default_country_iso = "BR"
# ...
end
Mudar idioma da interface do admin:
Spree::Backend::Config.configure do |config|
# ...
config.locale = 'pt-BR'
# ...
end
Incluir no config/application.rb
para definir os locales disponiveis:
module SuaLoja
class Application < Rails::Application
# ...
config.i18n.available_locales = ['pt-BR']
config.i18n.default_locale = 'pt-BR'
# ...
end
end
Seeds
Substituir Spree::Core::Engine.load_seed
por SolidusBrazilianAdaptations::Engine.load_seed
em db/seeds.rb
para utilizar o seeds da gem.
Configurações
A partir do initializer da gem é possivel configurar se será permitido efetuar compras utilizando CNPJ. Por padrão é essa opção é true
.
Storefront
Essa gem utiliza o starter frontend como base, então os exemplos serão feitos a partir de suas views.
Para adicionar os campos de CPF/CNPJ, número e bairro é possivel editar o form da etapa de endereço. Porém a edição é feita em duas partials diferentes:
Na partial app/views/checkouts/_checkout_step.html.erb
o seguinte HTML referente ao CPF/CNPJ pode ser inserido logo abaixo ao input de e-mail:
<%= form_for order, url: update_checkout_path(order.state), html: { id: "checkout_form_#{order.state}" } do |form| %>
<% if order.state == "address" || !order.email? %>
<div class="text-input">
<%= form.label :email, 'E-Mail:' %>
<%= form.email_field :email, required: true, placeholder: '[email protected]' %>
</div>
<!-- Adicionar -->
<% label = SolidusBrazilianAdaptations.config.allow_cnpj ? "CPF/CPNJ" : "CPF" %>
<div class="text-input" style="margin-top: 4px;">
<%= form.label :tax_id, "#{label}:" %>
<%= form.text_field :tax_id, required: true, placeholder: label %>
</div>
<!-- ********* -->
<% end %>
<%= render "checkouts/steps/#{order.state}_step", form: form, differentiator: @differentiator %>
<% end %>
E na partial app/views/checkouts/steps/address_step/_address_inputs.html.erb
adicionar o seguinte código referente ao número e bairro, podendo ser alterada a ordem dos campos:
<!-- Adicionar -->
<div class="text-input">
<%= form.label :number, "Número:" %>
<%= form.text_field :number, required: true %>
</div>
<!-- ********* -->
<div class="text-input">
<%= form.label :address2, "#{I18n.t("spree.street_address_2")}:" %>
<%= form.text_field :address2, autocomplete: "#{address_type} address-line2" %>
</div>
<!-- Adicionar -->
<div class="text-input">
<%= form.label :district, "Bairro:" %>
<%= form.text_field :district, required: true %>
</div>
<!-- ********* -->
<div class="text-input">
<%= form.label :city, "#{I18n.t("spree.city")}:" %>
<%= form.text_field :city, required: true, autocomplete: "#{address_type} address-level2" %>
</div>
Admin
Para visualizar os campos de CPF/CNPJ, número e bairro no painel do Admin na aba Order/Order_Number/Customer Details
é necessário criar as seguintes views em seu projeto:
Essas novas views irão sobrescrever as padrões, localizadas na gem solidus_backend
. Portanto, você deve ficar atento às atualizações do Solidus, pois ao sobrescrever as views as atualizações não serão aplicadas.
app/views/spree/admin/orders/customer_details/_form.html.erb
<fieldset data-hook="admin_customer_detail_form_fields" class="no-border-top">
<fieldset class="index no-border-bottom" data-hook="customer_guest">
<legend align="center"><%= t('spree.account') %></legend>
<div data-hook="customer_fields" class="row">
<div class="col-9">
<div class="field">
<%= f.label :email %>
<%= f.email_field :email, required: true, class: 'fullwidth' %>
</div>
<!-- Adicionado -->
<div class="field">
<%= f.label :tax_id, "CPF/CNPJ" %>
<%= f.text_field :tax_id, required: true, class: 'fullwidth' %>
</div>
<!-- ********* -->
</div>
<div class="col-3">
<div class="field">
<%= label_tag nil, t('spree.guest_checkout') %>
<ul>
<% if @order.completed? %>
<li>
<%= @order.user.nil? ? t('spree.say_yes') : t('spree.say_no') %>
</li>
<% else %>
<% guest = @order.user.nil? %>
<li>
<label>
<%= radio_button_tag :guest_checkout, true, guest %>
<%= t('spree.say_yes') %>
</label>
</li>
<li>
<label>
<%= radio_button_tag :guest_checkout, false, !guest, disabled: @order.cart? %>
<%= t('spree.say_no') %>
</label>
</li>
<%= hidden_field_tag :user_id, @order.user_id %>
<% end %>
</ul>
</div>
</div>
</div>
</fieldset>
<div class="row">
<% if Spree::Config[:order_bill_address_used] %>
<div class="col-6" data-hook="bill_address_wrapper">
<fieldset class="no-border-bottom">
<legend align="center"><%= t('spree.billing_address') %></legend>
<div class="js-billing-address">
<%= f.fields_for :bill_address do |ba_form| %>
<%= render partial: 'spree/admin/shared/address_form', locals: { f: ba_form, type: "billing" } %>
<% end %>
</div>
</fieldset>
</div>
<% end %>
<div class="col-6" data-hook="ship_address_wrapper">
<fieldset class="no-border-bottom">
<legend align="center"><%= t('spree.shipping_address') %></legend>
<% if Spree::Config[:order_bill_address_used] %>
<div class="field">
<span data-hook="use_billing">
<label>
<%= check_box_tag 'order[use_billing]', '1', (@order.ship_address.new_record? && @order.bill_address == @order.ship_address) %>
<%= t('spree.use_billing_address') %>
</label>
</span>
</div>
<% end %>
<div class="js-shipping-address">
<%= f.fields_for :ship_address do |ba_form| %>
<%= render partial: 'spree/admin/shared/address_form', locals: { f: ba_form, type: "shipping" } %>
<% end %>
</div>
</fieldset>
</div>
</div>
<div class="clear"></div>
<div class="form-buttons filter-actions actions" data-hook="buttons">
<%= button_tag t('spree.actions.update'), class: 'btn btn-primary' %>
</div>
</fieldset>
app/views/spree/admin/shared/_address_form.html.erb
<% s_or_b = type.chars.first %>
<div id="<%= type %>" data-hook="address_fields">
<div class="field <%= "#{type}-row" %>">
<%= f.label :name %>
<%= f.text_field :name, class: 'fullwidth' %>
</div>
<% if Spree::Config[:company] %>
<div class="field <%= "#{type}-row" %>">
<%= f.label :company %>
<%= f.text_field :company, class: 'fullwidth' %>
</div>
<% end %>
<div class="field <%= "#{type}-row" %>">
<%= f.label :address1 %>
<%= f.text_field :address1, class: 'fullwidth' %>
</div>
<!-- Adicionado -->
<div class="field <%= "#{type}-row" %>">
<%= f.label :number, "Número" %>
<%= f.text_field :number, class: 'fullwidth' %>
</div>
<!-- ********** -->
<div class="field <%= "#{type}-row" %>">
<%= f.label :address2 %>
<%= f.text_field :address2, class: 'fullwidth' %>
</div>
<!-- Adicionado -->
<div class="field <%= "#{type}-row" %>">
<%= f.label :district, "Bairro" %>
<%= f.text_field :district, class: 'fullwidth' %>
</div>
<!-- ********** -->
<div class="field <%= "#{type}-row" %>">
<%= f.label :city %>
<%= f.text_field :city, class: 'fullwidth' %>
</div>
<div class="field <%= "#{type}-row" %>">
<%= f.label :zipcode %>
<%= f.text_field :zipcode, class: 'fullwidth' %>
</div>
<div class="field <%= "#{type}-row" %>">
<%= f.label :country_id, Spree::Country.model_name.human %>
<span id="<%= s_or_b %>country">
<%= f.collection_select :country_id, available_countries, :id, :name, {}, {class: 'custom-select fullwidth js-country_id'} %>
</span>
</div>
<div class="field <%= "#{type}-row" %>">
<%= f.label :state_id, Spree::State.model_name.human %>
<span id="<%= s_or_b %>state">
<%= f.hidden_field :state_name, value: nil %>
<% states = f.object.country.try(:states).nil? ? [] : f.object.country.states %>
<%= f.text_field :state_name,
style: "display: #{states.empty? ? 'block' : 'none' };",
disabled: !states.empty?, class: 'fullwidth state_name js-state_name' %>
<%= f.hidden_field :state_id, value: nil %>
<%= f.collection_select :state_id,
states.sort,
:id, :name,
{ include_blank: true },
{ class: 'custom-select fullwidth js-state_id',
style: "display: #{states.empty? ? 'none' : 'block' };",
disabled: states.empty? } %>
</span>
</div>
<div class="field <%= "#{type}-row" %>">
<%= f.label :phone %>
<%= f.phone_field :phone, class: 'fullwidth' %>
</div>
</div>
Development
Testing the extension
First bundle your dependencies, then run bin/rake
. bin/rake
will default to building the dummy
app if it does not exist, then it will run specs. The dummy app can be regenerated by using
bin/rake extension:test_app
.
bin/rake
To run Rubocop static code analysis run
bundle exec rubocop
When testing your application's integration with this extension you may use its factories. You can load Solidus core factories along with this extension's factories using this statement:
SolidusDevSupport::TestingSupport::Factories.load_for(SolidusBrazilianAdaptations::Engine)
Running the sandbox
To run this extension in a sandboxed Solidus application, you can run bin/sandbox
. The path for
the sandbox app is ./sandbox
and bin/rails
will forward any Rails commands to
sandbox/bin/rails
.
Here's an example:
$ bin/rails server
=> Booting Puma
=> Rails 6.0.2.1 application starting in development
* Listening on tcp://127.0.0.1:3000
Use Ctrl-C to stop
Releasing new versions
Please refer to the dedicated page in the Solidus wiki.
License
Copyright (c) 2023 ulysses-bull, released under the New BSD License.