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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +150 -60
  4. data/Rakefile +0 -3
  5. data/active_admin/effective_orders.rb +14 -9
  6. data/app/assets/javascripts/effective_orders.js +2 -0
  7. data/app/assets/javascripts/effective_orders/{stripe_charges.js.coffee → providers/stripe_charges.js.coffee} +1 -1
  8. data/app/assets/javascripts/effective_orders/{stripe_subscriptions.js.coffee → providers/stripe_subscriptions.js.coffee} +1 -1
  9. data/app/assets/stylesheets/effective_orders/_order.scss +9 -3
  10. data/app/controllers/admin/orders_controller.rb +87 -3
  11. data/app/controllers/concerns/acts_as_active_admin_controller.rb +2 -1
  12. data/app/controllers/effective/carts_controller.rb +4 -2
  13. data/app/controllers/effective/orders_controller.rb +101 -60
  14. data/app/controllers/effective/providers/app_checkout.rb +10 -2
  15. data/app/controllers/effective/providers/ccbill.rb +34 -0
  16. data/app/controllers/effective/providers/cheque.rb +30 -0
  17. data/app/controllers/effective/providers/moneris.rb +7 -7
  18. data/app/controllers/effective/providers/paypal.rb +7 -5
  19. data/app/controllers/effective/providers/pretend.rb +22 -0
  20. data/app/controllers/effective/providers/stripe.rb +26 -24
  21. data/app/controllers/effective/webhooks_controller.rb +1 -1
  22. data/app/helpers/effective_carts_helper.rb +59 -29
  23. data/app/helpers/effective_ccbill_helper.rb +25 -0
  24. data/app/helpers/effective_orders_helper.rb +50 -41
  25. data/app/helpers/effective_paypal_helper.rb +11 -11
  26. data/app/mailers/effective/orders_mailer.rb +95 -20
  27. data/app/models/concerns/acts_as_purchasable.rb +14 -22
  28. data/app/models/effective/cart.rb +9 -15
  29. data/app/models/effective/cart_item.rb +15 -13
  30. data/app/models/effective/customer.rb +9 -9
  31. data/app/models/effective/datatables/order_items.rb +14 -8
  32. data/app/models/effective/datatables/orders.rb +56 -69
  33. data/app/models/effective/order.rb +250 -139
  34. data/app/models/effective/order_item.rb +23 -16
  35. data/app/models/effective/product.rb +23 -0
  36. data/app/models/effective/providers/ccbill_postback.rb +85 -0
  37. data/app/models/effective/{stripe_charge.rb → providers/stripe_charge.rb} +1 -1
  38. data/app/models/effective/subscription.rb +16 -12
  39. data/app/models/effective/tax_rate_calculator.rb +45 -0
  40. data/app/views/admin/customers/index.html.haml +2 -5
  41. data/app/views/admin/order_items/index.html.haml +2 -5
  42. data/app/views/admin/orders/_actions.html.haml +2 -0
  43. data/app/views/admin/orders/_form.html.haml +28 -0
  44. data/app/views/admin/orders/_order_item_fields.html.haml +9 -0
  45. data/app/views/admin/orders/index.html.haml +9 -5
  46. data/app/views/admin/orders/new.html.haml +3 -0
  47. data/app/views/effective/carts/_cart.html.haml +3 -12
  48. data/app/views/effective/carts/_cart_actions.html.haml +4 -0
  49. data/app/views/effective/carts/show.html.haml +10 -12
  50. data/app/views/effective/orders/_checkout_step1.html.haml +46 -0
  51. data/app/views/effective/orders/_checkout_step2.html.haml +33 -0
  52. data/app/views/effective/orders/_order.html.haml +2 -0
  53. data/app/views/effective/orders/_order_actions.html.haml +10 -5
  54. data/app/views/effective/orders/_order_footer.html.haml +1 -0
  55. data/app/views/effective/orders/_order_items.html.haml +11 -9
  56. data/app/views/effective/orders/_order_note.html.haml +8 -0
  57. data/app/views/effective/orders/_order_note_fields.html.haml +5 -0
  58. data/app/views/effective/orders/_order_shipping.html.haml +4 -4
  59. data/app/views/effective/orders/_order_user_fields.html.haml +1 -0
  60. data/app/views/effective/orders/_orders_table.html.haml +27 -0
  61. data/app/views/effective/orders/ccbill/_form.html.haml +24 -0
  62. data/app/views/effective/orders/checkout_step1.html.haml +3 -0
  63. data/app/views/effective/orders/checkout_step2.html.haml +3 -0
  64. data/app/views/effective/orders/cheque/_form.html.haml +2 -0
  65. data/app/views/effective/orders/cheque/pay_by_cheque.html.haml +9 -0
  66. data/app/views/effective/orders/declined.html.haml +4 -2
  67. data/app/views/effective/orders/my_purchases.html.haml +1 -1
  68. data/app/views/effective/orders/my_sales.html.haml +1 -1
  69. data/app/views/effective/orders/purchased.html.haml +3 -2
  70. data/app/views/effective/orders/show.html.haml +1 -1
  71. data/app/views/effective/orders/stripe/_form.html.haml +5 -5
  72. data/app/views/effective/orders_mailer/order_error.html.haml +11 -0
  73. data/app/views/effective/orders_mailer/payment_request_to_buyer.html.haml +13 -0
  74. data/app/views/effective/orders_mailer/pending_order_invoice_to_buyer.html.haml +13 -0
  75. data/app/views/layouts/effective_orders_mailer_layout.html.haml +7 -7
  76. data/config/routes.rb +39 -24
  77. data/db/migrate/01_create_effective_orders.rb.erb +20 -1
  78. data/db/upgrade/03_upgrade_effective_orders_from1x.rb.erb +95 -0
  79. data/lib/effective_orders.rb +67 -9
  80. data/lib/effective_orders/app_checkout_service.rb +1 -2
  81. data/lib/effective_orders/engine.rb +46 -14
  82. data/lib/effective_orders/version.rb +1 -1
  83. data/lib/generators/effective_orders/install_generator.rb +1 -0
  84. data/lib/generators/effective_orders/upgrade_from03x_generator.rb +1 -0
  85. data/lib/generators/effective_orders/upgrade_from1x_generator.rb +31 -0
  86. data/lib/generators/templates/effective_orders.rb +131 -66
  87. data/lib/generators/templates/effective_orders_mailer_preview.rb +28 -13
  88. data/spec/controllers/admin/orders_controller_spec.rb +242 -0
  89. data/spec/controllers/ccbill_orders_controller_spec.rb +103 -0
  90. data/spec/controllers/moneris_orders_controller_spec.rb +23 -23
  91. data/spec/controllers/orders_controller_spec.rb +167 -79
  92. data/spec/controllers/stripe_orders_controller_spec.rb +7 -7
  93. data/spec/dummy/app/models/product.rb +0 -8
  94. data/spec/dummy/app/models/product_with_float_price.rb +0 -8
  95. data/spec/dummy/app/models/user.rb +2 -19
  96. data/spec/dummy/config/application.rb +2 -1
  97. data/spec/dummy/config/environments/test.rb +1 -0
  98. data/spec/dummy/config/initializers/effective_orders.rb +109 -64
  99. data/spec/dummy/db/schema.rb +15 -2
  100. data/spec/dummy/db/test.sqlite3 +0 -0
  101. data/spec/dummy/log/test.log +0 -258
  102. data/spec/models/acts_as_purchasable_spec.rb +8 -6
  103. data/spec/models/factories_spec.rb +7 -1
  104. data/spec/models/order_item_spec.rb +1 -1
  105. data/spec/models/order_spec.rb +165 -46
  106. data/spec/models/stripe_charge_spec.rb +5 -5
  107. data/spec/spec_helper.rb +2 -0
  108. data/spec/support/factories.rb +49 -33
  109. metadata +47 -64
  110. data/app/views/effective/orders/_checkout_step_1.html.haml +0 -43
  111. data/app/views/effective/orders/_checkout_step_2.html.haml +0 -25
  112. data/app/views/effective/orders/_my_purchases.html.haml +0 -17
  113. data/app/views/effective/orders/checkout.html.haml +0 -3
  114. data/app/views/effective/orders/new.html.haml +0 -4
  115. 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 order_summary(order)
