solidus_me 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +151 -29
  3. data/app/controller/spree/admin/orders/labels_controller.rb +124 -0
  4. data/app/controller/spree/admin/solidus_me/accounts_controller.rb +7 -9
  5. data/app/models/solidus_me/account.rb +89 -42
  6. data/app/models/solidus_me/label.rb +155 -0
  7. data/app/models/solidus_me/me_client.rb +14 -0
  8. data/app/models/solidus_me/melhor_envio.rb +18 -140
  9. data/app/models/solidus_me/package_estimator.rb +22 -0
  10. data/app/models/solidus_me/rate_calculator.rb +37 -0
  11. data/app/views/spree/admin/orders/labels/_steps.html.erb +78 -0
  12. data/app/views/spree/admin/orders/labels/index.html.erb +28 -0
  13. data/app/views/spree/admin/orders/labels/show.html.erb +46 -0
  14. data/app/views/spree/admin/orders/labels/step_generate.html.erb +48 -0
  15. data/app/views/spree/admin/orders/labels/step_payment.html.erb +83 -0
  16. data/app/views/spree/admin/orders/labels/step_print.html.erb +48 -0
  17. data/app/views/spree/admin/solidus_me/accounts/_form.html.erb +14 -3
  18. data/app/views/spree/admin/solidus_me/accounts/index.html.erb +11 -6
  19. data/config/routes.rb +9 -0
  20. data/db/migrate/20250801130502_rename_column_token_expires_in.rb +5 -0
  21. data/db/migrate/20250801130700_add_column_test_mode.rb +5 -0
  22. data/db/migrate/20250801142036_create_solidus_me_labels.rb +16 -0
  23. data/db/migrate/20250801170700_add_last_synced_at_to_solidus_me_accounts.rb +5 -0
  24. data/db/migrate/20250801174106_add_timestamps_to_labels.rb +6 -0
  25. data/lib/generators/solidus_me/install/install_generator.rb +4 -0
  26. data/lib/generators/solidus_me/install/templates/app/overrides/spree_order_solidus_me_labels.rb +9 -0
  27. data/lib/solidus_me/version.rb +1 -1
  28. metadata +35 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1f7124426c0b227f783eafc7c7fdd1deda02e76d91a9b8ae6fce7e6eb97d8d1
4
- data.tar.gz: f293dbcab591dc6df18ad5f2e7d1f9155829691b22ed5450690d36221ba3c284
3
+ metadata.gz: e0fdf3ffe57812e34266c15d7074afe0b3a8f61adb492f424f7e9ddde4a80f09
4
+ data.tar.gz: d3073ba5b91e366396acf406c559ac94376a7a586e6fcdc614574ff4aad37bf6
5
5
  SHA512:
6
- metadata.gz: d9ab1662b0e051843e182185bbde45ec63cb1fa93234f78a66fa2cd108fad32037886625df9187965430721fabe7b1c0b066dd6312eeadf17dc8210c1a8842af
7
- data.tar.gz: 4fa4637fbb2055f194f7fc41cd6a7d875636fa5d6bdc5ef7b81ea517cd282a7ee9e425a919a355cb662b00d2f6768ff9125f69fa5b87afd6e2f1876a74d2cce0
6
+ metadata.gz: 68cee3865b4a70b8dbd35a5f5bc887b686e84adc101ce33f3e34a0a9808ae65b9d569d747ea2b2b1c144b85be373c9f6de000d212822c7451420df49ca8e66d7
7
+ data.tar.gz: 7e16935b7e3e79d6f7281b05405a4611ac21070a6a5058460e0c0489a112bb8ecd8c7779dbe8ceb95f187bf44eb7ed39d870f9ebfeb6c744009fcf15be0ea6e4
data/README.md CHANGED
@@ -1,54 +1,176 @@
1
- # Solidus Me
1
+ # SolidusMe
2
2
 
