solidus_me 3.6.1 → 3.7.1
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.
- checksums.yaml +4 -4
- data/README.md +151 -29
- data/app/controller/spree/admin/orders/labels_controller.rb +124 -0
- data/app/controller/spree/admin/solidus_me/accounts_controller.rb +7 -9
- data/app/models/solidus_me/account.rb +89 -43
- data/app/models/solidus_me/label.rb +155 -0
- data/app/models/solidus_me/me_client.rb +47 -0
- data/app/models/solidus_me/melhor_envio.rb +18 -140
- data/app/models/solidus_me/package_estimator.rb +22 -0
- data/app/models/solidus_me/rate_calculator.rb +37 -0
- data/app/views/spree/admin/orders/labels/_steps.html.erb +78 -0
- data/app/views/spree/admin/orders/labels/index.html.erb +28 -0
- data/app/views/spree/admin/orders/labels/show.html.erb +46 -0
- data/app/views/spree/admin/orders/labels/step_generate.html.erb +48 -0
- data/app/views/spree/admin/orders/labels/step_payment.html.erb +83 -0
- data/app/views/spree/admin/orders/labels/step_print.html.erb +48 -0
- data/app/views/spree/admin/solidus_me/accounts/_form.html.erb +14 -3
- data/app/views/spree/admin/solidus_me/accounts/index.html.erb +11 -6
- data/config/routes.rb +9 -0
- data/db/migrate/20250801130502_rename_column_token_expires_in.rb +5 -0
- data/db/migrate/20250801130700_add_column_test_mode.rb +5 -0
- data/db/migrate/20250801142036_create_solidus_me_labels.rb +16 -0
- data/db/migrate/20250801170700_add_last_synced_at_to_solidus_me_accounts.rb +5 -0
- data/db/migrate/20250801174106_add_timestamps_to_labels.rb +6 -0
- data/lib/generators/solidus_me/install/install_generator.rb +4 -0
- data/lib/generators/solidus_me/install/templates/app/overrides/spree_order_solidus_me_labels.rb +9 -0
- data/lib/solidus_me/version.rb +1 -1
- metadata +35 -4
@@ -0,0 +1,47 @@
|
|
1
|
+
module SolidusMe
|
2
|
+
class MeClient
|
3
|
+
def self.for_account(account)
|
4
|
+
client_class = account.test_mode? ? MeClientSandbox : MeClientProduction
|
5
|
+
client_class.new(account)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module AccountSyncable
|
10
|
+
attr_accessor :account
|
11
|
+
|
12
|
+
def initialize(account)
|
13
|
+
@account = account
|
14
|
+
super(
|
15
|
+
access_token: account.access_token,
|
16
|
+
refresh: account.refresh_token,
|
17
|
+
client_id: account.client_id,
|
18
|
+
client_secret: account.client_secret,
|
19
|
+
token_expires_at: account.token_expires_at,
|
20
|
+
test_mode: account.test_mode
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def refresh_token
|
25
|
+
super
|
26
|
+
sync_account_tokens
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def sync_account_tokens
|
32
|
+
account.update!(
|
33
|
+
access_token: access_token,
|
34
|
+
refresh_token: refresh,
|
35
|
+
token_expires_at: token_expires_at
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class MeClientSandbox < ::MeApi::ClientSandbox
|
41
|
+
include AccountSyncable
|
42
|
+
end
|
43
|
+
|
44
|
+
class MeClientProduction < ::MeApi::ClientProduction
|
45
|
+
include AccountSyncable
|
46
|
+
end
|
47
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module SolidusMe
|
2
|
-
|
3
2
|
class MelhorEnvio
|
4
3
|
SHIPPING_METHODS = [
|
5
4
|
{ name: "Correios PAC", carrier: "Correios", service_level: "PAC", external_id: 1 },
|
@@ -13,7 +12,7 @@ module SolidusMe
|
|
13
12
|
def initialize(package, shipping_methods)
|
14
13
|
@package = package
|
15
14
|
@shipping_methods = shipping_methods
|
16
|
-
@me_account =
|
15
|
+
@me_account = me_account || set_default_me_account
|
17
16
|
end
|
18
17
|
|
19
18
|
def self.generate_code external_id
|
@@ -36,164 +35,43 @@ module SolidusMe
|
|
36
35
|
end
|
37
36
|
|
38
37
|
def shipping_rates
|
39
|
-
melhor_envio_rates =
|
40
|
-
|
41
|
-
|
38
|
+
melhor_envio_rates = RateCalculator.new(package, me_account).calculate
|
39
|
+
valid_melhor_envio_rates = select_valid_shipping_rates(melhor_envio_rates)
|
40
|
+
shipping_rates = valid_melhor_envio_rates.map do |melhor_envio_rate|
|
41
|
+
build_shipping_rate(melhor_envio_rate)
|
42
42
|
end.compact
|
43
43
|
|
44
44
|
shipping_rates
|
45
45
|
end
|
46
46
|
|
47
|
-
def insert_package_into_cart from
|
48
|
-
me_client = set_me_client
|
49
|
-
order = package.order
|
50
|
-
line_items = order.line_items
|
51
|
-
address = order.ship_address
|
52
|
-
dimensions = estimate_dimensions_from_package(package)
|
53
|
-
tax_id_from = cleaned_tax_id(@me_account.tax_id)
|
54
|
-
tax_id_to = cleaned_tax_id(order.tax_id)
|
55
|
-
|
56
|
-
shipping_method_name = @shipping_methods.sole.name
|
57
|
-
service_id = SHIPPING_METHODS.filter { _1[:name] == shipping_method_name }.sole[:external_id]
|
58
|
-
|
59
|
-
from = {
|
60
|
-
name: @me_account.name,
|
61
|
-
phone: @me_account.phone,
|
62
|
-
email: @me_account.email,
|
63
|
-
address: @me_account.address,
|
64
|
-
complement: @me_account.address_complement,
|
65
|
-
number: @me_account.address_number,
|
66
|
-
district: @me_account.address_district,
|
67
|
-
city: @me_account.address_city,
|
68
|
-
postal_code: @me_account.postal_code_from,
|
69
|
-
state_abbr: @me_account.address_state_abbr
|
70
|
-
}
|
71
|
-
|
72
|
-
document_key = tax_id_from.length == 11 ? :document : :company_document
|
73
|
-
from[document_key] = tax_id_from
|
74
|
-
|
75
|
-
to = {
|
76
|
-
name: address.name,
|
77
|
-
phone: address.phone,
|
78
|
-
email: order.email,
|
79
|
-
address: address.address1,
|
80
|
-
complement: address.address2,
|
81
|
-
number: address.number,
|
82
|
-
district: address.district,
|
83
|
-
city: address.city,
|
84
|
-
postal_code: address.zipcode,
|
85
|
-
state_abbr: address.state.abbr
|
86
|
-
}
|
87
|
-
|
88
|
-
document_key = tax_id_to.length == 11 ? :document : :company_document
|
89
|
-
to[document_key] = tax_id_to
|
90
|
-
|
91
|
-
products = line_items.map do |line_item|
|
92
|
-
{
|
93
|
-
name: line_item.name,
|
94
|
-
quantity: line_item.quantity,
|
95
|
-
unitary_value: line_item.price
|
96
|
-
}
|
97
|
-
end
|
98
|
-
|
99
|
-
volumes = [{
|
100
|
-
width: dimensions[0].to_i.round(2),
|
101
|
-
height: dimensions[1].to_i.round(2),
|
102
|
-
length: dimensions[2].to_i.round(2),
|
103
|
-
weight: package.weight / 1000
|
104
|
-
}]
|
105
|
-
|
106
|
-
options = {
|
107
|
-
insurance_value: order.total,
|
108
|
-
}
|
109
|
-
|
110
|
-
me_client.insert_package_into_cart(service_id:, from:, to:, products:, volumes:, options:)
|
111
|
-
end
|
112
|
-
|
113
|
-
def payment_ticket(ticket_id)
|
114
|
-
me_client = set_me_client
|
115
|
-
me_client.payment_ticket ticket_id
|
116
|
-
end
|
117
|
-
|
118
|
-
def generate_ticket(ticket_id)
|
119
|
-
me_client = set_me_client
|
120
|
-
me_client.generate_ticket ticket_id
|
121
|
-
end
|
122
|
-
|
123
|
-
def print_ticket(ticket_id)
|
124
|
-
me_client = set_me_client
|
125
|
-
me_client.print_ticket ticket_id
|
126
|
-
end
|
127
|
-
|
128
|
-
def get_ticket(ticket_id)
|
129
|
-
me_client = set_me_client
|
130
|
-
me_client.get_ticket ticket_id
|
131
|
-
end
|
132
|
-
|
133
47
|
private
|
134
48
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
@me_account.check_token
|
139
|
-
MeApi::Client.new(@me_account.access_token)
|
140
|
-
end
|
141
|
-
|
142
|
-
def cleaned_tax_id(tax_id)
|
143
|
-
tax_id.gsub(/\D/, "")
|
49
|
+
def set_default_me_account
|
50
|
+
@default_me_account ||= Rails.env.production? ? SolidusMe::Account.first_active_production_mode : SolidusMe::Account.first_active_test_mode
|
144
51
|
end
|
145
52
|
|
146
|
-
def
|
147
|
-
me_client = set_me_client
|
148
|
-
price = package.contents.map { |content| content.price }.sum
|
149
|
-
weight = package.weight
|
150
|
-
dimensions = estimate_dimensions_from_package(package)
|
151
|
-
|
152
|
-
zipcode = package.order.ship_address.zipcode
|
53
|
+
def select_valid_shipping_rates(melhor_envio_rates)
|
153
54
|
available_shipping_method_codes = @shipping_methods.pluck(:code)
|
154
|
-
|
155
|
-
from: @me_account.postal_code_from,
|
156
|
-
to: zipcode,
|
157
|
-
weight_kg: (weight / 1000.00),
|
158
|
-
width_cm: dimensions[0].to_i.round(2),
|
159
|
-
height_cm: dimensions[1].to_i.round(2),
|
160
|
-
length_cm: dimensions[2].to_i.round(2),
|
161
|
-
contents_value_brl: price
|
162
|
-
)
|
163
|
-
rates.select { |rate| rate.price > 0 && available_shipping_method_codes.include?(self.class.generate_code(rate.id)) }
|
164
|
-
rescue
|
165
|
-
[]
|
166
|
-
end
|
167
|
-
|
168
|
-
def estimate_dimensions_from_package(package)
|
169
|
-
volume = package.contents.sum { |item| dimensions_from_item(item).reduce(:*) }
|
170
|
-
cube_side = Math.cbrt(volume)
|
171
|
-
[cube_side, cube_side, cube_side]
|
55
|
+
melhor_envio_rates.select { |rate| rate.try(:price) && rate.price&.to_f > 0 && available_shipping_method_codes.include?(self.class.generate_code(rate.id)) }
|
172
56
|
end
|
173
57
|
|
174
|
-
def
|
175
|
-
|
176
|
-
height = item.variant.height || 10
|
177
|
-
depth = item.variant.depth || 10
|
178
|
-
[width, height, depth]
|
179
|
-
end
|
180
|
-
|
181
|
-
def build_shipping_rate(melhor_envio_rate, package)
|
182
|
-
return unless melhor_envio_rate.max_delivery_time
|
58
|
+
def build_shipping_rate(melhor_envio_rate)
|
59
|
+
return unless melhor_envio_rate.delivery_range.max.to_s
|
183
60
|
shipping_method = Spree::ShippingMethod.find_by(code: self.class.generate_code(melhor_envio_rate.id))
|
184
|
-
estimate_prose = "Chegará em até #{ActionController::Base.helpers.pluralize(melhor_envio_rate.max_delivery_time, "dia", plural: "dias")} úteis"
|
185
61
|
|
186
62
|
Spree::ShippingRate.new(
|
187
63
|
shipment: package.shipment,
|
188
64
|
shipping_method: shipping_method,
|
189
|
-
cost: melhor_envio_rate.price,
|
190
|
-
min_delivery_time: melhor_envio_rate.
|
191
|
-
max_delivery_time: melhor_envio_rate.
|
192
|
-
estimate_prose: estimate_prose,
|
65
|
+
cost: melhor_envio_rate.price&.to_f,
|
66
|
+
min_delivery_time: melhor_envio_rate.delivery_range.min.business_days.from_now,
|
67
|
+
max_delivery_time: melhor_envio_rate.delivery_range.max.business_days.from_now,
|
68
|
+
estimate_prose: estimate_prose(melhor_envio_rate),
|
193
69
|
expires_in: Time.now + 2.hours
|
194
70
|
)
|
195
71
|
end
|
196
72
|
|
73
|
+
def estimate_prose(melhor_envio_rate)
|
74
|
+
"Chegará em até #{ActionController::Base.helpers.pluralize(melhor_envio_rate.delivery_range.max.to_s, "dia", plural: "dias")} úteis"
|
75
|
+
end
|
197
76
|
end
|
198
|
-
|
199
77
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SolidusMe
|
2
|
+
class PackageEstimator
|
3
|
+
attr_reader :package
|
4
|
+
|
5
|
+
def initialize(package)
|
6
|
+
@package = package
|
7
|
+
end
|
8
|
+
|
9
|
+
def estimate_dimensions
|
10
|
+
volume = package.contents.sum { |item| dimensions_from_item(item).reduce(:*) }
|
11
|
+
cube_side = Math.cbrt(volume)
|
12
|
+
[cube_side, cube_side, cube_side]
|
13
|
+
end
|
14
|
+
|
15
|
+
def dimensions_from_item(item)
|
16
|
+
width = (item.variant.width.to_i || 20).round(2)
|
17
|
+
height = (item.variant.height.to_i || 10).round(2)
|
18
|
+
depth = (item.variant.depth.to_i || 10).round(2)
|
19
|
+
[width, height, depth]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SolidusMe
|
2
|
+
class RateCalculator
|
3
|
+
attr_reader :package, :me_account
|
4
|
+
def initialize(package, me_account)
|
5
|
+
@package = package
|
6
|
+
raise "No Melhor Envio account found" unless me_account
|
7
|
+
@me_account = me_account
|
8
|
+
end
|
9
|
+
|
10
|
+
def calculate
|
11
|
+
price = package.contents.map { |content| content.price }.sum
|
12
|
+
weight = package.weight
|
13
|
+
dimensions = SolidusMe::PackageEstimator.new(package).estimate_dimensions
|
14
|
+
zipcode = package.order.ship_address.zipcode
|
15
|
+
rates = me_client.rates(
|
16
|
+
from_postal_code: me_account.postal_code_from,
|
17
|
+
to_postal_code: zipcode,
|
18
|
+
volume: MeApi::Volume.new(
|
19
|
+
width_cm: dimensions[0],
|
20
|
+
height_cm: dimensions[1],
|
21
|
+
length_cm: dimensions[2],
|
22
|
+
weight_kg: (weight / 1000.00)
|
23
|
+
),
|
24
|
+
contents_value_brl: price
|
25
|
+
)
|
26
|
+
rates
|
27
|
+
rescue
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def me_client
|
34
|
+
@me_client ||= MeClient.for_account(me_account)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
<div style="display: flex; justify-content: center; width: 100%; margin: 20px 0;">
|
2
|
+
<div style="background: white; padding: 20px 25px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); border: 1px solid #e9ecef; max-width: 600px; width: 100%;">
|
3
|
+
|
4
|
+
<div style="display: flex; justify-content: space-between; align-items: center; position: relative;">
|
5
|
+
|
6
|
+
<div style="display: flex; flex-direction: column; align-items: center; z-index: 3; position: relative;">
|
7
|
+
<div style="width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px;
|
8
|
+
background: <%= current_step > 1 ? 'linear-gradient(135deg, #28a745, #20c997)' : (current_step == 1 ? '#007bff' : '#e9ecef') %>;
|
9
|
+
color: <%= current_step >= 1 ? 'white' : '#6c757d' %>;
|
10
|
+
box-shadow: <%= current_step > 1 ? '0 2px 6px rgba(40, 167, 69, 0.3)' : (current_step == 1 ? '0 2px 6px rgba(0, 123, 255, 0.3)' : '0 1px 3px rgba(0,0,0,0.1)') %>;
|
11
|
+
border: 2px solid <%= current_step > 1 ? '#28a745' : (current_step == 1 ? '#007bff' : '#dee2e6') %>;
|
12
|
+
transition: all 0.3s ease;">
|
13
|
+
<%= current_step > 1 ? '✓' : '1' %>
|
14
|
+
</div>
|
15
|
+
<small style="margin-top: 6px; font-weight: 500; color: <%= current_step > 1 ? '#28a745' : (current_step == 1 ? '#007bff' : '#6c757d') %>; font-size: 9px; text-align: center; line-height: 1.1; max-width: 50px;">
|
16
|
+
Adicionar<br>Carrinho
|
17
|
+
</small>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<div style="display: flex; flex-direction: column; align-items: center; z-index: 3; position: relative;">
|
21
|
+
<div style="width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px;
|
22
|
+
background: <%= current_step > 2 ? 'linear-gradient(135deg, #28a745, #20c997)' : (current_step == 2 ? '#007bff' : '#e9ecef') %>;
|
23
|
+
color: <%= current_step >= 2 ? 'white' : '#6c757d' %>;
|
24
|
+
box-shadow: <%= current_step > 2 ? '0 2px 6px rgba(40, 167, 69, 0.3)' : (current_step == 2 ? '0 2px 6px rgba(0, 123, 255, 0.3)' : '0 1px 3px rgba(0,0,0,0.1)') %>;
|
25
|
+
border: 2px solid <%= current_step > 2 ? '#28a745' : (current_step == 2 ? '#007bff' : '#dee2e6') %>;
|
26
|
+
transition: all 0.3s ease;">
|
27
|
+
<%= current_step > 2 ? '✓' : '2' %>
|
28
|
+
</div>
|
29
|
+
<small style="margin-top: 6px; font-weight: 500; color: <%= current_step > 2 ? '#28a745' : (current_step == 2 ? '#007bff' : '#6c757d') %>; font-size: 9px; text-align: center; line-height: 1.1; max-width: 50px;">
|
30
|
+
Pagar<br>Etiqueta
|
31
|
+
</small>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div style="display: flex; flex-direction: column; align-items: center; z-index: 3; position: relative;">
|
35
|
+
<div style="width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px;
|
36
|
+
background: <%= current_step > 3 ? 'linear-gradient(135deg, #28a745, #20c997)' : (current_step == 3 ? '#007bff' : '#e9ecef') %>;
|
37
|
+
color: <%= current_step >= 3 ? 'white' : '#6c757d' %>;
|
38
|
+
box-shadow: <%= current_step > 3 ? '0 2px 6px rgba(40, 167, 69, 0.3)' : (current_step == 3 ? '0 2px 6px rgba(0, 123, 255, 0.3)' : '0 1px 3px rgba(0,0,0,0.1)') %>;
|
39
|
+
border: 2px solid <%= current_step > 3 ? '#28a745' : (current_step == 3 ? '#007bff' : '#dee2e6') %>;
|
40
|
+
transition: all 0.3s ease;">
|
41
|
+
<%= current_step > 3 ? '✓' : '3' %>
|
42
|
+
</div>
|
43
|
+
<small style="margin-top: 6px; font-weight: 500; color: <%= current_step > 3 ? '#28a745' : (current_step == 3 ? '#007bff' : '#6c757d') %>; font-size: 9px; text-align: center; line-height: 1.1; max-width: 50px;">
|
44
|
+
Gerar<br>Etiqueta
|
45
|
+
</small>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<div style="display: flex; flex-direction: column; align-items: center; z-index: 3; position: relative;">
|
49
|
+
<div style="width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px;
|
50
|
+
background: <%= current_step > 4 ? 'linear-gradient(135deg, #28a745, #20c997)' : (current_step == 4 ? '#007bff' : '#e9ecef') %>;
|
51
|
+
color: <%= current_step >= 4 ? 'white' : '#6c757d' %>;
|
52
|
+
box-shadow: <%= current_step > 4 ? '0 2px 6px rgba(40, 167, 69, 0.3)' : (current_step == 4 ? '0 2px 6px rgba(0, 123, 255, 0.3)' : '0 1px 3px rgba(0,0,0,0.1)') %>;
|
53
|
+
border: 2px solid <%= current_step > 4 ? '#28a745' : (current_step == 4 ? '#007bff' : '#dee2e6') %>;
|
54
|
+
transition: all 0.3s ease;">
|
55
|
+
<%= current_step > 4 ? '✓' : '4' %>
|
56
|
+
</div>
|
57
|
+
<small style="margin-top: 6px; font-weight: 500; color: <%= current_step > 4 ? '#28a745' : (current_step == 4 ? '#007bff' : '#6c757d') %>; font-size: 9px; text-align: center; line-height: 1.1; max-width: 50px;">
|
58
|
+
Gerar<br>Link
|
59
|
+
</small>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div style="display: flex; flex-direction: column; align-items: center; z-index: 3; position: relative;">
|
63
|
+
<div style="width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px;
|
64
|
+
background: <%= current_step >= 5 ? 'linear-gradient(135deg, #28a745, #20c997)' : '#e9ecef' %>;
|
65
|
+
color: <%= current_step >= 5 ? 'white' : '#6c757d' %>;
|
66
|
+
box-shadow: <%= current_step >= 5 ? '0 2px 6px rgba(40, 167, 69, 0.3)' : '0 1px 3px rgba(0,0,0,0.1)' %>;
|
67
|
+
border: 2px solid <%= current_step >= 5 ? '#28a745' : '#dee2e6' %>;
|
68
|
+
transition: all 0.3s ease;">
|
69
|
+
<%= current_step >= 5 ? '✓' : '5' %>
|
70
|
+
</div>
|
71
|
+
<small style="margin-top: 6px; font-weight: 500; color: <%= current_step >= 5 ? '#28a745' : '#6c757d' %>; font-size: 9px; text-align: center; line-height: 1.1; max-width: 50px;">
|
72
|
+
Etiqueta<br>Finalizada
|
73
|
+
</small>
|
74
|
+
</div>
|
75
|
+
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
</div>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Etiquetas' } %>
|
2
|
+
|
3
|
+
<% admin_breadcrumb("Etiquetas") %>
|
4
|
+
|
5
|
+
<div data-order-number="<%= @order.number %>" style="text-align: center !important; display: flex; flex-direction: column; align-items: center; width: 100%;">
|
6
|
+
<div style="width: 100%; max-width: 800px;">
|
7
|
+
<%= render partial: 'steps', locals: { current_step: @current_step } %>
|
8
|
+
|
9
|
+
<fieldset class="no-border-bottom" style="text-align: center !important;">
|
10
|
+
<legend style="text-align: center !important; width: 100%; margin: 0 auto;">Passo 1: Adicionar ao Carrinho do Melhor Envio</legend>
|
11
|
+
|
12
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
13
|
+
<p style="text-align: center !important; margin: 0 auto;">Para começar, vamos adicionar os itens deste pedido ao carrinho do Melhor Envio. Isso irá calcular o valor do frete e preparar a etiqueta.</p>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div style="text-align: center !important; width: 100%; display: flex; justify-content: center;">
|
17
|
+
<%= form_with url: admin_orders_create_label_url do |form| %>
|
18
|
+
<%= form.hidden_field :order_number, value: @order.number %>
|
19
|
+
<div class="form-buttons filter-actions actions" data-hook="buttons" style="text-align: center !important;">
|
20
|
+
<%= form.submit "Adicionar ao Carrinho do Melhor Envio", class: "btn btn-primary mt-2", style: "margin: 0 auto; display: block;" %>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
24
|
+
</fieldset>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<%= render partial: 'spree/shared/error_messages', locals: { target: @order } %>
|
28
|
+
</div>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<%= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Etiqueta' } %>
|
2
|
+
|
3
|
+
<% admin_breadcrumb("Etiqueta") %>
|
4
|
+
|
5
|
+
<div data-order-number="<%= @order.number %>" style="text-align: center !important; display: flex; flex-direction: column; align-items: center; width: 100%;">
|
6
|
+
<div style="width: 100%; max-width: 800px;">
|
7
|
+
<%= render partial: 'steps', locals: { current_step: @current_step } %>
|
8
|
+
|
9
|
+
<fieldset class="no-border-bottom" style="text-align: center !important;">
|
10
|
+
<legend style="text-align: center !important; width: 100%; margin: 0 auto;">Etiqueta Finalizada</legend>
|
11
|
+
|
12
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
13
|
+
<p style="text-align: center !important; margin: 0 auto;">Sua etiqueta está pronta para impressão e o código de rastreio foi gerado.</p>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div style="display: flex; justify-content: center; margin-bottom: 20px; width: 100%;">
|
17
|
+
<div style="display: flex; gap: 40px; align-items: center; justify-content: center;">
|
18
|
+
<div style="display: flex; flex-direction: column; align-items: center; text-align: center;">
|
19
|
+
<label style="text-align: center;">Valor pago pelo cliente:</label>
|
20
|
+
<h3 style="text-align: center; margin: 5px 0;"><%= number_to_currency @label.order.shipment_total %></h3>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div style="display: flex; flex-direction: column; align-items: center; text-align: center;">
|
24
|
+
<label style="text-align: center;">Preço da etiqueta:</label>
|
25
|
+
<h3 style="text-align: center; margin: 5px 0;"><%= number_to_currency @label.price %></h3>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<% if @label.link %>
|
31
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
32
|
+
<%= link_to "📄 Abrir Etiqueta para Impressão", @label.link, target: "_blank", class: "button", style: "margin: 0 auto; display: inline-block;" %>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
<% if @label.tracking_code %>
|
37
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
38
|
+
<label style="text-align: center !important;">Código de rastreio:</label>
|
39
|
+
<p style="text-align: center !important; margin: 5px 0;"><strong style="font-size: 16px; color: #007bff;"><%= @label.tracking_code %></strong></p>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
</fieldset>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<%= render partial: 'spree/shared/error_messages', locals: { target: @order } %>
|
46
|
+
</div>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<%= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Etiquetas' } %>
|
2
|
+
|
3
|
+
<% admin_breadcrumb("Etiquetas") %>
|
4
|
+
|
5
|
+
<div data-order-number="<%= @order.number %>" style="text-align: center !important; display: flex; flex-direction: column; align-items: center; width: 100%;">
|
6
|
+
<div style="width: 100%; max-width: 800px;">
|
7
|
+
<%= render partial: 'steps', locals: { current_step: @current_step } %>
|
8
|
+
|
9
|
+
<fieldset class="no-border-bottom" style="text-align: center !important;">
|
10
|
+
<legend style="text-align: center !important; width: 100%; margin: 0 auto;">Passo 3: Gerar Etiqueta</legend>
|
11
|
+
|
12
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
13
|
+
<p style="text-align: center !important; margin: 0 auto;">O pagamento foi confirmado. Agora vamos gerar a etiqueta de envio no sistema do Melhor Envio.</p>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div style="display: flex; justify-content: center; margin-bottom: 20px; width: 100%;">
|
17
|
+
<div style="display: flex; gap: 40px; align-items: center; justify-content: center;">
|
18
|
+
<div style="display: flex; flex-direction: column; align-items: center; text-align: center;">
|
19
|
+
<label style="text-align: center;">Valor pago pelo cliente:</label>
|
20
|
+
<h3 style="text-align: center; margin: 5px 0;"><%= number_to_currency @label.order.shipment_total %></h3>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div style="display: flex; flex-direction: column; align-items: center; text-align: center;">
|
24
|
+
<label style="text-align: center;">Preço da etiqueta:</label>
|
25
|
+
<h3 style="text-align: center; margin: 5px 0;"><%= number_to_currency @label.price %></h3>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
31
|
+
<label style="text-align: center !important;">Status do Pagamento:</label>
|
32
|
+
<p style="color: #28a745; font-weight: bold; text-align: center !important; margin: 5px 0;">✓ PAGO</p>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<hr style="margin: 20px auto; width: 80%; border: 1px solid #dee2e6;">
|
36
|
+
|
37
|
+
<div style="text-align: center !important; width: 100%; display: flex; justify-content: center;">
|
38
|
+
<%= form_with url: admin_orders_update_label_url(@order.number, @label.id), method: :patch, local: true do |form| %>
|
39
|
+
<div class="form-buttons filter-actions actions" data-hook="buttons" style="text-align: center !important;">
|
40
|
+
<%= form.submit "Gerar Etiqueta", class: "btn btn-primary", style: "margin: 0 auto; display: block;" %>
|
41
|
+
</div>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</fieldset>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<%= render partial: 'spree/shared/error_messages', locals: { target: @order } %>
|
48
|
+
</div>
|
@@ -0,0 +1,83 @@
|
|
1
|
+
<%= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Etiquetas' } %>
|
2
|
+
|
3
|
+
<% admin_breadcrumb("Etiquetas") %>
|
4
|
+
|
5
|
+
<div data-order-number="<%= @order.number %>" style="text-align: center !important; display: flex; flex-direction: column; align-items: center; width: 100%;">
|
6
|
+
<div style="width: 100%; max-width: 800px;">
|
7
|
+
<%= render partial: 'steps', locals: { current_step: @current_step } %>
|
8
|
+
|
9
|
+
<fieldset class="no-border-bottom" style="text-align: center !important;">
|
10
|
+
<legend style="text-align: center !important; width: 100%; margin: 0 auto;">Passo 2: Pagar Etiqueta</legend>
|
11
|
+
|
12
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
13
|
+
<p style="text-align: center !important; margin: 0 auto;">Os itens foram adicionados ao carrinho com sucesso! Agora vamos efetuar o pagamento da etiqueta no Melhor Envio.</p>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div style="display: flex; justify-content: center; margin-bottom: 25px; width: 100%;">
|
17
|
+
<div style="display: flex; gap: 60px; align-items: center; justify-content: center;">
|
18
|
+
<div style="text-align: center; padding: 16px; border-radius: 6px; background: #fafbfc; border: 1px solid #e8eaed;">
|
19
|
+
<div style="color: #5f6368; font-size: 12px; font-weight: 500; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.3px;">
|
20
|
+
Valor pago pelo cliente
|
21
|
+
</div>
|
22
|
+
<div style="color: #202124; font-size: 18px; font-weight: 600;">
|
23
|
+
<%= number_to_currency @label.order.shipment_total %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div style="text-align: center; padding: 16px; border-radius: 6px; background: #fafbfc; border: 1px solid #e8eaed;">
|
28
|
+
<div style="color: #5f6368; font-size: 12px; font-weight: 500; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.3px;">
|
29
|
+
Preço da etiqueta
|
30
|
+
</div>
|
31
|
+
<div style="color: #202124; font-size: 18px; font-weight: 600;">
|
32
|
+
<%= number_to_currency @label.price %>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
|
38
|
+
<div style="margin-bottom: 25px; text-align: center !important; width: 100%;">
|
39
|
+
<% if @balance < @label.price %>
|
40
|
+
<div style="background: #f8f9fa; border: 1px solid #ff6b6b; border-radius: 6px; padding: 18px; margin: 0 auto; max-width: 320px;">
|
41
|
+
<div style="color: #dc3545; font-size: 12px; font-weight: 500; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.3px;">
|
42
|
+
Saldo Melhor Envio
|
43
|
+
</div>
|
44
|
+
<div style="font-weight: 600; color: #dc3545; font-size: 20px; margin-bottom: 12px;">
|
45
|
+
<%= number_to_currency @balance %>
|
46
|
+
</div>
|
47
|
+
<div style="color: #6c757d; font-size: 13px; margin-bottom: 12px;">
|
48
|
+
Saldo insuficiente para pagar a etiqueta
|
49
|
+
</div>
|
50
|
+
<a href="https://app.melhorenvio.com.br/melhor-carteira" target="_blank" style="display: inline-block; background: #007bff; color: white; text-decoration: none; padding: 8px 16px; border-radius: 4px; font-size: 13px; font-weight: 500;">
|
51
|
+
Adicionar Saldo no Melhor Envio
|
52
|
+
</a>
|
53
|
+
</div>
|
54
|
+
<% else %>
|
55
|
+
<div style="background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; padding: 18px; margin: 0 auto; max-width: 280px;">
|
56
|
+
<div style="color: #5f6368; font-size: 12px; font-weight: 500; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.3px;">
|
57
|
+
Saldo Melhor Envio
|
58
|
+
</div>
|
59
|
+
<div style="font-weight: 600; color: #1a7332; font-size: 20px;">
|
60
|
+
<%= number_to_currency @balance %>
|
61
|
+
</div>
|
62
|
+
</div>
|
63
|
+
<% end %>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<hr style="margin: 20px auto; width: 80%; border: 1px solid #dee2e6;">
|
67
|
+
|
68
|
+
<div style="text-align: center !important; width: 100%; display: flex; justify-content: center;">
|
69
|
+
<%= form_with url: admin_orders_update_label_url(@order.number, @label.id), method: :patch, local: true do |form| %>
|
70
|
+
<div class="form-buttons filter-actions actions" data-hook="buttons" style="text-align: center !important;">
|
71
|
+
<% if @balance < @label.price %>
|
72
|
+
<%= form.submit "Saldo Insuficiente", class: "btn btn-secondary", disabled: true, style: "margin: 0 auto; display: block;" %>
|
73
|
+
<% else %>
|
74
|
+
<%= form.submit "Pagar Etiqueta", class: "btn btn-primary", style: "margin: 0 auto; display: block;" %>
|
75
|
+
<% end %>
|
76
|
+
</div>
|
77
|
+
<% end %>
|
78
|
+
</div>
|
79
|
+
</fieldset>
|
80
|
+
</div>
|
81
|
+
|
82
|
+
<%= render partial: 'spree/shared/error_messages', locals: { target: @order } %>
|
83
|
+
</div>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<%= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Etiquetas' } %>
|
2
|
+
|
3
|
+
<% admin_breadcrumb("Etiquetas") %>
|
4
|
+
|
5
|
+
<div data-order-number="<%= @order.number %>" style="text-align: center !important; display: flex; flex-direction: column; align-items: center; width: 100%;">
|
6
|
+
<div style="width: 100%; max-width: 800px;">
|
7
|
+
<%= render partial: 'steps', locals: { current_step: @current_step } %>
|
8
|
+
|
9
|
+
<fieldset class="no-border-bottom" style="text-align: center !important;">
|
10
|
+
<legend style="text-align: center !important; width: 100%; margin: 0 auto;">Passo 4: Gerar Link de Impressão</legend>
|
11
|
+
|
12
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
13
|
+
<p style="text-align: center !important; margin: 0 auto;">A etiqueta foi gerada com sucesso. Agora vamos gerar o link para impressão da etiqueta.</p>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div style="display: flex; justify-content: center; margin-bottom: 20px; width: 100%;">
|
17
|
+
<div style="display: flex; gap: 40px; align-items: center; justify-content: center;">
|
18
|
+
<div style="display: flex; flex-direction: column; align-items: center; text-align: center;">
|
19
|
+
<label style="text-align: center;">Valor pago pelo cliente:</label>
|
20
|
+
<h3 style="text-align: center; margin: 5px 0;"><%= number_to_currency @label.order.shipment_total %></h3>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div style="display: flex; flex-direction: column; align-items: center; text-align: center;">
|
24
|
+
<label style="text-align: center;">Preço da etiqueta:</label>
|
25
|
+
<h3 style="text-align: center; margin: 5px 0;"><%= number_to_currency @label.price %></h3>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div style="margin-bottom: 20px; text-align: center !important; width: 100%;">
|
31
|
+
<label style="text-align: center !important;">Status da Etiqueta:</label>
|
32
|
+
<p style="color: #28a745; font-weight: bold; text-align: center !important; margin: 5px 0;">✓ ETIQUETA GERADA</p>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<hr style="margin: 20px auto; width: 80%; border: 1px solid #dee2e6;">
|
36
|
+
|
37
|
+
<div style="text-align: center !important; width: 100%; display: flex; justify-content: center;">
|
38
|
+
<%= form_with url: admin_orders_update_label_url(@order.number, @label.id), method: :patch, local: true do |form| %>
|
39
|
+
<div class="form-buttons filter-actions actions" data-hook="buttons" style="text-align: center !important;">
|
40
|
+
<%= form.submit "Gerar Link de Impressão", class: "btn btn-primary", style: "margin: 0 auto; display: block;" %>
|
41
|
+
</div>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</fieldset>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<%= render partial: 'spree/shared/error_messages', locals: { target: @order } %>
|
48
|
+
</div>
|