effective_orders 5.9.3 → 6.0.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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/effective_orders/_order.scss +20 -14
- data/app/controllers/effective/providers/stripe.rb +30 -31
- data/app/datatables/admin/effective_orders_datatable.rb +7 -8
- data/app/datatables/effective_orders_datatable.rb +8 -6
- data/app/helpers/effective_carts_helper.rb +3 -2
- data/app/helpers/effective_moneris_checkout_helper.rb +1 -2
- data/app/helpers/effective_orders_helper.rb +23 -0
- data/app/helpers/effective_paypal_helper.rb +23 -8
- data/app/helpers/effective_stripe_helper.rb +2 -2
- data/app/models/effective/order.rb +141 -44
- data/app/models/effective/order_item.rb +1 -1
- data/app/views/effective/orders/_order_items.html.haml +46 -41
- data/app/views/effective/orders/_purchasables.html.haml +20 -0
- data/app/views/effective/orders/mark_as_paid/_form.html.haml +2 -1
- data/app/views/layouts/effective_orders_mailer_layout.html.haml +16 -7
- data/config/effective_orders.rb +6 -0
- data/db/migrate/01_create_effective_orders.rb.erb +4 -0
- data/lib/effective_orders/version.rb +1 -1
- data/lib/effective_orders.rb +6 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f548373346f66befb20e325b166d275eff0444b9b8d2865f87c37d8116d32b0d
|
4
|
+
data.tar.gz: c2cdbd728306010ba79e3847fd59578e3b51afbf2764021b3581767bad0d6831
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 027327c45635652ad18bf89f0597d3c22156adf9ba04a527014c5aae2daed01a92f50dd6d134cf7afc5a34b6ca02daae7b69e73fd801269bf22f75e07f61cc16
|
7
|
+
data.tar.gz: 54576594dab1cd9a569e7c37aab4cf945b8670628d92868a85a8d068a212a8f175cf31c0955fbb933fb618efde82cd9a0d090cb8a83446c876ea74b0a9b10c0a
|
@@ -1,31 +1,37 @@
|
|
1
1
|
.effective-order {
|
2
2
|
table {
|
3
|
-
|
4
|
-
margin-bottom: 25px;
|
3
|
+
.quantity { width: 0%; }
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
.price {
|
6
|
+
text-align: right;
|
7
|
+
padding-left: 2rem;
|
8
|
+
}
|
10
9
|
|
11
|
-
|
10
|
+
tfoot {
|
11
|
+
th, td {
|
12
12
|
border: none;
|
13
13
|
text-align: right;
|
14
|
-
|
14
|
+
white-space: nowrap;
|
15
15
|
}
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
// This is the first column
|
18
|
+
th { width: 100%; }
|
19
|
+
|
20
|
+
tr.single-line {
|
21
|
+
td { border-top: solid 1px; }
|
22
|
+
}
|
23
|
+
|
24
|
+
tr.double-line {
|
25
|
+
td { border-top: double 4px; }
|
19
26
|
}
|
20
27
|
|
21
|
-
.
|
28
|
+
.amount-owing { font-style: italic; }
|
29
|
+
.total { font-style: italic; }
|
22
30
|
}
|
23
31
|
}
|
24
|
-
|
25
|
-
.price { text-align: right; }
|
26
32
|
}
|
27
33
|
|
28
|
-
// Print - Resend Receipt.
|
34
|
+
// Print - Resend Receipt. Ontop of order.
|
29
35
|
.effective-order-actions {
|
30
36
|
text-align: right;
|
31
37
|
margin-bottom: 0.5rem;
|
@@ -13,7 +13,7 @@ module Effective
|
|
13
13
|
|
14
14
|
payment = validate_stripe_payment(stripe_params[:payment_intent_id])
|
15
15
|
|
16
|
-
if payment.blank?
|
16
|
+
if payment.blank?
|
17
17
|
return order_declined(payment: payment, provider: 'stripe', declined_url: stripe_params[:declined_url])
|
18
18
|
end
|
19
19
|
|
@@ -37,36 +37,35 @@ module Effective
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def validate_stripe_payment(payment_intent_id)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
40
|
+
intent = EffectiveOrders.with_stripe { ::Stripe::PaymentIntent.retrieve(payment_intent_id) }
|
41
|
+
raise('expected stripe intent to be present') if intent.blank?
|
42
|
+
return unless intent.status == 'succeeded'
|
43
|
+
|
44
|
+
# Stripe API version 2022-11-15 and 2022-08-01
|
45
|
+
charge_id = intent.try(:latest_charge) || (intent.charges.data.first.id rescue nil)
|
46
|
+
raise('expected stripe charge_id to be present') if charge_id.blank?
|
47
|
+
|
48
|
+
charge = EffectiveOrders.with_stripe { ::Stripe::Charge.retrieve(charge_id) }
|
49
|
+
raise('expected stripe charge to be present') if charge.blank?
|
50
|
+
return unless charge.status == 'succeeded'
|
51
|
+
|
52
|
+
card = charge.payment_method_details.try(:card) || {}
|
53
|
+
active_card = "**** **** **** #{card['last4']} #{card['brand']} #{card['exp_month']}/#{card['exp_year']}" if card.present?
|
54
|
+
|
55
|
+
{
|
56
|
+
charge_id: charge.id,
|
57
|
+
payment_method_id: charge.payment_method,
|
58
|
+
payment_intent_id: intent.id,
|
59
|
+
|
60
|
+
active_card: active_card,
|
61
|
+
card: card['brand'],
|
62
|
+
|
63
|
+
amount: charge.amount,
|
64
|
+
created: charge.created,
|
65
|
+
currency: charge.currency,
|
66
|
+
customer: charge.customer,
|
67
|
+
status: charge.status
|
68
|
+
}.compact
|
70
69
|
end
|
71
70
|
|
72
71
|
end
|
@@ -28,7 +28,7 @@ class Admin::EffectiveOrdersDatatable < Effective::Datatable
|
|
28
28
|
end
|
29
29
|
|
30
30
|
datatable do
|
31
|
-
order :
|
31
|
+
order :updated_at
|
32
32
|
|
33
33
|
bulk_actions_col
|
34
34
|
|
@@ -66,20 +66,19 @@ class Admin::EffectiveOrdersDatatable < Effective::Datatable
|
|
66
66
|
end
|
67
67
|
|
68
68
|
col :payment_method
|
69
|
-
col :payment_provider
|
69
|
+
col :payment_provider, label: 'Provider', visible: false, search: { collection: EffectiveOrders.admin_payment_providers }
|
70
|
+
col :payment_card, label: 'Card', visible: false
|
70
71
|
|
71
72
|
col :subtotal, as: :price, visible: false
|
73
|
+
|
72
74
|
col :tax, as: :price, visible: false
|
75
|
+
col(:tax_rate, visible: false) { |order| rate_to_percentage(order.tax_rate) }
|
73
76
|
|
74
|
-
col :
|
75
|
-
|
76
|
-
end
|
77
|
+
col :surcharge, as: :price, visible: false
|
78
|
+
col(:surcharge_percent, visible: false) { |order| rate_to_percentage(order.surcharge_percent) }
|
77
79
|
|
78
80
|
col :total, as: :price
|
79
81
|
|
80
|
-
col :payment_provider, label: 'Provider', visible: false, search: { collection: EffectiveOrders.admin_payment_providers }
|
81
|
-
col :payment_card, label: 'Card', visible: false
|
82
|
-
|
83
82
|
if EffectiveOrders.collect_note
|
84
83
|
col :note, visible: false
|
85
84
|
end
|
@@ -42,18 +42,20 @@ class EffectiveOrdersDatatable < Effective::Datatable
|
|
42
42
|
collection.where(id: Effective::OrderItem.where('name ILIKE ?', "%#{term}%").select('order_id'))
|
43
43
|
end
|
44
44
|
|
45
|
+
col :payment_method, visible: false
|
46
|
+
col :payment_provider, label: 'Provider', visible: false, search: { collection: EffectiveOrders.payment_providers }
|
47
|
+
col :payment_card, label: 'Card', visible: false
|
48
|
+
|
45
49
|
col :subtotal, as: :price, visible: false
|
50
|
+
|
46
51
|
col :tax, as: :price, visible: false
|
52
|
+
col(:tax_rate, visible: false) { |order| rate_to_percentage(order.tax_rate) }
|
47
53
|
|
48
|
-
col :
|
49
|
-
|
50
|
-
end
|
54
|
+
col :surcharge, as: :price, visible: false
|
55
|
+
col(:surcharge_percent, visible: false) { |order| rate_to_percentage(order.surcharge_percent) }
|
51
56
|
|
52
57
|
col :total, as: :price
|
53
58
|
|
54
|
-
col :payment_provider, label: 'Provider', visible: false, search: { collection: EffectiveOrders.payment_providers }
|
55
|
-
col :payment_card, label: 'Card', visible: false
|
56
|
-
|
57
59
|
if EffectiveOrders.collect_note
|
58
60
|
col :note
|
59
61
|
end
|
@@ -103,11 +103,12 @@ module EffectiveCartsHelper
|
|
103
103
|
|
104
104
|
def render_cart(cart = nil)
|
105
105
|
cart ||= current_cart
|
106
|
-
render(
|
106
|
+
render('effective/carts/cart', cart: cart)
|
107
107
|
end
|
108
108
|
|
109
109
|
def render_purchasables(*purchasables)
|
110
|
-
|
110
|
+
order = Effective::Order.new(purchasables)
|
111
|
+
render('effective/orders/purchasables', order: order)
|
111
112
|
end
|
112
113
|
|
113
114
|
end
|
@@ -10,9 +10,8 @@ module EffectiveMonerisCheckoutHelper
|
|
10
10
|
api_token: EffectiveOrders.moneris_checkout.fetch(:api_token),
|
11
11
|
store_id: EffectiveOrders.moneris_checkout.fetch(:store_id),
|
12
12
|
checkout_id: EffectiveOrders.moneris_checkout.fetch(:checkout_id),
|
13
|
-
|
14
13
|
action: :preload,
|
15
|
-
txn_total:
|
14
|
+
txn_total: ('%.2f' % (order.total_with_surcharge / 100.0)),
|
16
15
|
|
17
16
|
# Optional
|
18
17
|
order_no: order.transaction_id, # Has to be unique. This is order number, billing name and Time.now
|
@@ -5,6 +5,12 @@ module EffectiveOrdersHelper
|
|
5
5
|
number_to_currency(price / 100.0)
|
6
6
|
end
|
7
7
|
|
8
|
+
def rate_to_percentage(rate)
|
9
|
+
rate = rate || 0.0
|
10
|
+
number_to_percentage(rate, strip_insignificant_zeros: true)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Deprecated.
|
8
14
|
def tax_rate_to_percentage(tax_rate, options = {})
|
9
15
|
options[:strip_insignificant_zeros] = true if options[:strip_insignificant_zeros].nil?
|
10
16
|
number_to_percentage(tax_rate, strip_insignificant_zeros: true)
|
@@ -119,4 +125,21 @@ module EffectiveOrdersHelper
|
|
119
125
|
icon_to('shopping-cart', path, { title: 'Checkout' }.merge(options))
|
120
126
|
end
|
121
127
|
|
128
|
+
def admin_mark_as_paid_payment_providers
|
129
|
+
providers = EffectiveOrders.admin_payment_providers
|
130
|
+
|
131
|
+
percentage = EffectiveOrders.credit_card_surcharge_percent.to_f
|
132
|
+
return providers unless percentage > 0.0
|
133
|
+
|
134
|
+
surcharge_providers = EffectiveOrders.credit_card_payment_providers
|
135
|
+
|
136
|
+
with_surcharge = providers.select { |provider| surcharge_providers.include?(provider) }
|
137
|
+
without_surcharge = providers.reject { |provider| surcharge_providers.include?(provider) }
|
138
|
+
|
139
|
+
{
|
140
|
+
"With #{rate_to_percentage(percentage)} credit card surcharge": with_surcharge.map { |provider| [provider, provider] },
|
141
|
+
'Without credit card surcharge': without_surcharge.map { |provider| [provider, provider] }
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
122
145
|
end
|
@@ -31,16 +31,31 @@ module EffectivePaypalHelper
|
|
31
31
|
cert_id: EffectiveOrders.paypal[:cert_id],
|
32
32
|
currency_code: EffectiveOrders.paypal[:currency],
|
33
33
|
invoice: order.id,
|
34
|
-
amount: (order.
|
35
|
-
tax_cart: (order.tax / 100.0)
|
34
|
+
amount: '%.2f' % (order.amount_owing / 100.0),
|
35
|
+
tax_cart: '%.2f' % ((order.tax + order.surcharge_tax) / 100.0)
|
36
36
|
}
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
values["
|
38
|
+
number = 0
|
39
|
+
|
40
|
+
order.order_items.each do |item|
|
41
|
+
number += 1
|
42
|
+
|
43
|
+
values["item_number_#{number}"] = number
|
44
|
+
values["item_name_#{number}"] = item.name
|
45
|
+
values["quantity_#{number}"] = item.quantity
|
46
|
+
values["amount_#{number}"] = '%.2f' % (item.price / 100.0)
|
47
|
+
values["tax_#{number}"] = '%.2f' % ((item.tax / 100.0) / item.quantity) # Tax for 1 of these items
|
48
|
+
end
|
49
|
+
|
50
|
+
# Credit Card Surcharge
|
51
|
+
if order.surcharge != 0
|
52
|
+
number += 1
|
53
|
+
|
54
|
+
values["item_number_#{number}"] = number
|
55
|
+
values["item_name_#{number}"] = 'Credit Card Surcharge'
|
56
|
+
values["quantity_#{number}"] = 1
|
57
|
+
values["amount_#{number}"] = '%.2f' % (order.surcharge / 100.0)
|
58
|
+
values["tax_#{number}"] = '%.2f' % (order.surcharge_tax / 100.0)
|
44
59
|
end
|
45
60
|
|
46
61
|
signed = OpenSSL::PKCS7::sign(OpenSSL::X509::Certificate.new(APP_CERT_PEM), OpenSSL::PKey::RSA.new(APP_KEY_PEM, ''), values.map { |k, v| "#{k}=#{v}" }.join("\n"), [], OpenSSL::PKCS7::BINARY)
|
@@ -60,12 +60,12 @@ module EffectiveStripeHelper
|
|
60
60
|
customer.create_stripe_customer! # Only creates if customer not already present
|
61
61
|
|
62
62
|
payment = {
|
63
|
-
amount: order.
|
63
|
+
amount: order.total_with_surcharge,
|
64
64
|
currency: EffectiveOrders.stripe[:currency],
|
65
65
|
customer: customer.stripe_customer_id,
|
66
66
|
payment_method: customer.payment_method_id.presence,
|
67
67
|
description: stripe_order_description(order),
|
68
|
-
metadata: { order_id: order.id }
|
68
|
+
metadata: { order_id: order.id }
|
69
69
|
}
|
70
70
|
|
71
71
|
token_required = customer.token_required?
|
@@ -52,11 +52,17 @@ module Effective
|
|
52
52
|
payment_provider :string
|
53
53
|
payment_card :string
|
54
54
|
|
55
|
-
tax_rate
|
55
|
+
tax_rate :decimal, precision: 6, scale: 3
|
56
|
+
surcharge_percent :decimal, precision: 6, scale: 3
|
56
57
|
|
57
|
-
subtotal :integer
|
58
|
-
tax :integer
|
59
|
-
|
58
|
+
subtotal :integer # Sum of items subtotal
|
59
|
+
tax :integer # Tax on subtotal
|
60
|
+
amount_owing :integer # Subtotal + Tax
|
61
|
+
|
62
|
+
surcharge :integer # Credit Card Surcharge
|
63
|
+
surcharge_tax :integer # Tax on surcharge
|
64
|
+
|
65
|
+
total :integer # Subtotal + Tax + Surcharge + Surcharge Tax
|
60
66
|
|
61
67
|
timestamps
|
62
68
|
end
|
@@ -88,7 +94,8 @@ module Effective
|
|
88
94
|
before_validation { assign_email }
|
89
95
|
before_validation { assign_user_address }
|
90
96
|
before_validation { assign_billing_name }
|
91
|
-
before_validation {
|
97
|
+
before_validation { assign_order_values }
|
98
|
+
before_validation { assign_order_charges }
|
92
99
|
end
|
93
100
|
|
94
101
|
# Order validations
|
@@ -119,7 +126,7 @@ module Effective
|
|
119
126
|
end
|
120
127
|
|
121
128
|
# User validations -- An admin skips these when working in the admin/ namespace
|
122
|
-
with_options
|
129
|
+
with_options(unless: -> { pending? || skip_buyer_validations? || purchased? }) do
|
123
130
|
validates :tax_rate, presence: { message: "can't be determined based on billing address" }
|
124
131
|
validates :tax, presence: true
|
125
132
|
|
@@ -129,7 +136,7 @@ module Effective
|
|
129
136
|
end
|
130
137
|
|
131
138
|
# When Purchased
|
132
|
-
with_options
|
139
|
+
with_options(if: -> { purchased? }) do
|
133
140
|
validates :purchased_at, presence: true
|
134
141
|
validates :payment, presence: true
|
135
142
|
|
@@ -137,7 +144,7 @@ module Effective
|
|
137
144
|
validates :payment_card, presence: true
|
138
145
|
end
|
139
146
|
|
140
|
-
with_options
|
147
|
+
with_options(if: -> { deferred? }) do
|
141
148
|
validates :payment_provider, presence: true
|
142
149
|
|
143
150
|
validate do
|
@@ -145,11 +152,18 @@ module Effective
|
|
145
152
|
end
|
146
153
|
end
|
147
154
|
|
148
|
-
|
149
|
-
|
150
|
-
|
155
|
+
# Sanity check
|
156
|
+
before_save(if: -> { was_purchased? }) do
|
157
|
+
raise('cannot unpurchase an order') unless purchased?
|
158
|
+
|
159
|
+
raise('cannot change subtotal of a purchased order') if changes[:subtotal].present?
|
160
|
+
|
161
|
+
raise('cannot change tax of a purchased order') if changes[:tax].present?
|
162
|
+
raise('cannot change tax of a purchased order') if changes[:tax_rate].present?
|
163
|
+
|
164
|
+
raise('cannot change surcharge of a purchased order') if changes[:surcharge].present?
|
165
|
+
raise('cannot change surcharge percent of a purchased order') if changes[:surcharge_percent].present?
|
151
166
|
|
152
|
-
before_save(if: -> { done? }) do
|
153
167
|
raise('cannot change total of a purchased order') if changes[:total].present?
|
154
168
|
end
|
155
169
|
|
@@ -213,9 +227,7 @@ module Effective
|
|
213
227
|
removed.each { |order_item| order_item.mark_for_destruction }
|
214
228
|
|
215
229
|
# Make sure to reset stored aggregates
|
216
|
-
|
217
|
-
self.subtotal = nil
|
218
|
-
self.tax = nil
|
230
|
+
assign_attributes(subtotal: nil, tax_rate: nil, tax: nil, surcharge_percent: nil, surcharge: nil, total: nil)
|
219
231
|
|
220
232
|
removed.length == 1 ? removed.first : removed
|
221
233
|
end
|
@@ -259,9 +271,7 @@ module Effective
|
|
259
271
|
end.compact
|
260
272
|
|
261
273
|
# Make sure to reset stored aggregates
|
262
|
-
|
263
|
-
self.subtotal = nil
|
264
|
-
self.tax = nil
|
274
|
+
assign_attributes(subtotal: nil, tax_rate: nil, tax: nil, surcharge_percent: nil, surcharge: nil, total: nil)
|
265
275
|
|
266
276
|
retval = cart_items.map do |item|
|
267
277
|
order_items.build(
|
@@ -311,24 +321,14 @@ module Effective
|
|
311
321
|
end
|
312
322
|
|
313
323
|
def total_label
|
314
|
-
|
315
|
-
'Total Paid'
|
316
|
-
elsif purchased?
|
317
|
-
'Total Paid'
|
318
|
-
elsif refund? && (pending? || confirmed?)
|
319
|
-
'Total Due'
|
320
|
-
elsif (pending? || confirmed?)
|
321
|
-
'Total Due'
|
322
|
-
else
|
323
|
-
'Total'
|
324
|
-
end
|
324
|
+
purchased? ? 'Total Paid' : 'Total Due'
|
325
325
|
end
|
326
326
|
|
327
327
|
# Visa - 1234
|
328
328
|
def payment_method
|
329
329
|
return nil unless purchased?
|
330
330
|
|
331
|
-
provider = payment_provider if ['cheque', 'etransfer', 'phone'].include?(payment_provider)
|
331
|
+
provider = payment_provider if ['cheque', 'etransfer', 'phone', 'credit card'].include?(payment_provider)
|
332
332
|
|
333
333
|
# Normalize payment card
|
334
334
|
card = case payment_card.to_s.downcase.gsub(' ', '').strip
|
@@ -411,6 +411,18 @@ module Effective
|
|
411
411
|
false
|
412
412
|
end
|
413
413
|
|
414
|
+
def was_purchased?
|
415
|
+
state_was == EffectiveOrders::PURCHASED
|
416
|
+
end
|
417
|
+
|
418
|
+
def purchased_with_credit_card?
|
419
|
+
purchased? && EffectiveOrders.credit_card_payment_providers.include?(payment_provider)
|
420
|
+
end
|
421
|
+
|
422
|
+
def purchased_without_credit_card?
|
423
|
+
purchased? && EffectiveOrders.credit_card_payment_providers.exclude?(payment_provider)
|
424
|
+
end
|
425
|
+
|
414
426
|
def declined?
|
415
427
|
state == EffectiveOrders::DECLINED
|
416
428
|
end
|
@@ -424,7 +436,7 @@ module Effective
|
|
424
436
|
end
|
425
437
|
|
426
438
|
def subtotal
|
427
|
-
self[:subtotal] ||
|
439
|
+
self[:subtotal] || get_subtotal()
|
428
440
|
end
|
429
441
|
|
430
442
|
def tax_rate
|
@@ -435,8 +447,32 @@ module Effective
|
|
435
447
|
self[:tax] || get_tax()
|
436
448
|
end
|
437
449
|
|
450
|
+
def amount_owing
|
451
|
+
self[:amount_owing] || get_amount_owing()
|
452
|
+
end
|
453
|
+
|
454
|
+
def surcharge_percent
|
455
|
+
self[:surcharge_percent] || get_surcharge_percent()
|
456
|
+
end
|
457
|
+
|
458
|
+
def surcharge
|
459
|
+
self[:surcharge] || get_surcharge()
|
460
|
+
end
|
461
|
+
|
462
|
+
def surcharge_tax
|
463
|
+
self[:surcharge_tax] || get_surcharge_tax()
|
464
|
+
end
|
465
|
+
|
438
466
|
def total
|
439
|
-
|
467
|
+
self[:total] || get_total()
|
468
|
+
end
|
469
|
+
|
470
|
+
def total_with_surcharge
|
471
|
+
get_total_with_surcharge()
|
472
|
+
end
|
473
|
+
|
474
|
+
def total_without_surcharge
|
475
|
+
get_total_without_surcharge()
|
440
476
|
end
|
441
477
|
|
442
478
|
def free?
|
@@ -526,12 +562,13 @@ module Effective
|
|
526
562
|
|
527
563
|
# Call this as a way to skip over non consequential orders
|
528
564
|
# And mark some purchasables purchased
|
565
|
+
# This is different than the Mark as Paid payment processor
|
529
566
|
def mark_as_purchased!
|
530
567
|
purchase!(skip_buyer_validations: true, email: false, skip_quickbooks: true)
|
531
568
|
end
|
532
569
|
|
533
570
|
# Effective::Order.new(items: Product.first, user: User.first).purchase!(email: false)
|
534
|
-
def purchase!(payment:
|
571
|
+
def purchase!(payment: nil, provider: nil, card: nil, email: true, skip_buyer_validations: false, skip_quickbooks: false)
|
535
572
|
return true if purchased?
|
536
573
|
|
537
574
|
# Assign attributes
|
@@ -539,12 +576,16 @@ module Effective
|
|
539
576
|
state: EffectiveOrders::PURCHASED,
|
540
577
|
skip_buyer_validations: skip_buyer_validations,
|
541
578
|
|
542
|
-
|
543
|
-
payment_card: (card.presence || 'none'),
|
579
|
+
payment: payment_to_h(payment.presence || 'none'),
|
544
580
|
purchased_at: (purchased_at.presence || Time.zone.now),
|
545
|
-
|
581
|
+
|
582
|
+
payment_provider: (provider.presence || 'none'),
|
583
|
+
payment_card: (card.presence || 'none')
|
546
584
|
)
|
547
585
|
|
586
|
+
# Updates surcharge and total based on payment_provider
|
587
|
+
assign_order_charges()
|
588
|
+
|
548
589
|
begin
|
549
590
|
Effective::Order.transaction do
|
550
591
|
run_purchasable_callbacks(:before_purchase)
|
@@ -600,7 +641,7 @@ module Effective
|
|
600
641
|
def decline!(payment: 'none', provider: 'none', card: 'none', validate: true)
|
601
642
|
return false if declined?
|
602
643
|
|
603
|
-
raise
|
644
|
+
raise('order already purchased') if purchased?
|
604
645
|
|
605
646
|
error = nil
|
606
647
|
|
@@ -664,6 +705,10 @@ module Effective
|
|
664
705
|
|
665
706
|
protected
|
666
707
|
|
708
|
+
def get_subtotal
|
709
|
+
present_order_items.map { |oi| oi.subtotal }.sum
|
710
|
+
end
|
711
|
+
|
667
712
|
def get_tax_rate
|
668
713
|
rate = instance_exec(self, &EffectiveOrders.order_tax_rate_method).to_f
|
669
714
|
|
@@ -675,10 +720,49 @@ module Effective
|
|
675
720
|
end
|
676
721
|
|
677
722
|
def get_tax
|
678
|
-
return
|
723
|
+
return 0 unless tax_rate.present?
|
679
724
|
present_order_items.reject { |oi| oi.tax_exempt? }.map { |oi| (oi.subtotal * (tax_rate / 100.0)).round(0).to_i }.sum
|
680
725
|
end
|
681
726
|
|
727
|
+
def get_amount_owing
|
728
|
+
subtotal + tax
|
729
|
+
end
|
730
|
+
|
731
|
+
def get_surcharge_percent
|
732
|
+
percent = EffectiveOrders.credit_card_surcharge_percent.to_f
|
733
|
+
return nil unless percent > 0.0
|
734
|
+
|
735
|
+
return 0.0 if purchased_without_credit_card?
|
736
|
+
|
737
|
+
if (percent > 10.0 || percent < 0.5)
|
738
|
+
raise "expected EffectiveOrders.credit_card_surcharge to return a value between 10.0 (10%) and 0.5 (0.5%) or nil. Received #{percent}. Please return 2.5 for 2.5% surcharge."
|
739
|
+
end
|
740
|
+
|
741
|
+
percent
|
742
|
+
end
|
743
|
+
|
744
|
+
def get_surcharge
|
745
|
+
return 0 unless surcharge_percent.present?
|
746
|
+
((subtotal + tax) * (surcharge_percent / 100.0)).round(0).to_i
|
747
|
+
end
|
748
|
+
|
749
|
+
def get_surcharge_tax
|
750
|
+
return 0 unless tax_rate.present?
|
751
|
+
(surcharge * (tax_rate / 100.0)).round(0).to_i
|
752
|
+
end
|
753
|
+
|
754
|
+
def get_total
|
755
|
+
subtotal + tax + surcharge + surcharge_tax
|
756
|
+
end
|
757
|
+
|
758
|
+
def get_total_with_surcharge
|
759
|
+
subtotal + tax + surcharge + surcharge_tax
|
760
|
+
end
|
761
|
+
|
762
|
+
def get_total_without_surcharge
|
763
|
+
subtotal + tax
|
764
|
+
end
|
765
|
+
|
682
766
|
private
|
683
767
|
|
684
768
|
def present_order_items
|
@@ -707,18 +791,31 @@ module Effective
|
|
707
791
|
end
|
708
792
|
end
|
709
793
|
|
710
|
-
# This overwrites the prices, taxes, etc on every save.
|
711
|
-
|
794
|
+
# This overwrites the prices, taxes, surcharge, etc on every save.
|
795
|
+
# Does not get run from the before_validate on purchase.
|
796
|
+
def assign_order_values
|
712
797
|
# Copies prices from purchasable into order items
|
713
798
|
present_order_items.each { |oi| oi.assign_purchasable_attributes }
|
714
799
|
|
715
|
-
#
|
716
|
-
subtotal =
|
800
|
+
# Calculated from each item
|
801
|
+
self.subtotal = get_subtotal()
|
717
802
|
|
718
|
-
|
803
|
+
# We only know tax if there is a billing address
|
719
804
|
self.tax_rate = get_tax_rate()
|
720
805
|
self.tax = get_tax()
|
721
|
-
|
806
|
+
|
807
|
+
# Subtotal + Tax
|
808
|
+
self.amount_owing = get_amount_owing()
|
809
|
+
end
|
810
|
+
|
811
|
+
def assign_order_charges
|
812
|
+
# We only apply surcharge for credit card orders. But we have to display and calculate for non purchased orders
|
813
|
+
self.surcharge_percent = get_surcharge_percent()
|
814
|
+
self.surcharge = get_surcharge()
|
815
|
+
self.surcharge_tax = get_surcharge_tax()
|
816
|
+
|
817
|
+
# Subtotal + Tax + Surcharge + Surcharge Tax
|
818
|
+
self.total = get_total()
|
722
819
|
end
|
723
820
|
|
724
821
|
def update_purchasables_purchased_order!
|
@@ -32,7 +32,7 @@ module Effective
|
|
32
32
|
((quantity || 0) > 1 ? "#{quantity}x #{name}" : name) || 'order item'
|
33
33
|
end
|
34
34
|
|
35
|
-
# This method is called in a before_validation in order.
|
35
|
+
# This method is called in a before_validation in order.assign_order_values()
|
36
36
|
def assign_purchasable_attributes
|
37
37
|
assign_attributes(name: purchasable.purchasable_name, price: purchasable.price, tax_exempt: purchasable.tax_exempt) if purchasable
|
38
38
|
end
|
@@ -1,67 +1,72 @@
|
|
1
|
-
- include_quantity_column = order.order_items.any? { |order_item| order_item.quantity > 1 }
|
2
|
-
- include_download_column = order.purchased? && order.order_items.any? { |order_item| order_item.purchased_download_url.present? }
|
3
|
-
|
4
1
|
.effective-order-items
|
5
2
|
%table.table
|
6
3
|
%thead
|
7
4
|
%tr
|
8
|
-
|
9
|
-
%th.quantity Qty
|
10
|
-
|
11
|
-
- if include_download_column
|
12
|
-
%th.download Download
|
13
|
-
|
5
|
+
%th.quantity Qty
|
14
6
|
%th.item= order.order_items.length > 1 ? 'Items' : 'Item'
|
15
7
|
%th.price Price
|
16
8
|
|
17
9
|
%tbody
|
18
10
|
- order.order_items.each do |item|
|
19
11
|
%tr
|
20
|
-
|
21
|
-
|
12
|
+
%td.quantity= item.quantity
|
13
|
+
|
14
|
+
%td.item
|
15
|
+
= item.name.html_safe
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
- if item.purchased_download_url.present?
|
26
|
-
= link_to 'download', item.purchased_download_url
|
27
|
-
- else
|
28
|
-
= '-'
|
17
|
+
- if order.purchased? && item.purchased_download_url.present?
|
18
|
+
= link_to 'Download', item.purchased_download_url
|
29
19
|
|
30
|
-
%td.item= item.name.html_safe
|
31
20
|
%td.price= price_to_currency(item.subtotal)
|
32
21
|
|
22
|
+
%table.table
|
33
23
|
%tfoot
|
34
|
-
|
35
|
-
%
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
- if include_download_column
|
40
|
-
%th.download
|
24
|
+
%tr
|
25
|
+
%th
|
26
|
+
%td.subtotal Subtotal
|
27
|
+
%td.price.subtotal-price= price_to_currency(order.subtotal)
|
41
28
|
|
42
|
-
|
43
|
-
|
29
|
+
- if order.tax_rate.blank?
|
30
|
+
-# Nothing to do. We can't display Tax, Total or Credit Card Surcharge (which is taxed) yet.
|
44
31
|
|
45
|
-
-
|
32
|
+
- elsif order.tax_rate.present? && order.surcharge_percent.to_f > 0.0
|
46
33
|
%tr
|
47
|
-
|
48
|
-
|
34
|
+
%th
|
35
|
+
%td.tax Tax (#{rate_to_percentage(order.tax_rate)})
|
36
|
+
%td.price.tax-price= price_to_currency(order.tax)
|
49
37
|
|
50
|
-
|
51
|
-
|
38
|
+
%tr.single-line
|
39
|
+
%th
|
40
|
+
%td.amount-owing Amount owing before Credit Card Processing Fee
|
41
|
+
%td.price.amount-owing-price= price_to_currency(order.amount_owing)
|
52
42
|
|
53
|
-
|
54
|
-
%
|
43
|
+
%tr
|
44
|
+
%th
|
45
|
+
%td
|
46
|
+
%td
|
47
|
+
|
48
|
+
%tr
|
49
|
+
%th
|
50
|
+
%td.surcharge Credit Card Processing Fee (#{rate_to_percentage(order.surcharge_percent)}) on #{price_to_currency(order.amount_owing)}
|
51
|
+
%td.price.surcharge-price= price_to_currency(order.surcharge)
|
55
52
|
|
56
|
-
- if order.tax_rate.present?
|
57
53
|
%tr
|
58
|
-
|
59
|
-
|
54
|
+
%th
|
55
|
+
%td.surcharge-tax Tax (#{rate_to_percentage(order.tax_rate)}) on Credit Card Processing Fee
|
56
|
+
%td.price.surcharge-tax-price= price_to_currency(order.surcharge_tax)
|
60
57
|
|
61
|
-
|
62
|
-
|
58
|
+
%tr.double-line
|
59
|
+
%th
|
60
|
+
%td.total Total amount charged to credit card
|
61
|
+
%td.price.total-price= price_to_currency(order.total)
|
63
62
|
|
64
|
-
|
65
|
-
|
63
|
+
- elsif order.tax_rate.present? && !(order.surcharge_percent.to_f > 0.0)
|
64
|
+
%tr
|
65
|
+
%th
|
66
|
+
%td.tax Tax (#{rate_to_percentage(order.tax_rate)})
|
67
|
+
%td.price.tax-price= price_to_currency(order.tax)
|
66
68
|
|
69
|
+
%tr.single-line
|
70
|
+
%th
|
71
|
+
%td.total= order.total_label
|
67
72
|
%td.price.total-price= price_to_currency(order.total)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
.effective-order-items
|
2
|
+
%table.table
|
3
|
+
%thead
|
4
|
+
%tr
|
5
|
+
%th.quantity Qty
|
6
|
+
%th.item= order.order_items.length > 1 ? 'Items' : 'Item'
|
7
|
+
%th.price Price
|
8
|
+
|
9
|
+
%tbody
|
10
|
+
- order.order_items.each do |item|
|
11
|
+
%tr
|
12
|
+
%td.quantity= item.quantity
|
13
|
+
|
14
|
+
%td.item
|
15
|
+
= item.name.html_safe
|
16
|
+
|
17
|
+
- if order.purchased? && item.purchased_download_url.present?
|
18
|
+
= link_to 'Download', item.purchased_download_url
|
19
|
+
|
20
|
+
%td.price= price_to_currency(item.subtotal)
|
@@ -7,7 +7,8 @@
|
|
7
7
|
= effective_form_with(model: order, url: effective_orders.mark_as_paid_order_path(order), method: :post) do |f|
|
8
8
|
.row
|
9
9
|
.col-6
|
10
|
-
|
10
|
+
- collection = admin_mark_as_paid_payment_providers()
|
11
|
+
= f.select :payment_provider, collection, required: true, grouped: collection.kind_of?(Hash)
|
11
12
|
|
12
13
|
= f.text_field :payment_card,
|
13
14
|
label: 'Payment card type, cheque, e-transfer or transaction number',
|
@@ -4,15 +4,24 @@
|
|
4
4
|
%meta{:content => 'text/html; charset=UTF-8', 'http-equiv' => 'Content-Type'}
|
5
5
|
|
6
6
|
:css
|
7
|
+
.effective-order-header h1 { margin: 0; }
|
8
|
+
.effective-order-shipping { padding-top: 12px; padding-bottom: 12px; }
|
9
|
+
|
7
10
|
.effective-order table.table { min-width: 650px; vertical-align: top; border: 0; }
|
8
|
-
.effective-order table.table td { border: 0; vertical-align: top; }
|
9
11
|
.effective-order { text-align: left; }
|
10
|
-
.effective-order .price { text-align: right; }
|
11
|
-
|
12
|
-
.effective-order tfoot
|
13
|
-
.effective-order tfoot
|
14
|
-
|
15
|
-
.effective-order
|
12
|
+
.effective-order .price { text-align: right; padding-left: 24px; }
|
13
|
+
|
14
|
+
.effective-order tfoot th { text-align: right; white-space: nowrap; }
|
15
|
+
.effective-order tfoot td { text-align: right; white-space: nowrap; }
|
16
|
+
|
17
|
+
.effective-order tfoot th { width: 100%; }
|
18
|
+
|
19
|
+
.effective-order tfoot tr.single-line td { border-top: solid 1px; }
|
20
|
+
.effective-order tfoot tr.double-line td { border-top: double 4px; }
|
21
|
+
|
22
|
+
.effective-order tfoot .amount-owing { font-style: italic; }
|
23
|
+
.effective-order tfoot .total { font-style: italic; }
|
24
|
+
|
16
25
|
@media print { .effective-page-break { page-break-inside: avoid; page-break-after: always; min-height: 1px; } }
|
17
26
|
|
18
27
|
%body{style: 'background: #fff;'}
|
data/config/effective_orders.rb
CHANGED
@@ -47,6 +47,12 @@ EffectiveOrders.setup do |config|
|
|
47
47
|
# An order must have a tax rate (even if the value is 0) to be purchased
|
48
48
|
config.order_tax_rate_method = Proc.new { |order| Effective::TaxRateCalculator.new(order: order).tax_rate }
|
49
49
|
|
50
|
+
# Credit Card Surcharge
|
51
|
+
# Will be applied to all orders based off the after-tax total.
|
52
|
+
# Use 2.4 for 2.4% or nil for none
|
53
|
+
config.credit_card_surcharge_percent = nil
|
54
|
+
config.credit_card_surcharge_qb_item_name = 'Credit Card Surcharge'
|
55
|
+
|
50
56
|
# Minimum Charge
|
51
57
|
# Prevent orders less than this value from being purchased
|
52
58
|
# Stripe doesn't allow orders less than $0.50
|
@@ -23,9 +23,13 @@ class CreateEffectiveOrders < ActiveRecord::Migration[4.2]
|
|
23
23
|
t.string :payment_card
|
24
24
|
|
25
25
|
t.decimal :tax_rate, :precision => 6, :scale => 3
|
26
|
+
t.decimal :surcharge_percent, :precision => 6, :scale => 3
|
26
27
|
|
27
28
|
t.integer :subtotal
|
28
29
|
t.integer :tax
|
30
|
+
t.integer :amount_owing
|
31
|
+
t.integer :surcharge
|
32
|
+
t.integer :surcharge_tax
|
29
33
|
t.integer :total
|
30
34
|
|
31
35
|
t.timestamps
|
data/lib/effective_orders.rb
CHANGED
@@ -39,6 +39,7 @@ module EffectiveOrders
|
|
39
39
|
:billing_address, :shipping_address,
|
40
40
|
:collect_note, :collect_note_required, :collect_note_message,
|
41
41
|
:terms_and_conditions, :terms_and_conditions_label, :minimum_charge,
|
42
|
+
:credit_card_surcharge_percent, :credit_card_surcharge_qb_item_name,
|
42
43
|
|
43
44
|
# Mailer
|
44
45
|
:mailer, :parent_mailer, :deliver_method, :mailer_layout, :mailer_sender, :mailer_admin, :mailer_subject,
|
@@ -166,7 +167,7 @@ module EffectiveOrders
|
|
166
167
|
#('pretend' if pretend?),
|
167
168
|
#('refund' if refund?),
|
168
169
|
('stripe' if stripe?),
|
169
|
-
('other' if mark_as_paid?),
|
170
|
+
('other (non credit card)' if mark_as_paid?),
|
170
171
|
'none'
|
171
172
|
].compact
|
172
173
|
end
|
@@ -175,6 +176,10 @@ module EffectiveOrders
|
|
175
176
|
[('cheque' if cheque?), ('etransfer' if etransfer?), ('phone' if phone?)].compact
|
176
177
|
end
|
177
178
|
|
179
|
+
def self.credit_card_payment_providers
|
180
|
+
['credit card', 'moneris', 'moneris_checkout', 'paypal', 'stripe']
|
181
|
+
end
|
182
|
+
|
178
183
|
def self.qb_sync?
|
179
184
|
use_effective_qb_sync && defined?(EffectiveQbSync)
|
180
185
|
end
|
@@ -285,7 +290,6 @@ module EffectiveOrders
|
|
285
290
|
end
|
286
291
|
|
287
292
|
class SoldOutException < Exception; end
|
288
|
-
class AlreadyPurchasedException < Exception; end
|
289
293
|
|
290
294
|
def self.gem_path
|
291
295
|
__dir__.chomp('/lib')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_orders
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -269,6 +269,7 @@ files:
|
|
269
269
|
- app/views/effective/orders/_order_payment.html.haml
|
270
270
|
- app/views/effective/orders/_order_shipping.html.haml
|
271
271
|
- app/views/effective/orders/_orders_table.html.haml
|
272
|
+
- app/views/effective/orders/_purchasables.html.haml
|
272
273
|
- app/views/effective/orders/cheque/_form.html.haml
|
273
274
|
- app/views/effective/orders/declined.html.haml
|
274
275
|
- app/views/effective/orders/deferred.html.haml
|