8
- content_tag(:p, "#{price_to_currency(order.total)} total for #{pluralize(order.num_items, 'item')}:") +
7
+ def tax_rate_to_percentage(tax_rate)
8
+ number_to_percentage(tax_rate, strip_insignificant_zeros: true)
9
+ end
9
10
 
10
- content_tag(:ul) do
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 Moneris'
39
+ when :moneris, :stripe, :ccbill
40
+ 'Checkout with credit card'
38
41
  when :paypal
39
42
  'Checkout with PayPal'
40
- when :pretend # The logic for this is in orders/views/_checkout_step_2.html.haml
43
+ when :pretend
41
44
  EffectiveOrders.allow_pretend_purchase_in_production ? 'Purchase Order' : 'Purchase Order (development only)'
42
- when :stripe
43
- 'Checkout with Stripe'
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(:partial => 'effective/orders/order', :locals => {:order => order})
83
+ render(partial: 'effective/orders/order', locals: {order: order})
81
84
  end
82
85
 
83
- def render_checkout(order, opts = {})
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
- :purchased_redirect_url => nil,
88
- :declined_redirect_url => nil
89
- }.merge(opts)
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(:partial => 'effective/orders/checkout_step_1', :locals => locals.merge({:order => order}))
101
+ render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
93
102
  else