3
- Gem para cotar empresas e valores de entrega junto à Api do Melhor Envio.
3
+ [![Gem Version](https://badge.fury.io/rb/solidus_me.svg)](https://badge.fury.io/rb/solidus_me)
4
4
 
5
- ## Installation
5
+ Extensão do Solidus para integração com a API do [Melhor Envio](https://melhorenvio.com.br/), permitindo:
6
6
 
7
- Adicionar solidus_me ao Gemfile:
7
+ - Cálculo de frete em tempo real
8
+ - Geração de etiquetas de envio
9
+ - Gerenciamento de contas do Melhor Envio
10
+ - Interface administrativa para controle de etiquetas
11
+
12
+ ## Requisitos
13
+
14
+ - Ruby >= 2.7
15
+ - Solidus >= 3.0
16
+ - Rails >= 6.0
17
+
18
+ ## Instalação
19
+
20
+ Adicione esta linha ao Gemfile da sua aplicação:
8
21
 
9
22
  ```ruby
10
23
  gem 'solidus_me'
11
24
  ```
12
25
 
13
- Executar:
26
+ Execute:
27
+
28
+ ```bash
29
+ $ bundle install
30
+ ```
31
+
32
+ Execute o gerador de instalação:
33
+
34
+ ```bash
35
+ $ rails generate solidus_me:install
36
+ ```
37
+
38
+ Execute as migrações:
39
+
40
+ ```bash
41
+ $ rails db:migrate
42
+ ```
43
+
44
+ ## Dependências
45
+
46
+ Esta gem depende de outras gems que precisam ser configuradas antes do uso:
47
+
48
+ ### 1. SolidusTecEstimator
49
+
50
+ Execute o gerador de instalação:
51
+
52
+ ```bash
53
+ $ rails generate solidus_tec_estimator:install
54
+ ```
55
+
56
+ Siga as instruções de configuração da gem **[solidus_tec_estimator](https://github.com/todasessascoisas/solidus_tec_estimator)**.
57
+
58
+ ### 2. SolidusBrazilianAdaptations
59
+
60
+ Execute o gerador de instalação:
61
+
62
+ ```bash
63
+ $ rails generate solidus_brazilian_adaptations:install
64
+ ```
65
+
66
+ Siga as instruções de configuração da gem **[solidus_brazilian_adaptations](https://github.com/todasessascoisas/solidus_brazilian_adaptations)**.
67
+
68
+ ## Configuração
69
+
70
+ ### 1. Configuração da Conta do Melhor Envio
71
+
72
+ Acesse o painel administrativo do Solidus e navegue até **Configurações > Contas Melhor Envio** para configurar suas credenciais da API.
14
73
 
15
- ```shell
16
- bundle
17
- bin/rails generate solidus_me:install
74
+ ### 2. Configuração do Provider de Frete
75
+
76
+ Configure o **TecEstimator** ou outro provider compatível para usar o "Melhor Envio" como provedor de frete.
77
+
78
+ ### 3. Configuração da Interface de Etiquetas
79
+
80
+ Para exibir a aba "Etiqueta" nas ordens do painel administrativo, você precisa adicionar o seguinte código ao arquivo `app/views/spree/admin/shared/_order_submenu.html.erb`:
81
+
82
+ ```erb
83
+ <% if @order.completed? %>
84
+ <% provider_id = @order.shipments.sole.selected_shipping_rate.shipping_method.provider_id %>
85
+ <% if provider_id && SolidusTecEstimator::Provider.find(provider_id).name == "Melhor Envio" %>
86
+ <% label = SolidusMe::Label.find_by(spree_order_id: @order.id) %>
87
+ <li class="tab" data-hook='admin_order_tabs_return_authorizations'>
88
+ <% if label %>
89
+ <a href="<%= admin_orders_label_path(order_number: @order.number, id: label.id) %>">Etiqueta</a>
90
+ <% else %>
91
+ <a href="<%= admin_orders_labels_path(order_number: @order.number) %>">Etiqueta</a>
92
+ <% end %>
93
+ </li>
94
+ <% end %>
95
+ <% end %>
18
96
  ```
19
97
 
20
- Executar o comando para instalar a gem de dependências `solidus_brasilian_adaptations`:
98
+ **Importante:** Se o arquivo `app/views/spree/admin/shared/_order_submenu.html.erb` não existir na sua aplicação, você deve criá-lo copiando o arquivo original do Solidus:
99
+
100
+ 👉 **[Baixar arquivo original do Solidus](https://github.com/solidusio/solidus/blob/main/backend/app/views/spree/admin/shared/_order_submenu.html.erb)**
101
+
102
+ Após baixar, coloque o arquivo no caminho correto da sua aplicação e adicione o código da aba de etiqueta.
103
+
104
+ ## Funcionalidades
105
+
106
+ ### Gerenciamento de Contas
107
+
108
+ - **Múltiplas contas**: Suporte a contas de teste e produção
109
+ - **Autenticação OAuth2**: Integração segura com a API do Melhor Envio
110
+ - **Sincronização automática**: Atualização de saldos e informações da conta
111
+
112
+ ### Etiquetas de Envio
113
+
114
+ - **Geração de etiquetas**: Criação automática de etiquetas após finalização do pedido
115
+ - **Rastreamento**: Acompanhamento do status de entrega
116
+ - **Impressão**: Interface para impressão de etiquetas
117
+ - **Histórico**: Controle completo do ciclo de vida das etiquetas
118
+
119
+ ### Interface Administrativa
120
+
121
+ - **Dashboard de contas**: Visualização de saldos e status das contas
122
+ - **Gestão de etiquetas**: Interface completa para gerenciar etiquetas por pedido
123
+ - **Configurações**: Painel de configuração das integrações
124
+
125
+ ## Modelos
21
126
 
22
- ```shell
23
- bin/rails generate solidus_brazilian_adaptations:install
127
+ ### SolidusMe::Account
128
+
129
+ Representa uma conta do Melhor Envio configurada no sistema.
130
+
131
+ ```ruby
132
+ # Buscar conta ativa em modo de teste
133
+ account = SolidusMe::Account.first_active_test_mode
134
+
135
+ # Verificar saldo da conta
136
+ balance = account.get_balance
137
+
138
+ # Obter dados da conta
139
+ account_data = account.get_account_data
24
140
  ```
25
141
 
26
- Após, seguir o [tutorial](https://github.com/ulysses-bull/solidus_brazilian_adaptations) da gem `soluds_brazilian_adaptations`
142
+ ### SolidusMe::Label
27
143
 
28
- Adicionar no arquivo `/config/initilizers/spree.rb`
144
+ Representa uma etiqueta de envio gerada para um pedido.
29
145
 
30
146
  ```ruby
31
- Spree.config do |config|
32
- +++
33
- config.stock.estimator_class = "SolidusMe::ShippingEstimator"
34
- +++
35
- end
147
+ # Buscar etiqueta por pedido
148
+ label = SolidusMe::Label.find_by(spree_order_id: order.id)
149
+
150
+ # Verificar status da etiqueta
151
+ puts label.status
36
152
  ```
37
153
 
38
- No painel do admin será adiconado o item do `Melhor Envio` no menu lateral. Nesta página deverão ser preenchidos os seguintes campos para autenticação do aplicativo:
154
+ ## Configuração Avançada
39
155
 
40
- ```text
41
- CEP de origem:
42
- Client ID:
43
- Client Secret:
44
- Redirect Url:
45
- State:
156
+ ### Inicializador
157
+
158
+ O gerador cria um arquivo de configuração em `config/initializers/solidus_me.rb`:
159
+
160
+ ```ruby
161
+ SolidusMe.configure do |config|
162
+ # Suas configurações personalizadas
163
+ end
46
164
  ```
47
- Após salvar, o link completo para autenticação do aplicativo ficará disponível abaixo. A gem disponibiliza um endpoint (`HOST/admin/melhor_envio/authorize`) para receber o code e realizar a autenticação automaticamente. Uma vez autorizado, são gerados os `refresh_token` e `access_token`, e o sistema estará pronto para cotação dos fretes junto ao Melhor Envio.
48
165
 
49
- Por padrão, os serviços de entrega são os `SEDEX`, `PAC` e `MiniEnvios` dos CORREIOS. Porém, os serviços podem ser customizados de acordo com a necessidade. Basta adicioná-los aos `preferences` da `Spree::Store`:
166
+ ### Rotas
167
+
168
+ As rotas são automaticamente montadas na aplicação:
50
169
 
51
170
  ```ruby
52
- # Por exemplo
53
- Spree::Store.first.update(preferences: { melhor_envio: { services: ["SEDEX", "PAC", "Mini Envios", ".Com", ".Package"] } })
171
+ # config/routes.rb
172
+ Rails.application.routes.draw do
173
+ mount SolidusMe::Engine => "/solidus_me"
174
+ # suas outras rotas...
175
+ end
54
176
  ```
@@ -0,0 +1,124 @@
1
+ module Spree
2
+ module Admin
3
+ module Orders
4
+ class LabelsController < BaseController
5
+ before_action :load_order
6
+ before_action :set_label, only: [:show, :update, :step_payment, :step_generate, :step_print]
7
+
8
+ def index
9
+ @current_step = 1
10
+ labels = order_labels.recent
11
+ if labels.present?
12
+ @label = labels.first
13
+ redirect_to admin_orders_label_path(@order.number, @label.id)
14
+ end
15
+ end
16
+
17
+ def show
18
+ @current_step = @label.current_step
19
+
20
+ unless @label.status == "completed_label"
21
+ redirect_to step_path
22
+ end
23
+ end
24
+
25
+ def step_payment
26
+ @balance = current_account.get_balance&.balance
27
+ @current_step = 2
28
+ end
29
+
30
+ def step_generate
31
+ @current_step = 3
32
+ end
33
+
34
+ def step_print
35
+ @current_step = 4
36
+ end
37
+
38
+ def create
39
+ @label = ::SolidusMe::Label.find_or_initialize_by(
40
+ spree_order_id: @order.id,
41
+ solidus_me_account_id: current_account.id,
42
+ status: "ready_to_add_to_cart"
43
+ )
44
+
45
+ @label.add_to_cart!
46
+ flash[:success] = "Etiqueta criada e adicionada ao carrinho com sucesso"
47
+ redirect_to admin_orders_step_payment_label_path(@order.number, @label.id)
48
+ rescue StandardError => e
49
+ Rails.logger.error "Failed to create label for order #{@order.number}: #{e.message}"
50
+ flash[:error] = "Erro ao processar etiqueta: #{e.message}"
51
+ redirect_to admin_orders_labels_path(@order.number)
52
+ end
53
+
54
+ def update
55
+ case @label.status
56
+ when "pending"
57
+ process_payment
58
+ when "paid"
59
+ process_generation
60
+ when "generated_label"
61
+ process_printing
62
+ else
63
+ flash[:error] = "Ação não permitida para status atual"
64
+ redirect_to admin_orders_label_path(@order.number, @label.id)
65
+ end
66
+ rescue StandardError => e
67
+ flash[:error] = "Erro ao processar: #{e.message}"
68
+ redirect_to admin_orders_label_path(@order.number, @label.id)
69
+ end
70
+
71
+ private
72
+
73
+ def load_order
74
+ @order = Spree::Order.find_by!(number: params[:order_number])
75
+ end
76
+
77
+ def set_label
78
+ @label = order_labels.find(params[:id])
79
+ end
80
+
81
+ def order_labels
82
+ @order.solidus_me_labels
83
+ end
84
+
85
+ def current_account
86
+ @current_account ||= ::SolidusMe::Account.active.first
87
+ end
88
+
89
+ def step_path
90
+ case @label.status
91
+ when "pending"
92
+ admin_orders_step_payment_label_path(@order.number, @label.id)
93
+ when "paid"
94
+ admin_orders_step_generate_label_path(@order.number, @label.id)
95
+ when "generated_label"
96
+ admin_orders_step_print_label_path(@order.number, @label.id)
97
+ when "completed_label"
98
+ admin_orders_label_path(@order.number, @label.id)
99
+ else
100
+ admin_orders_labels_path(@order.number)
101
+ end
102
+ end
103
+
104
+ def process_payment
105
+ @label.pay!
106
+ flash[:success] = "Etiqueta paga com sucesso"
107
+ redirect_to admin_orders_step_generate_label_path(@order.number, @label.id)
108
+ end
109
+
110
+ def process_generation
111
+ @label.generate!
112
+ flash[:success] = "Etiqueta gerada com sucesso"
113
+ redirect_to admin_orders_step_print_label_path(@order.number, @label.id)
114
+ end
115
+
116
+ def process_printing
117
+ @label.print!
118
+ flash[:success] = "Link de impressão gerado com sucesso"
119
+ redirect_to admin_orders_label_path(@order.number, @label.id)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -44,22 +44,20 @@ module Spree
44
44
  def authorize
45
45
  code = params[:code]
46
46
  me_account = ::SolidusMe::Account.first
47
- authorize_json = MeApi::Client.new.authorize(
48
- client_id: me_account.client_id,
49
- client_secret: me_account.client_secret,
47
+ authorize_response = MeApi::Client.new(client_id: me_account.client_id, client_secret: me_account.client_secret).authorize(
50
48
  code: code,
51
49
  redirect_url: me_account.redirect_url
52
- ).json
50
+ )
53
51
  me_account.update(
54
- access_token: authorize_json["access_token"],
55
- refresh_token: authorize_json["refresh_token"],
56
- token_expires_in: (DateTime.now + authorize_json["expires_in"].seconds).to_s
52
+ access_token: authorize_response.access_token,
53
+ refresh_token: authorize_response.refresh_token,
54
+ token_expires_at: (DateTime.now + authorize_response.expires_in.seconds).to_s
57
55
  )
58
56
  redirect_to edit_admin_solidus_me_account_path me_account
59
57
  end
60
58
 
61
59
  def update_from_api
62
- @me_account.sync_with_api_data
60
+ @me_account.sync_with_api!
63
61
  flash[:success] = "Dados da conta atualizados a partir da API"
64
62
  redirect_to edit_admin_solidus_me_account_path(@me_account.id)
65
63
  rescue StandardError => e
@@ -71,7 +69,7 @@ module Spree
71
69
 
72
70
  def account_params
73
71
  params.require(:account).permit(
74
- :client_id, :client_secret, :access_token, :refresh_token, :redirect_url, :state, :token_expires_in, :services, :postal_code_from, :name, :email, :tax_id, :phone, :address, :address_complement, :address_number, :address_district, :address_city, :address_state_abbr
72
+ :client_id, :client_secret, :access_token, :refresh_token, :redirect_url, :state, :token_expires_at, :services, :postal_code_from, :name, :email, :tax_id, :phone, :address, :address_complement, :address_number, :address_district, :address_city, :address_state_abbr, :test_mode
75
73
  )
76
74
  end
77
75
 
@@ -1,52 +1,99 @@
1
1
  module SolidusMe
2
2
  class Account < ApplicationRecord
3
- def check_token
4
- return if client_id.blank? || client_secret.blank? || refresh_token.blank?
5
- return if (token_expires_in - 1.day) > DateTime.now
6
- refresh
7
- end
8
-
9
- def refresh
10
- refresh_json = MeApi::Client.new.refresh_token(
11
- client_id: client_id,
12
- client_secret: client_secret,
13
- refresh_token: refresh_token
14
- ).json
15
- access_token = refresh_json["access_token"]
16
- refresh_token = refresh_json["refresh_token"]
17
- token_expires_in = DateTime.now.utc + refresh_json["expires_in"].seconds
18
- update(access_token: access_token, refresh_token: refresh_token, token_expires_in: token_expires_in)
19
- end
20
-
21
- def account_data
22
- me_client.get_account_data
23
- end
24
-
25
- def balance
26
- me_client.get_balance
27
- end
28
-
29
- def sync_with_api_data
30
- data = account_data
31
-
32
- update(
33
- name: data.name,
34
- email: data.email,
35
- tax_id: data.document,
36
- phone: data.phone.phone,
37
- address: data.address&.address,
38
- address_complement: data.address&.complement,
39
- address_number: data.address&.number,
40
- address_district: data.address&.district,
41
- address_city: data.address&.city&.city,
42
- address_state_abbr: data.address&.city&.state&.state_abbr
43
- )
3
+ has_many :labels, class_name: "SolidusMe::Label", foreign_key: :solidus_me_account_id, dependent: :destroy
4
+
5
+ validates :client_id, :client_secret, presence: true
6
+ validate :only_one_production_account_active, if: :production_mode?
7
+
8
+ scope :active, -> { where.not(access_token: [nil, ""]) }
9
+ scope :production_mode, -> { where(test_mode: false) }
10
+ scope :test_mode, -> { where(test_mode: true) }
11
+
12
+ scope :first_active_test_mode, -> { test_mode.active.first }
13
+ scope :first_active_production_mode, -> { production_mode.active.first }
14
+
15
+ delegate :get_account_data, :get_balance, to: :me_client
16
+
17
+ def active?
18
+ access_token.present?
19
+ end
20
+
21
+ def test_mode?
22
+ test_mode
23
+ end
24
+
25
+ def production_mode?
26
+ !test_mode?
27
+ end
28
+
29
+ def address_complete?
30
+ [address, address_city, address_state_abbr, postal_code_from].all?(&:present?)
31
+ end
32
+
33
+ def mode_display
34
+ test_mode? ? "Teste" : "Produção"
35
+ end
36
+
37
+ def status_display
38
+ if active?
39
+ "Ativa (#{mode_display})"
40
+ else
41
+ "Inativa"
42
+ end
43
+ end
44
+
45
+ def sync_with_api!
46
+ transaction do
47
+ api_data = get_account_data
48
+ sync_with_api_data(api_data)
49
+ touch(:last_synced_at)
50
+ end
51
+ rescue StandardError => e
52
+ Rails.logger.error "Failed to sync account #{id}: #{e.message}"
53
+ raise
44
54
  end
45
55
 
46
56
  private
47
57
 
48
58
  def me_client
49
- @me_client ||= MeApi::Client.new(access_token)
59
+ @me_client ||= MeClient.for_account(self)
60
+ end
61
+
62
+ def sync_profile_data(api_data)
63
+ assign_attributes(
64
+ name: api_data.name,
65
+ email: api_data.email,
66
+ tax_id: api_data.document,
67
+ phone: api_data.phone&.phone
68
+ )
69
+ end
70
+
71
+ def sync_address_data(api_address)
72
+ return if api_address.nil?
73
+
74
+ assign_attributes(
75
+ address: api_address.address,
76
+ address_complement: api_address.complement,
77
+ address_number: api_address.number,
78
+ address_district: api_address.district,
79
+ address_city: api_address.city&.city,
80
+ address_state_abbr: api_address.city&.state&.state_abbr,
81
+ postal_code_from: api_address.postal_code
82
+ )
83
+ end
84
+
85
+ def sync_with_api_data(api_data)
86
+ sync_profile_data(api_data)
87
+ sync_address_data(api_data.address)
88
+ end
89
+
90
+ def only_one_production_account_active
91
+ return unless access_token.present? && production_mode?
92
+
93
+ existing_production = self.class.production_mode.active.where.not(id: id)
94
+ if existing_production.exists?
95
+ errors.add(:test_mode, "Apenas uma conta de produção pode estar ativa por vez")
96
+ end
50
97
  end
51
98
  end
52
99
  end
@@ -0,0 +1,155 @@
1
+ module SolidusMe
2
+ class Label < ApplicationRecord
3
+ belongs_to :order, foreign_key: :spree_order_id, class_name: "::Spree::Order"
4
+ belongs_to :account, foreign_key: :solidus_me_account_id, class_name: "SolidusMe::Account"
5
+
6
+ validates :status, presence: true
7
+
8
+ scope :pending, -> { where(status: 'pending') }
9
+ scope :paid, -> { where(status: 'paid') }
10
+ scope :generated, -> { where(status: 'generated_label') }
11
+ scope :completed, -> { where(status: 'completed_label') }
12
+ scope :recent, -> { order(created_at: :desc) }
13
+
14
+ def pending?
15
+ status == 'pending'
16
+ end
17
+
18
+ def paid?
19
+ status == 'paid'
20
+ end
21
+
22
+ def generated?
23
+ status == 'generated_label'
24
+ end
25
+
26
+ def completed?
27
+ status == 'completed_label'
28
+ end
29
+
30
+ def can_pay?
31
+ pending?
32
+ end
33
+
34
+ def can_generate?
35
+ paid?
36
+ end
37
+
38
+ def can_print?
39
+ generated?
40
+ end
41
+
42
+ def current_step
43
+ case status
44
+ when "pending"
45
+ 2
46
+ when "paid"
47
+ 3
48
+ when "generated_label"
49
+ 4
50
+ when "completed_label"
51
+ 5
52
+ else
53
+ 1
54
+ end
55
+ end
56
+
57
+ def add_to_cart!
58
+ cart_response = build_cart_request
59
+ update!(
60
+ external_id: cart_response.id,
61
+ price: cart_response.price,
62
+ status: cart_response.status
63
+ )
64
+ end
65
+
66
+ def pay!
67
+ raise "Cannot pay label in #{status} status" unless can_pay?
68
+
69
+ payment_response = me_client.pay_label(external_id)
70
+ update!(
71
+ status: payment_response.purchase.status,
72
+ paid_at: payment_response.purchase.paid_at
73
+ )
74
+ end
75
+
76
+ def generate!
77
+ raise "Cannot generate label in #{status} status" unless can_generate?
78
+
79
+ me_client.generate_label(external_id)
80
+ update!(status: 'generated_label')
81
+ touch(:generated_at)
82
+ end
83
+
84
+ def print!
85
+ raise "Cannot print label in #{status} status" unless can_print?
86
+
87
+ print_response = me_client.print_label(external_id)
88
+ get_response = me_client.get_label(external_id)
89
+
90
+ update!(
91
+ status: 'completed_label',
92
+ link: print_response.first,
93
+ tracking_code: get_response.self_tracking
94
+ )
95
+ touch(:completed_at)
96
+ end
97
+
98
+ private
99
+
100
+ def me_client
101
+ @me_client ||= MeClient.for_account(account)
102
+ end
103
+
104
+ def build_cart_request
105
+ order = Spree::Order.find(spree_order_id)
106
+ address = order.ship_address
107
+ package = Spree::Stock::SimpleCoordinator.new(order).send(:build_packages).sole
108
+ shipping_method = order.shipments.sole.selected_shipping_rate.shipping_method
109
+ service_data = MelhorEnvio::SHIPPING_METHODS.find { |m| m[:name] == shipping_method.name }
110
+ dimensions = PackageEstimator.new(package).estimate_dimensions
111
+
112
+ me_client.add_label_to_cart(
113
+ service_id: service_data[:external_id],
114
+ from_name: account.name,
115
+ from_phone: account.phone,
116
+ from_email: account.email,
117
+ from_address: account.address,
118
+ from_address_complement: account.address_complement,
119
+ from_address_number: account.address_number,
120
+ from_address_district: account.address_district,
121
+ from_address_city: account.address_city,
122
+ from_postal_code: account.postal_code_from,
123
+ from_address_state_abbr: account.address_state_abbr,
124
+ from_tax_id: account.tax_id,
125
+ to_name: address.name,
126
+ to_phone: address.phone,
127
+ to_email: order.email,
128
+ to_address: address.address1,
129
+ to_address_complement: address.address2,
130
+ to_address_number: address.number,
131
+ to_address_district: address.district,
132
+ to_address_city: address.city,
133
+ to_postal_code: address.zipcode,
134
+ to_address_state_abbr: address.state.abbr,
135
+ to_tax_id: order.tax_id,
136
+ volumes: [
137
+ MeApi::Volume.new(
138
+ width_cm: dimensions[0],
139
+ height_cm: dimensions[1],
140
+ length_cm: dimensions[2],
141
+ weight_kg: package.weight / 1000
142
+ )
143
+ ],
144
+ products: order.line_items.map do |line_item|
145
+ MeApi::Product.new(
146
+ name: line_item.name,
147
+ quantity: line_item.quantity,
148
+ unitary_value: line_item.price
149
+ )
150
+ end,
151
+ insurance_value: order.total
152
+ )
153
+ end
154
+ end
155
+ end