flowcommerce_spree 0.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +91 -0
- data/Rakefile +33 -0
- data/SPREE_FLOW.md +134 -0
- data/app/assets/javascripts/flowcommerce_spree/application.js +13 -0
- data/app/assets/stylesheets/flowcommerce_spree/application.css +15 -0
- data/app/controllers/concerns/current_zone_loader_decorator.rb +49 -0
- data/app/controllers/flowcommerce_spree/webhooks_controller.rb +25 -0
- data/app/helpers/flowcommerce_spree/application_helper.rb +6 -0
- data/app/helpers/spree/admin/orders_helper_decorator.rb +17 -0
- data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +53 -0
- data/app/mailers/spree/spree_order_mailer_decorator.rb +24 -0
- data/app/models/flowcommerce_spree/settings.rb +8 -0
- data/app/models/spree/credit_card_decorator.rb +9 -0
- data/app/models/spree/flow_io_product_decorator.rb +91 -0
- data/app/models/spree/flow_io_variant_decorator.rb +205 -0
- data/app/models/spree/gateway/spree_flow_gateway.rb +116 -0
- data/app/models/spree/line_item_decorator.rb +15 -0
- data/app/models/spree/order_decorator.rb +179 -0
- data/app/models/spree/promotion_decorator.rb +10 -0
- data/app/models/spree/promotion_handler/coupon_decorator.rb +30 -0
- data/app/models/spree/spree_user_decorator.rb +15 -0
- data/app/models/spree/taxon_decorator.rb +37 -0
- data/app/models/spree/zone_decorator.rb +7 -0
- data/app/models/spree/zones/flow_io_product_zone_decorator.rb +55 -0
- data/app/services/flowcommerce_spree/import_experience_items.rb +76 -0
- data/app/services/flowcommerce_spree/import_experiences.rb +37 -0
- data/app/services/flowcommerce_spree/order_sync.rb +231 -0
- data/app/views/layouts/flowcommerce_spree/application.html.erb +14 -0
- data/app/views/spree/admin/payments/index.html.erb +28 -0
- data/app/views/spree/admin/promotions/edit.html.erb +57 -0
- data/app/views/spree/admin/shared/_order_summary.html.erb +44 -0
- data/app/views/spree/admin/shared/_order_summary_flow.html.erb +13 -0
- data/app/views/spree/order_mailer/confirm_email.html.erb +86 -0
- data/app/views/spree/order_mailer/confirm_email.text.erb +38 -0
- data/config/initializers/flowcommerce_spree.rb +7 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20201021160159_add_type_and_meta_to_spree_zone.rb +23 -0
- data/db/migrate/20201021755957_add_meta_to_spree_tables.rb +17 -0
- data/db/migrate/20201022173210_add_zone_type_to_spree_zone_members.rb +24 -0
- data/db/migrate/20201022174252_add_kind_to_zone.rb +22 -0
- data/lib/flow/error.rb +73 -0
- data/lib/flow/pay_pal.rb +25 -0
- data/lib/flow/simple_gateway.rb +115 -0
- data/lib/flowcommerce_spree.rb +31 -0
- data/lib/flowcommerce_spree/api.rb +48 -0
- data/lib/flowcommerce_spree/engine.rb +27 -0
- data/lib/flowcommerce_spree/experience_service.rb +65 -0
- data/lib/flowcommerce_spree/logging_http_client.rb +43 -0
- data/lib/flowcommerce_spree/logging_http_handler.rb +15 -0
- data/lib/flowcommerce_spree/refresher.rb +81 -0
- data/lib/flowcommerce_spree/session.rb +71 -0
- data/lib/flowcommerce_spree/version.rb +5 -0
- data/lib/flowcommerce_spree/webhook_service.rb +98 -0
- data/lib/simple_csv_writer.rb +44 -0
- data/lib/tasks/flowcommerce_spree.rake +289 -0
- metadata +220 -0
@@ -0,0 +1,231 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowcommerceSpree
|
4
|
+
# represents flow.io order syncing service
|
5
|
+
# for easy integration we are currently passing:
|
6
|
+
# - flow experience
|
7
|
+
# - spree order
|
8
|
+
# - current customer, present as @current_spree_user controller instance variable
|
9
|
+
#
|
10
|
+
# example:
|
11
|
+
# flow_order = FlowcommerceSpree::OrderSync.new # init flow-order object
|
12
|
+
# order: Spree::Order.last,
|
13
|
+
# experience: @flow_session.experience
|
14
|
+
# customer: Spree::User.last
|
15
|
+
# flow_order.build_flow_request # builds json body to be posted to flow.io api
|
16
|
+
# flow_order.synchronize! # sends order to flow
|
17
|
+
class OrderSync
|
18
|
+
FLOW_CENTER = 'default'
|
19
|
+
|
20
|
+
attr_reader :digest, :order, :response
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def clear_cache(order)
|
24
|
+
return unless order.flow_data['order']
|
25
|
+
|
26
|
+
order.flow_data.delete('order')
|
27
|
+
order.update_column :meta, order.meta.to_json
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(order:)
|
32
|
+
raise(ArgumentError, 'Experience not defined or not active') unless order.zone&.flow_io_active_experience?
|
33
|
+
|
34
|
+
@client = FlowcommerceSpree.client(session_id: order.flow_data['session_id'])
|
35
|
+
@experience = order.flow_io_experience_key
|
36
|
+
@order = order
|
37
|
+
@customer = order.user
|
38
|
+
@items = []
|
39
|
+
end
|
40
|
+
|
41
|
+
# helper method to send complete order from Spree to flow.io
|
42
|
+
def synchronize!
|
43
|
+
sync_body!
|
44
|
+
check_state!
|
45
|
+
write_response_in_cache
|
46
|
+
@response
|
47
|
+
end
|
48
|
+
|
49
|
+
def error
|
50
|
+
@response['messages'].join(', ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def error_code
|
54
|
+
@response['code']
|
55
|
+
end
|
56
|
+
|
57
|
+
def error?
|
58
|
+
@response&.[]('code') && @response&.[]('messages') ? true : false
|
59
|
+
end
|
60
|
+
|
61
|
+
def delivery
|
62
|
+
deliveries.select { |el| el[:active] }.first
|
63
|
+
end
|
64
|
+
|
65
|
+
# delivery methods are defined in flow console
|
66
|
+
def deliveries
|
67
|
+
# if we have erorr with an order, but still using this method
|
68
|
+
return [] unless @order.flow_order
|
69
|
+
|
70
|
+
@order.flow_data ||= {}
|
71
|
+
|
72
|
+
delivery_list = @order.flow_order['deliveries'][0]['options']
|
73
|
+
delivery_list = delivery_list.map do |opts|
|
74
|
+
name = opts['tier']['name']
|
75
|
+
|
76
|
+
# add original Flow ID
|
77
|
+
# name += ' (%s)' % opts['tier']['strategy'] if opts['tier']['strategy']
|
78
|
+
|
79
|
+
selection_id = opts['id']
|
80
|
+
|
81
|
+
{ id: selection_id,
|
82
|
+
price: { label: opts['price']['label'] },
|
83
|
+
active: @order.flow_order['selections'].include?(selection_id),
|
84
|
+
name: name }
|
85
|
+
end.to_a
|
86
|
+
|
87
|
+
# make first one active unless we have active element
|
88
|
+
delivery_list.first[:active] = true unless delivery_list.select { |el| el[:active] }.first
|
89
|
+
|
90
|
+
delivery_list
|
91
|
+
end
|
92
|
+
|
93
|
+
def total_price
|
94
|
+
@order.flow_total
|
95
|
+
end
|
96
|
+
|
97
|
+
def delivered_duty
|
98
|
+
# paid is default
|
99
|
+
@order.flow_data['delivered_duty'] || ::Io::Flow::V0::Models::DeliveredDuty.paid.value
|
100
|
+
end
|
101
|
+
|
102
|
+
# builds object that can be sent to api.flow.io to sync order data
|
103
|
+
def build_flow_request
|
104
|
+
@order.line_items.each { |line_item| add_item(line_item) }
|
105
|
+
|
106
|
+
@opts = {}
|
107
|
+
@opts[:experience] = @experience
|
108
|
+
@opts[:expand] = ['experience']
|
109
|
+
|
110
|
+
@body = { items: @items, number: @order.number }
|
111
|
+
|
112
|
+
add_customer if @customer
|
113
|
+
|
114
|
+
if (flow_data = @order.flow_data['order'])
|
115
|
+
@body[:selections] = flow_data['selections'].presence
|
116
|
+
@body[:delivered_duty] = flow_data['delivered_duty'].presence
|
117
|
+
@body[:attributes] = flow_data['attributes'].presence
|
118
|
+
|
119
|
+
if @order.adjustment_total != 0
|
120
|
+
# discount on full order is applied
|
121
|
+
@body[:discount] = { amount: @order.adjustment_total, currency: @order.currency }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# calculate digest body and cache it
|
126
|
+
@digest = Digest::SHA1.hexdigest(@opts.to_json + @body.to_json)
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# if customer is defined, add customer info
|
132
|
+
# it is possible to have order in Spree without customer info (new guest session)
|
133
|
+
def add_customer
|
134
|
+
return unless @customer
|
135
|
+
|
136
|
+
address = @customer.ship_address
|
137
|
+
# address = nil
|
138
|
+
if address
|
139
|
+
@body[:customer] = { name: { first: address.firstname,
|
140
|
+
last: address.lastname },
|
141
|
+
email: @customer.email,
|
142
|
+
number: @customer.flow_number,
|
143
|
+
phone: address.phone }
|
144
|
+
|
145
|
+
streets = []
|
146
|
+
streets.push address.address1 unless address.address1.blank?
|
147
|
+
streets.push address.address2 unless address.address2.blank?
|
148
|
+
|
149
|
+
@body[:destination] = { streets: streets,
|
150
|
+
city: address.city,
|
151
|
+
province: address.state_name,
|
152
|
+
postal: address.zipcode,
|
153
|
+
country: (address.country.iso3 || 'USA'),
|
154
|
+
contact: @body[:customer] }
|
155
|
+
|
156
|
+
@body[:destination].delete_if { |_k, v| v.nil? }
|
157
|
+
end
|
158
|
+
|
159
|
+
@body
|
160
|
+
end
|
161
|
+
|
162
|
+
def sync_body!
|
163
|
+
build_flow_request if @body.blank?
|
164
|
+
|
165
|
+
@use_get = false
|
166
|
+
|
167
|
+
# use get if order is completed and closed
|
168
|
+
@use_get = true if @order.state == 'complete'
|
169
|
+
|
170
|
+
# use get if local digest hash check said there is no change
|
171
|
+
@use_get ||= true if @order.flow_data['digest'] == @digest
|
172
|
+
|
173
|
+
# do not use get if there is no local order cache
|
174
|
+
@use_get = false unless @order.flow_data['order']
|
175
|
+
|
176
|
+
if @use_get
|
177
|
+
@response ||= FlowcommerceSpree::Api.run :get, "/:organization/orders/#{@body[:number]}", expand: 'experience'
|
178
|
+
else
|
179
|
+
@response = @client.orders.put_by_number(FlowcommerceSpree::ORGANIZATION, @order.number,
|
180
|
+
Io::Flow::V0::Models::OrderPutForm.new(@body), @opts).to_hash
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def check_state!
|
185
|
+
# authorize if not authorized
|
186
|
+
# if !@order.flow_order_authorized?
|
187
|
+
|
188
|
+
# authorize payment on complete, unless authorized
|
189
|
+
if @order.state == 'complete' && !@order.flow_order_authorized?
|
190
|
+
simple_gateway = Flow::SimpleGateway.new(@order)
|
191
|
+
simple_gateway.cc_authorization
|
192
|
+
end
|
193
|
+
|
194
|
+
@order.flow_finalize! if @order.flow_order_authorized? && @order.state != 'complete'
|
195
|
+
end
|
196
|
+
|
197
|
+
def add_item(line_item)
|
198
|
+
variant = line_item.variant
|
199
|
+
price_root = variant.flow_data&.dig('exp', @experience, 'prices')&.[](0) || {}
|
200
|
+
|
201
|
+
# create flow order line item
|
202
|
+
item = { center: FLOW_CENTER,
|
203
|
+
number: variant.sku,
|
204
|
+
quantity: line_item.quantity,
|
205
|
+
price: { amount: price_root['amount'] || variant.cost_price,
|
206
|
+
currency: price_root['currency'] || variant.cost_currency } }
|
207
|
+
|
208
|
+
@items.push item
|
209
|
+
end
|
210
|
+
|
211
|
+
# set cache for total order ammount
|
212
|
+
# written in flow_data field inside spree_orders table
|
213
|
+
def write_response_in_cache
|
214
|
+
if !@response || error?
|
215
|
+
@order.flow_data.delete('digest')
|
216
|
+
@order.flow_data.delete('order')
|
217
|
+
else
|
218
|
+
response_total = @response.dig('total', 'label')
|
219
|
+
cache_total = @order.flow_data.dig('order', 'total', 'label')
|
220
|
+
|
221
|
+
# return if total is not changed, no products removed or added
|
222
|
+
return if @use_get && response_total == cache_total
|
223
|
+
|
224
|
+
# update local order
|
225
|
+
@order.flow_data.merge!('digest' => @digest, 'order' => @response.to_hash)
|
226
|
+
end
|
227
|
+
|
228
|
+
@order.update_column(:meta, @order.meta.to_json)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>FlowcommerceSpree</title>
|
5
|
+
<%= stylesheet_link_tag "flowcommerce_spree/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "flowcommerce_spree/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Payments' } %>
|
2
|
+
|
3
|
+
<% content_for :page_actions do %>
|
4
|
+
<% if @order.outstanding_balance? %>
|
5
|
+
<li id="new_payment_section">
|
6
|
+
<%= button_link_to Spree.t(:new_payment), new_admin_order_payment_url(@order), icon: 'plus' %>
|
7
|
+
</li>
|
8
|
+
<% end %>
|
9
|
+
<li><%= button_link_to Spree.t(:back_to_orders_list), admin_orders_path, icon: 'arrow-left' %></li>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<% content_for :page_title do %>
|
13
|
+
<i class="fa fa-arrow-right"></i> <%= Spree.t(:payments) %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% if @order.outstanding_balance? %>
|
17
|
+
<h5 class="outstanding-balance"><%= @order.outstanding_balance < 0 ? Spree.t(:credit_owed) : Spree.t(:balance_due) %>: <strong><%= @order.display_outstanding_balance %></strong></h5>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if @payments.any? %>
|
21
|
+
<%= render partial: 'list', :locals => { :payments => @payments } %>
|
22
|
+
<% else %>
|
23
|
+
<div class="alpha twelve columns no-objects-found"><%= Spree.t(:order_has_no_payments) %></div>
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<!-- SHOW REFUNDS START -->
|
27
|
+
<%= render '/flow/show_refunds' %>
|
28
|
+
<!-- SHOW REFUNDS END -->
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<% content_for :page_title do %>
|
2
|
+
<%= Spree.t(:editing_promotion) %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<% content_for :page_actions do %>
|
6
|
+
<li>
|
7
|
+
<%= button_link_to Spree.t(:back_to_promotions_list), admin_promotions_path, icon: 'arrow-left' %>
|
8
|
+
</li>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<%= form_for @promotion, url: object_url, method: :put do |f| %>
|
12
|
+
<fieldset class="no-border-top">
|
13
|
+
<%= render partial: 'form', locals: { f: f } %>
|
14
|
+
<%= render partial: 'spree/admin/shared/edit_resource_links' %>
|
15
|
+
</fieldset>
|
16
|
+
<% end %>
|
17
|
+
|
18
|
+
<div id="promotion-filters" class="row">
|
19
|
+
<div id="rules_container" class="alpha eight columns">
|
20
|
+
<%= render partial: 'rules' %>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div id="actions_container" class="omega eight columns">
|
24
|
+
<%= render partial: 'actions' %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<%= render partial: 'spree/admin/variants/autocomplete', :formats => [:js] %>
|
29
|
+
|
30
|
+
<!-- Flow filters experience -->
|
31
|
+
|
32
|
+
<%
|
33
|
+
@promotion_keys = @promotion.flow_data.dig('filter', 'experience') || []
|
34
|
+
%>
|
35
|
+
|
36
|
+
<script>
|
37
|
+
window.promotion_set_option = function(key_name, value) {
|
38
|
+
var opts = {
|
39
|
+
id: <%= @promotion.id %>,
|
40
|
+
type: 'experience',
|
41
|
+
name: key_name,
|
42
|
+
value: value ? 1 : 0
|
43
|
+
};
|
44
|
+
|
45
|
+
$.post('/flow/promotion_set_option', opts, function(r) { console.log(r); });
|
46
|
+
}
|
47
|
+
</script>
|
48
|
+
|
49
|
+
<fieldset>
|
50
|
+
<legend align="center">Enable for Flow experiences</legend>
|
51
|
+
|
52
|
+
<p>Promotion will be enabled for all experiences unless a selection is made.</p>
|
53
|
+
|
54
|
+
<% for experience in FlowcommerceSpree::ExperienceService.all %>
|
55
|
+
<p><label><input type="checkbox" onclick="promotion_set_option('<%= experience.key %>', this.checked);" <%= @promotion_keys.include?(experience.key) ? 'checked="1"' : '' %> /> <%= experience.key %></label></p>
|
56
|
+
<% end %>
|
57
|
+
</fieldset>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<header id="order_tab_summary" data-hook>
|
2
|
+
<dl class="additional-info">
|
3
|
+
<dt id="order_status" data-hook><%= Spree.t(:status) %>:</dt>
|
4
|
+
<dd><span class="state <%= @order.state %>"><%= Spree.t(@order.state, scope: :order_state) %></span></dd>
|
5
|
+
<dt data-hook='admin_order_tab_subtotal_title'><%= Spree.t(:subtotal) %>:</dt>
|
6
|
+
<dd id='item_total'><%= @order.display_item_total.to_html %></dd>
|
7
|
+
<% if checkout_steps.include?("delivery") && @order.ship_total > 0 %>
|
8
|
+
<dt data-hook='admin_order_tab_ship_total_title'><%= Spree.t(:ship_total) %>:</dt>
|
9
|
+
<dd id='ship_total'><%= @order.display_ship_total.to_html %></dd>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<% if @order.included_tax_total != 0 %>
|
13
|
+
<dt data-hook='admin_order_tab_included_tax_title'><%= Spree.t(:tax_included) %>:</dt>
|
14
|
+
<dd id='included_tax_total'><%= @order.display_included_tax_total.to_html %></dd>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% if @order.additional_tax_total != 0 %>
|
18
|
+
<dt data-hook='admin_order_tab_additional_tax_title'><%= Spree.t(:tax) %>:</dt>
|
19
|
+
<dd id='additional_tax_total'><%= @order.display_additional_tax_total.to_html %></dd>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
<dt data-hook='admin_order_tab_total_title'><%= Spree.t(:total) %>:</dt>
|
23
|
+
<dd id='order_total'><%= @order.display_total.to_html %></dd>
|
24
|
+
|
25
|
+
<% if @order.completed? %>
|
26
|
+
<dt><%= Spree.t(:shipment) %>: </dt>
|
27
|
+
<dd id='shipment_status'><span class="state <%= @order.shipment_state %>"><%= Spree.t(@order.shipment_state, scope: :shipment_states, default: [:missing, "none"]) %></span></dd>
|
28
|
+
<dt><%= Spree.t(:payment) %>: </dt>
|
29
|
+
<dd id='payment_status'><span class="state <%= @order.payment_state %>"><%= Spree.t(@order.payment_state, scope: :payment_states, default: [:missing, "none"]) %></span></dd>
|
30
|
+
<dt data-hook='admin_order_tab_date_completed_title'><%= Spree.t(:date_completed) %>:</dt>
|
31
|
+
<dd id='date_complete'><%= pretty_time(@order.completed_at) %></dd>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% if @order.approved? %>
|
35
|
+
<dt><%= Spree.t(:approver) %></dt>
|
36
|
+
<dd><%= @order.approver.email %></dd>
|
37
|
+
<dt><%= Spree.t(:approved_at) %></dt>
|
38
|
+
<dd><%= pretty_time(@order.approved_at) %></dd>
|
39
|
+
<% end %>
|
40
|
+
</dl>
|
41
|
+
</header>
|
42
|
+
|
43
|
+
<!-- Add Flow order summary, if available -->
|
44
|
+
<%= render partial: 'spree/admin/shared/order_summary_flow' %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<hr />
|
2
|
+
|
3
|
+
<h5 class="sidebar-title">Flow details</h5>
|
4
|
+
|
5
|
+
<%= total_cart_breakdown %>
|
6
|
+
|
7
|
+
<% if @order.flow_data['order'] %>
|
8
|
+
<p>Experience: <b><%= @order.flow_data.dig('order', 'experience', 'key') %></b></p>
|
9
|
+
<a target="_fc_<%= @order.id %>" href="https://console.flow.io/<%= FlowcommerceSpree::ORGANIZATION %>/orders/<%= @order.number %>">Open in Flow console</a>
|
10
|
+
<% else %>
|
11
|
+
<p>Order is not localized with Flow.</p>
|
12
|
+
<% end %>
|
13
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
<%
|
2
|
+
# render text email in console. parts[1] for html body
|
3
|
+
# puts Spree::OrderMailer.confirm_email(Spree::Order.last).body.parts[0].body
|
4
|
+
|
5
|
+
@prices = @order.flow_cart_breakdown
|
6
|
+
@total_price = @prices.pop
|
7
|
+
%>
|
8
|
+
|
9
|
+
<style>
|
10
|
+
table.order td { padding: 4px; border-top: 1px solid #bbb; }
|
11
|
+
</style>
|
12
|
+
|
13
|
+
<h6>Dear <%= @order.bill_address.firstname %></h6>
|
14
|
+
|
15
|
+
<br>
|
16
|
+
<br>
|
17
|
+
|
18
|
+
<p><%= Spree.t('order_mailer.confirm_email.instructions') %></p>
|
19
|
+
<p><%= Spree.t('order_mailer.confirm_email.order_summary') %></p>
|
20
|
+
|
21
|
+
<table class="order">
|
22
|
+
<tr>
|
23
|
+
<th>Product</th>
|
24
|
+
<th width="100" align="right">Price</th>
|
25
|
+
<th width="80" align="center">Quantity</th>
|
26
|
+
<th width="100" align="right">Total</th>
|
27
|
+
</tr>
|
28
|
+
<% @order.line_items.each do |line_item| %>
|
29
|
+
<tr>
|
30
|
+
<td><%= line_item.variant.product.name %></td>
|
31
|
+
<td align="right"><%= @order.flow_line_item_price(line_item) %></td>
|
32
|
+
<td align="center"><%= line_item.quantity %></td>
|
33
|
+
<td align="right"><%= @order.flow_line_item_price(line_item, :with_quantity) %></td>
|
34
|
+
</tr>
|
35
|
+
<% end %>
|
36
|
+
</table>
|
37
|
+
|
38
|
+
<br>
|
39
|
+
|
40
|
+
<p><b>Total</b></p>
|
41
|
+
|
42
|
+
<table class="order">
|
43
|
+
|
44
|
+
<% @prices.each do |price| %>
|
45
|
+
<tr><td width="120"><%= price.name.capitalize %></td><td align="right"><%= price.label %></td></tr>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
<tr>
|
49
|
+
<td><%= Spree.t(:total) %></td>
|
50
|
+
<td align="right"><b><%= @total_price.label %></b></td>
|
51
|
+
</tr>
|
52
|
+
<tr>
|
53
|
+
<td>Payment method</td>
|
54
|
+
<td align="right"><%= @order.flow_payment_method == 'paypal' ? 'PayPal' : 'Credit Card' %></td>
|
55
|
+
</tr>
|
56
|
+
</table>
|
57
|
+
|
58
|
+
<br>
|
59
|
+
|
60
|
+
<% ['ship', 'bill'].each do |name|
|
61
|
+
address = @order.send('%s_address' % name)
|
62
|
+
%>
|
63
|
+
<p><b><%= name.capitalize %>ing address</b></p>
|
64
|
+
|
65
|
+
<table class="order">
|
66
|
+
<tr>
|
67
|
+
<td>Full name</td>
|
68
|
+
<td><%= address.firstname %> <%= address.lastname %></td>
|
69
|
+
</tr>
|
70
|
+
<tr>
|
71
|
+
<td>Address</td>
|
72
|
+
<td><%= address.address1 %></td>
|
73
|
+
</tr>
|
74
|
+
<tr>
|
75
|
+
<td>City</td>
|
76
|
+
<td><%= address.city %></td>
|
77
|
+
</tr>
|
78
|
+
<tr>
|
79
|
+
<td>Country</td>
|
80
|
+
<td><%= address.country.name rescue '-' %>, <%= address.state.name rescue '-' %></td>
|
81
|
+
</tr>
|
82
|
+
</table>
|
83
|
+
<br />
|
84
|
+
<% end %>
|
85
|
+
|
86
|
+
<p><%= Spree.t('order_mailer.confirm_email.thanks') %></p>
|