flowcommerce_spree 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|