effective_orders 1.8.1 → 2.0.0

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