94
- render(:partial => 'effective/orders/checkout_step_2', :locals => locals.merge({:order => order}))
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 = {:rel => :nofollow}.merge(opts)
100
- link_to (options.delete(:label) || 'My Purchases'), effective_orders.my_purchases_path, options
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 render_order_history(user_or_orders, opts = {})
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
- begin
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, :render_order_history
126
- alias_method :render_my_purchases, :render_order_history
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
- content_tag(:td) do
139
- if v.kind_of?(Hash)
140
- tableize_order_payment(v, options.merge(th: (options.key?(:sub_th) ? options[:sub_th] : options[:th])))
141
- elsif v.kind_of?(Array)
142
- '[' + v.join(', ') + ']'
143
- else
144
- v
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
- :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)
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(order)
9
- @order = order
10
- mail(:to => EffectiveOrders.mailer[:admin_email], :subject => receipt_to_admin_subject(order))
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(order) # Buyer
14
- @order = order
15
- mail(:to => order.user.email, :subject => receipt_to_buyer_subject(order))
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(order, seller, order_items)
19
- @order = 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 = receipt_to_seller_subject(order, order_items, seller.user)
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 receipt_to_admin_subject(order)
30
- string_or_callable = EffectiveOrders.mailer[:subject_for_admin_receipt]
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} Receipt")
92
+ prefix_subject(string_or_callable.presence || "Order Receipt: ##{order.to_param}")
37
93
  end
38
94
 
39
- def receipt_to_buyer_subject(order)
40
- string_or_callable = EffectiveOrders.mailer[:subject_for_buyer_receipt]
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} Receipt")
102
+ prefix_subject(string_or_callable.presence || "Order Receipt: ##{order.to_param}")
47
103
  end
48
104
 
49
- def receipt_to_seller_subject(order, order_items, seller)
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, :through => :order_items, :class_name => 'Effective::Order'
13
- has_many :order_items, :as => :purchasable, :class_name => 'Effective::OrderItem'
14
- has_many :cart_items, :as => :purchasable, :dependent => :delete_all, :class_name => 'Effective::CartItem'
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, :on => :create
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, :presence => true, :numericality => { less_than_or_equal_to: 2000000000, message: 'maximum price is $20,000,000' }
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, :inclusion => {:in => [true, false]}
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, :numericality => {:allow_nil => true}, :if => proc { |purchasable| (purchasable.quantity_enabled? rescue false) }
25
- validates :quantity_max, :numericality => {:allow_nil => true}, :if => proc { |purchasable| (purchasable.quantity_enabled? rescue false) }
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(:orders => {:purchase_state => EffectiveOrders::PURCHASED}).uniq }
28
- scope :purchased_by, lambda { |user| joins(:order_items).joins(:orders).where(:orders => {:user_id => user.try(:id), :purchase_state => EffectiveOrders::PURCHASED}).uniq }
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(:order_items => {:seller_id => user.try(:id)}).where(:orders => {:purchase_state => EffectiveOrders::PURCHASED}).uniq }
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
- timestamps
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 = 1)
15
- raise 'expecting an acts_as_purchasable object' unless item.respond_to?(:is_effectively_purchasable?)
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(:purchasable_id => item.id, :purchasable_type => item.class.name).first
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(:quantity => existing_item.quantity + quantity)
25
+ existing_item.update_attributes(quantity: existing_item.quantity + quantity)
26
26
  else
27
- cart_items.create(:cart => self, :purchasable_id => item.id, :purchasable_type => item.class.name, :quantity => quantity)
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