effective_orders 1.8.1 → 2.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/MIT-LICENSE +1 -1
- data/README.md +150 -60
- data/Rakefile +0 -3
- data/active_admin/effective_orders.rb +14 -9
- data/app/assets/javascripts/effective_orders.js +2 -0
- data/app/assets/javascripts/effective_orders/{stripe_charges.js.coffee → providers/stripe_charges.js.coffee} +1 -1
- data/app/assets/javascripts/effective_orders/{stripe_subscriptions.js.coffee → providers/stripe_subscriptions.js.coffee} +1 -1
- data/app/assets/stylesheets/effective_orders/_order.scss +9 -3
- data/app/controllers/admin/orders_controller.rb +87 -3
- data/app/controllers/concerns/acts_as_active_admin_controller.rb +2 -1
- data/app/controllers/effective/carts_controller.rb +4 -2
- data/app/controllers/effective/orders_controller.rb +101 -60
- data/app/controllers/effective/providers/app_checkout.rb +10 -2
- data/app/controllers/effective/providers/ccbill.rb +34 -0
- data/app/controllers/effective/providers/cheque.rb +30 -0
- data/app/controllers/effective/providers/moneris.rb +7 -7
- data/app/controllers/effective/providers/paypal.rb +7 -5
- data/app/controllers/effective/providers/pretend.rb +22 -0
- data/app/controllers/effective/providers/stripe.rb +26 -24
- data/app/controllers/effective/webhooks_controller.rb +1 -1
- data/app/helpers/effective_carts_helper.rb +59 -29
- data/app/helpers/effective_ccbill_helper.rb +25 -0
- data/app/helpers/effective_orders_helper.rb +50 -41
- data/app/helpers/effective_paypal_helper.rb +11 -11
- data/app/mailers/effective/orders_mailer.rb +95 -20
- data/app/models/concerns/acts_as_purchasable.rb +14 -22
- data/app/models/effective/cart.rb +9 -15
- data/app/models/effective/cart_item.rb +15 -13
- data/app/models/effective/customer.rb +9 -9
- data/app/models/effective/datatables/order_items.rb +14 -8
- data/app/models/effective/datatables/orders.rb +56 -69
- data/app/models/effective/order.rb +250 -139
- data/app/models/effective/order_item.rb +23 -16
- data/app/models/effective/product.rb +23 -0
- data/app/models/effective/providers/ccbill_postback.rb +85 -0
- data/app/models/effective/{stripe_charge.rb → providers/stripe_charge.rb} +1 -1
- data/app/models/effective/subscription.rb +16 -12
- data/app/models/effective/tax_rate_calculator.rb +45 -0
- data/app/views/admin/customers/index.html.haml +2 -5
- data/app/views/admin/order_items/index.html.haml +2 -5
- data/app/views/admin/orders/_actions.html.haml +2 -0
- data/app/views/admin/orders/_form.html.haml +28 -0
- data/app/views/admin/orders/_order_item_fields.html.haml +9 -0
- data/app/views/admin/orders/index.html.haml +9 -5
- data/app/views/admin/orders/new.html.haml +3 -0
- data/app/views/effective/carts/_cart.html.haml +3 -12
- data/app/views/effective/carts/_cart_actions.html.haml +4 -0
- data/app/views/effective/carts/show.html.haml +10 -12
- data/app/views/effective/orders/_checkout_step1.html.haml +46 -0
- data/app/views/effective/orders/_checkout_step2.html.haml +33 -0
- data/app/views/effective/orders/_order.html.haml +2 -0
- data/app/views/effective/orders/_order_actions.html.haml +10 -5
- data/app/views/effective/orders/_order_footer.html.haml +1 -0
- data/app/views/effective/orders/_order_items.html.haml +11 -9
- data/app/views/effective/orders/_order_note.html.haml +8 -0
- data/app/views/effective/orders/_order_note_fields.html.haml +5 -0
- data/app/views/effective/orders/_order_shipping.html.haml +4 -4
- data/app/views/effective/orders/_order_user_fields.html.haml +1 -0
- data/app/views/effective/orders/_orders_table.html.haml +27 -0
- data/app/views/effective/orders/ccbill/_form.html.haml +24 -0
- data/app/views/effective/orders/checkout_step1.html.haml +3 -0
- data/app/views/effective/orders/checkout_step2.html.haml +3 -0
- data/app/views/effective/orders/cheque/_form.html.haml +2 -0
- data/app/views/effective/orders/cheque/pay_by_cheque.html.haml +9 -0
- data/app/views/effective/orders/declined.html.haml +4 -2
- data/app/views/effective/orders/my_purchases.html.haml +1 -1
- data/app/views/effective/orders/my_sales.html.haml +1 -1
- data/app/views/effective/orders/purchased.html.haml +3 -2
- data/app/views/effective/orders/show.html.haml +1 -1
- data/app/views/effective/orders/stripe/_form.html.haml +5 -5
- data/app/views/effective/orders_mailer/order_error.html.haml +11 -0
- data/app/views/effective/orders_mailer/payment_request_to_buyer.html.haml +13 -0
- data/app/views/effective/orders_mailer/pending_order_invoice_to_buyer.html.haml +13 -0
- data/app/views/layouts/effective_orders_mailer_layout.html.haml +7 -7
- data/config/routes.rb +39 -24
- data/db/migrate/01_create_effective_orders.rb.erb +20 -1
- data/db/upgrade/03_upgrade_effective_orders_from1x.rb.erb +95 -0
- data/lib/effective_orders.rb +67 -9
- data/lib/effective_orders/app_checkout_service.rb +1 -2
- data/lib/effective_orders/engine.rb +46 -14
- data/lib/effective_orders/version.rb +1 -1
- data/lib/generators/effective_orders/install_generator.rb +1 -0
- data/lib/generators/effective_orders/upgrade_from03x_generator.rb +1 -0
- data/lib/generators/effective_orders/upgrade_from1x_generator.rb +31 -0
- data/lib/generators/templates/effective_orders.rb +131 -66
- data/lib/generators/templates/effective_orders_mailer_preview.rb +28 -13
- data/spec/controllers/admin/orders_controller_spec.rb +242 -0
- data/spec/controllers/ccbill_orders_controller_spec.rb +103 -0
- data/spec/controllers/moneris_orders_controller_spec.rb +23 -23
- data/spec/controllers/orders_controller_spec.rb +167 -79
- data/spec/controllers/stripe_orders_controller_spec.rb +7 -7
- data/spec/dummy/app/models/product.rb +0 -8
- data/spec/dummy/app/models/product_with_float_price.rb +0 -8
- data/spec/dummy/app/models/user.rb +2 -19
- data/spec/dummy/config/application.rb +2 -1
- data/spec/dummy/config/environments/test.rb +1 -0
- data/spec/dummy/config/initializers/effective_orders.rb +109 -64
- data/spec/dummy/db/schema.rb +15 -2
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +0 -258
- data/spec/models/acts_as_purchasable_spec.rb +8 -6
- data/spec/models/factories_spec.rb +7 -1
- data/spec/models/order_item_spec.rb +1 -1
- data/spec/models/order_spec.rb +165 -46
- data/spec/models/stripe_charge_spec.rb +5 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/support/factories.rb +49 -33
- metadata +47 -64
- data/app/views/effective/orders/_checkout_step_1.html.haml +0 -43
- data/app/views/effective/orders/_checkout_step_2.html.haml +0 -25
- data/app/views/effective/orders/_my_purchases.html.haml +0 -17
- data/app/views/effective/orders/checkout.html.haml +0 -3
- data/app/views/effective/orders/new.html.haml +0 -4
- data/spec/dummy/log/development.log +0 -82
@@ -0,0 +1,25 @@
|
|
1
|
+
module EffectiveCcbillHelper
|
2
|
+
def ccbill_form_digest(order)
|
3
|
+
digested_variables = [
|
4
|
+
ccbill_price(order.total),
|
5
|
+
EffectiveOrders.ccbill[:form_period],
|
6
|
+
EffectiveOrders.ccbill[:currency_code],
|
7
|
+
EffectiveOrders.ccbill[:dynamic_pricing_salt]
|
8
|
+
]
|
9
|
+
string = digested_variables.join('')
|
10
|
+
Digest::MD5.hexdigest(string)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ccbill_price(price)
|
14
|
+
number_to_currency(price/100.0, unit: '')
|
15
|
+
end
|
16
|
+
|
17
|
+
def ccbill_customer_name(order, name = :full_name)
|
18
|
+
if order.user.present? && order.user.try(name).present?
|
19
|
+
order.user.public_send(name)
|
20
|
+
elsif order.billing_address.present?
|
21
|
+
order.billing_address.public_send(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -4,10 +4,12 @@ module EffectiveOrdersHelper
|
|
4
4
|
number_to_currency(price / 100.0)
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def tax_rate_to_percentage(tax_rate)
|
8
|
+
number_to_percentage(tax_rate, strip_insignificant_zeros: true)
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
+
def order_summary(order)
|
12
|
+
order_item_list = content_tag(:ul) do
|
11
13
|
order.order_items.map do |item|
|
12
14
|
content_tag(:li) do
|
13
15
|
title = item.title.split('<br>')
|
@@ -17,6 +19,7 @@ module EffectiveOrdersHelper
|
|
17
19
|
end
|
18
20
|
end.join.html_safe
|
19
21
|
end
|
22
|
+
content_tag(:p, "#{price_to_currency(order.total)} total for #{pluralize(order.num_items, 'item')}:") + order_item_list
|
20
23
|
end
|
21
24
|
|
22
25
|
def order_item_summary(order_item)
|
@@ -33,16 +36,16 @@ module EffectiveOrdersHelper
|
|
33
36
|
case processor
|
34
37
|
when :free
|
35
38
|
'Checkout'
|
36
|
-
when :moneris
|
37
|
-
'Checkout with
|
39
|
+
when :moneris, :stripe, :ccbill
|
40
|
+
'Checkout with credit card'
|
38
41
|
when :paypal
|
39
42
|
'Checkout with PayPal'
|
40
|
-
when :pretend
|
43
|
+
when :pretend
|
41
44
|
EffectiveOrders.allow_pretend_purchase_in_production ? 'Purchase Order' : 'Purchase Order (development only)'
|
42
|
-
when :
|
43
|
-
'
|
45
|
+
when :cheque
|
46
|
+
'Pay by Cheque'
|
44
47
|
when :app_checkout
|
45
|
-
EffectiveOrders.app_checkout[:checkout_label]
|
48
|
+
EffectiveOrders.app_checkout[:checkout_label].presence || 'Checkout'
|
46
49
|
else
|
47
50
|
'Checkout'
|
48
51
|
end
|
@@ -77,53 +80,59 @@ module EffectiveOrdersHelper
|
|
77
80
|
end
|
78
81
|
|
79
82
|
def render_order(order)
|
80
|
-
render(:
|
83
|
+
render(partial: 'effective/orders/order', locals: {order: order})
|
81
84
|
end
|
82
85
|
|
83
|
-
def
|
86
|
+
def render_checkout_step1(order, opts = {})
|
84
87
|
raise ArgumentError.new('unable to checkout an order without a user') unless order.user.present?
|
85
88
|
|
86
|
-
locals = {
|
87
|
-
|
88
|
-
|
89
|
-
|
89
|
+
locals = { purchased_redirect_url: nil, declined_redirect_url: nil }.merge(opts)
|
90
|
+
|
91
|
+
render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
|
92
|
+
end
|
93
|
+
alias_method :render_checkout, :render_checkout_step1
|
94
|
+
|
95
|
+
def render_checkout_step2(order, opts = {})
|
96
|
+
raise ArgumentError.new('unable to checkout an order without a user') unless order.user.present?
|
97
|
+
|
98
|
+
locals = { purchased_redirect_url: nil, declined_redirect_url: nil }.merge(opts)
|
90
99
|
|
91
100
|
if order.new_record? || !order.valid?
|
92
|
-
render(:
|
101
|
+
render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
|
93
102
|
else
|
94
|
-
render(:
|
103
|
+
render(partial: 'effective/orders/checkout_step2', locals: locals.merge({order: order}))
|
95
104
|
end
|
96
105
|
end
|
97
106
|
|
98
107
|
def link_to_my_purchases(opts = {})
|
99
|
-
options = {
|
100
|
-
|
108
|
+
options = {
|
109
|
+
label: 'My Purchases',
|
110
|
+
class: 'btn btn-default',
|
111
|
+
rel: :nofollow
|
112
|
+
}.merge(opts)
|
113
|
+
|
114
|
+
label = options.delete(:label)
|
115
|
+
options[:class] = ((options[:class] || '') + ' btn-my-purchases')
|
116
|
+
|
117
|
+
link_to(label, effective_orders.my_purchases_path, options)
|
101
118
|
end
|
102
119
|
alias_method :link_to_order_history, :link_to_my_purchases
|
103
120
|
|
104
|
-
def
|
121
|
+
def render_orders_table(user_or_orders, opts = {})
|
105
122
|
if user_or_orders.kind_of?(User)
|
106
123
|
orders = Effective::Order.purchased_by(user_or_orders)
|
107
124
|
elsif user_or_orders.respond_to?(:to_a)
|
108
|
-
|
109
|
-
orders = user_or_orders.to_a.select { |order| order.purchased? }
|
110
|
-
rescue => e
|
111
|
-
raise ArgumentError.new('expecting an instance of User or an array/collection of Effective::Order objects')
|
112
|
-
end
|
125
|
+
orders = user_or_orders.to_a
|
113
126
|
else
|
114
127
|
raise ArgumentError.new('expecting an instance of User or an array/collection of Effective::Order objects')
|
115
128
|
end
|
116
129
|
|
117
|
-
locals
|
118
|
-
:orders => orders,
|
119
|
-
:order_path => effective_orders.order_path(':id') # The :id string will be replaced with the order id
|
120
|
-
}.merge(opts)
|
121
|
-
|
122
|
-
render(:partial => 'effective/orders/my_purchases', :locals => locals)
|
130
|
+
render(partial: 'effective/orders/orders_table', locals: {orders: orders}.merge(opts))
|
123
131
|
end
|
124
132
|
|
125
|
-
alias_method :render_purchases, :
|
126
|
-
alias_method :render_my_purchases, :
|
133
|
+
alias_method :render_purchases, :render_orders_table
|
134
|
+
alias_method :render_my_purchases, :render_orders_table
|
135
|
+
alias_method :render_order_history, :render_orders_table
|
127
136
|
|
128
137
|
# Used by the _payment_details partial
|
129
138
|
def tableize_order_payment(hash, options = {class: 'table table-bordered'})
|
@@ -135,15 +144,15 @@ module EffectiveOrdersHelper
|
|
135
144
|
hash.map do |k, v|
|
136
145
|
content_tag(:tr) do
|
137
146
|
content_tag((options[:th] ? :th : :td), k) +
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
147
|
+
content_tag(:td) do
|
148
|
+
if v.kind_of?(Hash)
|
149
|
+
tableize_order_payment(v, options.merge(th: (options.key?(:sub_th) ? options[:sub_th] : options[:th])))
|
150
|
+
elsif v.kind_of?(Array)
|
151
|
+
'[' + v.join(', ') + ']'
|
152
|
+
else
|
153
|
+
v
|
154
|
+
end
|
145
155
|
end
|
146
|
-
end
|
147
156
|
end
|
148
157
|
end.join.html_safe
|
149
158
|
end
|
@@ -20,17 +20,17 @@ module EffectivePaypalHelper
|
|
20
20
|
raise ArgumentError.new("unable to read EffectiveOrders PayPal app_key #{EffectiveOrders.paypal[:app_key]}") unless APP_KEY_PEM.present?
|
21
21
|
|
22
22
|
values = {
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
23
|
+
business: EffectiveOrders.paypal[:seller_email],
|
24
|
+
custom: EffectiveOrders.paypal[:secret],
|
25
|
+
cmd: '_cart',
|
26
|
+
upload: 1,
|
27
|
+
return: effective_orders.order_purchased_url(order),
|
28
|
+
notify_url: effective_orders.paypal_postback_url,
|
29
|
+
cert_id: EffectiveOrders.paypal[:cert_id],
|
30
|
+
currency_code: EffectiveOrders.paypal[:currency],
|
31
|
+
invoice: order.id,
|
32
|
+
amount: (order.subtotal / 100.0).round(2),
|
33
|
+
tax_cart: (order.tax / 100.0).round(2)
|
34
34
|
}
|
35
35
|
|
36
36
|
order.order_items.each_with_index do |item, x|
|
@@ -1,52 +1,108 @@
|
|
1
1
|
module Effective
|
2
2
|
class OrdersMailer < ActionMailer::Base
|
3
3
|
helper EffectiveOrdersHelper
|
4
|
-
default :from => EffectiveOrders.mailer[:default_from]
|
5
4
|
|
6
5
|
layout EffectiveOrders.mailer[:layout].presence || 'effective_orders_mailer_layout'
|
7
6
|
|
8
|
-
def order_receipt_to_admin(
|
9
|
-
@order =
|
10
|
-
|
7
|
+
def order_receipt_to_admin(order_param)
|
8
|
+
@order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
|
9
|
+
|
10
|
+
mail(
|
11
|
+
to: EffectiveOrders.mailer[:admin_email],
|
12
|
+
from: EffectiveOrders.mailer[:default_from],
|
13
|
+
subject: subject_for_order_receipt_to_admin(@order)
|
14
|
+
)
|
11
15
|
end
|
12
16
|
|
13
|
-
def order_receipt_to_buyer(
|
14
|
-
@order =
|
15
|
-
|
17
|
+
def order_receipt_to_buyer(order_param) # Buyer
|
18
|
+
@order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
|
19
|
+
|
20
|
+
mail(
|
21
|
+
to: @order.user.email,
|
22
|
+
from: EffectiveOrders.mailer[:default_from],
|
23
|
+
subject: subject_for_order_receipt_to_buyer(@order)
|
24
|
+
)
|
16
25
|
end
|
17
26
|
|
18
|
-
def order_receipt_to_seller(
|
19
|
-
@order =
|
27
|
+
def order_receipt_to_seller(order_param, seller, order_items)
|
28
|
+
@order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
|
20
29
|
@user = seller.user
|
21
30
|
@order_items = order_items
|
22
|
-
@subject =
|
31
|
+
@subject = subject_for_order_receipt_to_seller(@order, @order_items, seller.user)
|
32
|
+
|
33
|
+
mail(
|
34
|
+
to: @user.email,
|
35
|
+
from: EffectiveOrders.mailer[:default_from],
|
36
|
+
subject: @subject
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# This is sent when an admin creates a new order or /admin/orders/new
|
41
|
+
# Or uses the order action Send Payment Request
|
42
|
+
def payment_request_to_buyer(order_param)
|
43
|
+
@order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
|
44
|
+
|
45
|
+
mail(
|
46
|
+
to: @order.user.email,
|
47
|
+
from: EffectiveOrders.mailer[:default_from],
|
48
|
+
subject: subject_for_payment_request_to_buyer(@order)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
# This is sent when someone chooses to Pay by Cheque
|
53
|
+
def pending_order_invoice_to_buyer(order_param)
|
54
|
+
@order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
|
55
|
+
|
56
|
+
mail(
|
57
|
+
to: @order.user.email,
|
58
|
+
from: EffectiveOrders.mailer[:default_from],
|
59
|
+
subject: subject_for_pending_order_invoice_to_buyer(@order)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def order_error(order: nil, error: nil, to: nil, from: nil, subject: nil, template: 'order_error')
|
64
|
+
if order.present?
|
65
|
+
@order = (order.kind_of?(Effective::Order) ? order : Effective::Order.find(order))
|
66
|
+
@subject = (subject || "An error occurred with order: ##{@order.try(:to_param)}")
|
67
|
+
else
|
68
|
+
@subject = (subject || "An order error occurred with an unknown order")
|
69
|
+
end
|
70
|
+
|
71
|
+
@error = error.to_s
|
72
|
+
|
73
|
+
mail(
|
74
|
+
to: (to || EffectiveOrders.mailer[:admin_email]),
|
75
|
+
from: (from || EffectiveOrders.mailer[:default_from]),
|
76
|
+
subject: prefix_subject(@subject),
|
77
|
+
) do |format|
|
78
|
+
format.html { render(template) }
|
79
|
+
end
|
23
80
|
|
24
|
-
mail(:to => @user.email, :subject => @subject)
|
25
81
|
end
|
26
82
|
|
27
83
|
private
|
28
84
|
|
29
|
-
def
|
30
|
-
string_or_callable = EffectiveOrders.mailer[:
|
85
|
+
def subject_for_order_receipt_to_admin(order)
|
86
|
+
string_or_callable = EffectiveOrders.mailer[:subject_for_order_receipt_to_admin]
|
31
87
|
|
32
88
|
if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
|
33
89
|
string_or_callable = self.instance_exec(order, &string_or_callable)
|
34
90
|
end
|
35
91
|
|
36
|
-
prefix_subject(string_or_callable.presence || "Order ##{order.to_param}
|
92
|
+
prefix_subject(string_or_callable.presence || "Order Receipt: ##{order.to_param}")
|
37
93
|
end
|
38
94
|
|
39
|
-
def
|
40
|
-
string_or_callable = EffectiveOrders.mailer[:
|
95
|
+
def subject_for_order_receipt_to_buyer(order)
|
96
|
+
string_or_callable = EffectiveOrders.mailer[:subject_for_order_receipt_to_buyer]
|
41
97
|
|
42
98
|
if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
|
43
99
|
string_or_callable = self.instance_exec(order, &string_or_callable)
|
44
100
|
end
|
45
101
|
|
46
|
-
prefix_subject(string_or_callable.presence || "Order ##{order.to_param}
|
102
|
+
prefix_subject(string_or_callable.presence || "Order Receipt: ##{order.to_param}")
|
47
103
|
end
|
48
104
|
|
49
|
-
def
|
105
|
+
def subject_for_order_receipt_to_seller(order, order_items, seller)
|
50
106
|
string_or_callable = EffectiveOrders.mailer[:subject_for_seller_receipt]
|
51
107
|
|
52
108
|
if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
|
@@ -56,11 +112,30 @@ module Effective
|
|
56
112
|
prefix_subject(string_or_callable.presence || "#{order_items.length} of your products #{order_items.length > 1 ? 'have' : 'has'} been purchased")
|
57
113
|
end
|
58
114
|
|
115
|
+
def subject_for_payment_request_to_buyer(order)
|
116
|
+
string_or_callable = EffectiveOrders.mailer[:subject_for_payment_request_to_buyer]
|
117
|
+
|
118
|
+
if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
|
119
|
+
string_or_callable = self.instance_exec(order, &string_or_callable)
|
120
|
+
end
|
121
|
+
|
122
|
+
prefix_subject(string_or_callable.presence || "Request for Payment: Invoice ##{order.to_param}")
|
123
|
+
end
|
124
|
+
|
125
|
+
def subject_for_pending_order_invoice_to_buyer(order)
|
126
|
+
string_or_callable = EffectiveOrders.mailer[:subject_for_pending_order_invoice_to_buyer]
|
127
|
+
|
128
|
+
if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
|
129
|
+
string_or_callable = self.instance_exec(order, &string_or_callable)
|
130
|
+
end
|
131
|
+
|
132
|
+
prefix_subject(string_or_callable.presence || "Pending Order: ##{order.to_param}")
|
133
|
+
end
|
134
|
+
|
135
|
+
|
59
136
|
def prefix_subject(text)
|
60
137
|
prefix = (EffectiveOrders.mailer[:subject_prefix].to_s rescue '')
|
61
138
|
prefix.present? ? (prefix.chomp(' ') + ' ' + text) : text
|
62
139
|
end
|
63
|
-
|
64
140
|
end
|
65
141
|
end
|
66
|
-
|
@@ -1,33 +1,36 @@
|
|
1
1
|
module ActsAsPurchasable
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
+
mattr_accessor :descendants
|
5
|
+
|
4
6
|
module ActiveRecord
|
5
7
|
def acts_as_purchasable(*options)
|
6
8
|
@acts_as_purchasable = options || []
|
7
9
|
include ::ActsAsPurchasable
|
10
|
+
(ActsAsPurchasable.descendants ||= []) << self
|
8
11
|
end
|
9
12
|
end
|
10
13
|
|
11
14
|
included do
|
12
|
-
has_many :orders, :
|
13
|
-
has_many :order_items, :
|
14
|
-
has_many :cart_items, :
|
15
|
+
has_many :orders, through: :order_items, class_name: 'Effective::Order'
|
16
|
+
has_many :order_items, as: :purchasable, class_name: 'Effective::OrderItem'
|
17
|
+
has_many :cart_items, as: :purchasable, dependent: :delete_all, class_name: 'Effective::CartItem'
|
15
18
|
|
16
|
-
validates_with Effective::SoldOutValidator, :
|
19
|
+
validates_with Effective::SoldOutValidator, on: :create
|
17
20
|
|
18
21
|
# Database max integer value is 2147483647. So let's round that down and use a max/min of $20 million (2000000000)
|
19
|
-
validates :price, :
|
22
|
+
validates :price, presence: true, numericality: { less_than_or_equal_to: 2000000000, message: 'maximum price is $20,000,000' }
|
20
23
|
|
21
|
-
validates :tax_exempt, :
|
24
|
+
validates :tax_exempt, inclusion: {in: [true, false]}
|
22
25
|
|
23
26
|
# These are breaking on the check for quanitty_enabled?. More research is due
|
24
|
-
validates :quantity_purchased, :
|
25
|
-
validates :quantity_max, :
|
27
|
+
validates :quantity_purchased, numericality: {allow_nil: true}, if: proc { |purchasable| (purchasable.quantity_enabled? rescue false) }
|
28
|
+
validates :quantity_max, numericality: {allow_nil: true}, if: proc { |purchasable| (purchasable.quantity_enabled? rescue false) }
|
26
29
|
|
27
|
-
scope :purchased, -> { joins(:order_items).joins(:orders).where(:
|
28
|
-
scope :purchased_by, lambda { |user| joins(:order_items).joins(:orders).where(:
|
30
|
+
scope :purchased, -> { joins(:order_items).joins(:orders).where(orders: {purchase_state: EffectiveOrders::PURCHASED}).uniq }
|
31
|
+
scope :purchased_by, lambda { |user| joins(:order_items).joins(:orders).where(orders: {user_id: user.try(:id), purchase_state: EffectiveOrders::PURCHASED}).uniq }
|
29
32
|
scope :sold, -> { purchased() }
|
30
|
-
scope :sold_by, lambda { |user| joins(:order_items).joins(:orders).where(:
|
33
|
+
scope :sold_by, lambda { |user| joins(:order_items).joins(:orders).where(order_items: {seller_id: user.try(:id)}).where(orders: {purchase_state: EffectiveOrders::PURCHASED}).uniq }
|
31
34
|
|
32
35
|
scope :not_purchased, -> { where('id NOT IN (?)', purchased.pluck(:id).presence || [0]) }
|
33
36
|
scope :not_purchased_by, lambda { |user| where('id NOT IN (?)', purchased_by(user).pluck(:id).presence || [0]) }
|
@@ -44,9 +47,6 @@ module ActsAsPurchasable
|
|
44
47
|
end
|
45
48
|
|
46
49
|
# Regular instance methods
|
47
|
-
def is_effectively_purchasable?
|
48
|
-
true
|
49
|
-
end
|
50
50
|
|
51
51
|
def price
|
52
52
|
self[:price] || 0
|
@@ -75,14 +75,6 @@ module ActsAsPurchasable
|
|
75
75
|
self[:tax_exempt] || false
|
76
76
|
end
|
77
77
|
|
78
|
-
def tax_rate
|
79
|
-
@tax_rate ||= (
|
80
|
-
self.instance_exec(self, &EffectiveOrders.tax_rate_method).to_f.tap do |rate|
|
81
|
-
raise ArgumentError.new("expected EffectiveOrders.tax_rate_method to return a value between 0 and 1. Received #{rate}. Please return 0.05 for 5% tax.") if (rate > 1.0 || rate < 0.0)
|
82
|
-
end
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
78
|
def seller
|
87
79
|
if EffectiveOrders.stripe_connect_enabled
|
88
80
|
raise 'acts_as_purchasable object requires the seller be defined to return the User selling this item. This is only a requirement when using StripeConnect.'
|
@@ -5,16 +5,16 @@ module Effective
|
|
5
5
|
belongs_to :user # This is optional. We want to let non-logged-in people have carts too
|
6
6
|
has_many :cart_items, :inverse_of => :cart, :dependent => :delete_all
|
7
7
|
|
8
|
-
structure do
|
9
|
-
|
10
|
-
end
|
8
|
+
# structure do
|
9
|
+
# timestamps
|
10
|
+
# end
|
11
11
|
|
12
12
|
default_scope -> { includes(:cart_items => :purchasable) }
|
13
13
|
|
14
|
-
def add(item, quantity
|
15
|
-
raise 'expecting an acts_as_purchasable object' unless item.
|
14
|
+
def add(item, quantity: 1)
|
15
|
+
raise 'expecting an acts_as_purchasable object' unless item.kind_of?(ActsAsPurchasable)
|
16
16
|
|
17
|
-
existing_item = cart_items.where(:
|
17
|
+
existing_item = cart_items.where(purchasable_id: item.id, purchasable_type: item.class.name).first
|
18
18
|
|
19
19
|
if item.quantity_enabled? && (quantity + (existing_item.quantity rescue 0)) > item.quantity_remaining
|
20
20
|
raise EffectiveOrders::SoldOutException, "#{item.title} is sold out"
|
@@ -22,9 +22,9 @@ module Effective
|
|
22
22
|
end
|
23
23
|
|
24
24
|
if existing_item.present?
|
25
|
-
existing_item.update_attributes(:
|
25
|
+
existing_item.update_attributes(quantity: existing_item.quantity + quantity)
|
26
26
|
else
|
27
|
-
cart_items.create(:
|
27
|
+
cart_items.create(cart: self, purchasable_id: item.id, purchasable_type: item.class.name, quantity: quantity)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
alias_method :add_to_cart, :add
|
@@ -53,13 +53,7 @@ module Effective
|
|
53
53
|
def subtotal
|
54
54
|
cart_items.map { |ci| ci.subtotal }.sum
|
55
55
|
end
|
56
|
+
alias_method :total, :subtotal
|
56
57
|
|
57
|
-
def tax
|
58
|
-
cart_items.map { |ci| ci.tax }.sum
|
59
|
-
end
|
60
|
-
|
61
|
-
def total
|
62
|
-
cart_items.map { |ci| ci.total }.sum
|
63
|
-
end
|
64
58
|
end
|
65
59
|
end
|