effective_orders 2.2.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +124 -84
  4. data/app/assets/javascripts/effective_orders/customers.js.coffee +39 -0
  5. data/app/assets/javascripts/effective_orders/providers/{stripe_charges.js.coffee → stripe.js.coffee} +15 -13
  6. data/app/assets/javascripts/effective_orders/subscriptions.js.coffee +73 -0
  7. data/app/assets/stylesheets/effective_orders.scss +2 -1
  8. data/app/assets/stylesheets/effective_orders/_order.scss +16 -8
  9. data/app/assets/stylesheets/effective_orders/_subscriptions.scss +14 -0
  10. data/app/controllers/admin/customers_controller.rb +11 -8
  11. data/app/controllers/admin/order_items_controller.rb +4 -8
  12. data/app/controllers/admin/orders_controller.rb +133 -87
  13. data/app/controllers/effective/carts_controller.rb +18 -8
  14. data/app/controllers/effective/concerns/purchase.rb +39 -0
  15. data/app/controllers/effective/customers_controller.rb +43 -0
  16. data/app/controllers/effective/orders_controller.rb +73 -119
  17. data/app/controllers/effective/providers/app_checkout.rb +3 -1
  18. data/app/controllers/effective/providers/ccbill.rb +4 -6
  19. data/app/controllers/effective/providers/cheque.rb +20 -11
  20. data/app/controllers/effective/providers/free.rb +33 -0
  21. data/app/controllers/effective/providers/mark_as_paid.rb +33 -0
  22. data/app/controllers/effective/providers/moneris.rb +9 -17
  23. data/app/controllers/effective/providers/paypal.rb +4 -6
  24. data/app/controllers/effective/providers/pretend.rb +4 -4
  25. data/app/controllers/effective/providers/refund.rb +39 -0
  26. data/app/controllers/effective/providers/stripe.rb +19 -40
  27. data/app/controllers/effective/providers/stripe_connect.rb +2 -6
  28. data/app/controllers/effective/webhooks_controller.rb +44 -95
  29. data/app/datatables/effective_customers_datatable.rb +21 -29
  30. data/app/datatables/effective_order_items_datatable.rb +77 -79
  31. data/app/datatables/effective_orders_datatable.rb +67 -57
  32. data/app/helpers/effective_carts_helper.rb +17 -14
  33. data/app/helpers/effective_orders_helper.rb +40 -56
  34. data/app/helpers/effective_paypal_helper.rb +3 -3
  35. data/app/helpers/effective_stripe_helper.rb +47 -18
  36. data/app/helpers/effective_subscriptions_helper.rb +79 -0
  37. data/app/mailers/effective/orders_mailer.rb +125 -2
  38. data/app/models/concerns/acts_as_purchasable.rb +23 -33
  39. data/app/models/concerns/acts_as_subscribable.rb +68 -0
  40. data/app/models/concerns/acts_as_subscribable_buyer.rb +22 -0
  41. data/app/models/effective/cart.rb +53 -24
  42. data/app/models/effective/cart_item.rb +6 -12
  43. data/app/models/effective/customer.rb +51 -54
  44. data/app/models/effective/order.rb +160 -147
  45. data/app/models/effective/order_item.rb +18 -21
  46. data/app/models/effective/product.rb +7 -7
  47. data/app/models/effective/providers/ccbill_postback.rb +1 -1
  48. data/app/models/effective/providers/stripe_charge.rb +8 -19
  49. data/app/models/effective/subscripter.rb +230 -0
  50. data/app/models/effective/subscription.rb +27 -76
  51. data/app/models/effective/tax_rate_calculator.rb +10 -7
  52. data/app/views/admin/customers/_actions.html.haml +1 -2
  53. data/app/views/admin/customers/index.html.haml +1 -1
  54. data/app/views/admin/customers/show.html.haml +6 -0
  55. data/app/views/admin/orders/_actions.html.haml +9 -7
  56. data/app/views/admin/orders/_form.html.haml +11 -7
  57. data/app/views/admin/orders/_order_actions.html.haml +2 -1
  58. data/app/views/admin/orders/_order_item_fields.html.haml +1 -1
  59. data/app/views/admin/orders/edit.html.haml +4 -0
  60. data/app/views/admin/orders/index.html.haml +1 -4
  61. data/app/views/admin/orders/new.html.haml +1 -1
  62. data/app/views/admin/orders/show.html.haml +5 -6
  63. data/app/views/effective/carts/_cart.html.haml +2 -2
  64. data/app/views/effective/carts/show.html.haml +2 -2
  65. data/app/views/effective/customers/_customer.html.haml +152 -0
  66. data/app/views/effective/customers/_fields.html.haml +12 -0
  67. data/app/views/effective/customers/_form.html.haml +13 -0
  68. data/app/views/effective/customers/edit.html.haml +3 -0
  69. data/app/views/effective/orders/_checkout_step1.html.haml +8 -15
  70. data/app/views/effective/orders/_checkout_step2.html.haml +34 -21
  71. data/app/views/effective/orders/_order.html.haml +8 -9
  72. data/app/views/effective/orders/_order_actions.html.haml +7 -8
  73. data/app/views/effective/orders/_order_header.html.haml +1 -1
  74. data/app/views/effective/orders/_order_items.html.haml +11 -5
  75. data/app/views/effective/orders/_order_note.html.haml +4 -7
  76. data/app/views/effective/orders/_orders_table.html.haml +26 -26
  77. data/app/views/effective/orders/app_checkout/_form.html.haml +2 -2
  78. data/app/views/effective/orders/ccbill/_form.html.haml +1 -1
  79. data/app/views/effective/orders/cheque/_form.html.haml +3 -1
  80. data/app/views/effective/orders/declined.html.haml +1 -1
  81. data/app/views/effective/orders/{checkout_step1.html.haml → edit.html.haml} +0 -0
  82. data/app/views/effective/orders/free/_form.html.haml +4 -0
  83. data/app/views/effective/orders/index.html.haml +2 -4
  84. data/app/views/effective/orders/mark_as_paid/_form.html.haml +32 -0
  85. data/app/views/effective/orders/moneris/_form.html.haml +6 -6
  86. data/app/views/effective/orders/{checkout_step2.html.haml → new.html.haml} +1 -1
  87. data/app/views/effective/orders/paypal/_form.html.haml +2 -2
  88. data/app/views/effective/orders/pretend/_form.html.haml +2 -2
  89. data/app/views/effective/orders/purchased.html.haml +3 -0
  90. data/app/views/effective/orders/refund/_form.html.haml +32 -0
  91. data/app/views/effective/orders/show.html.haml +4 -1
  92. data/app/views/effective/orders/stripe/_form.html.haml +5 -5
  93. data/app/views/effective/orders_mailer/subscription_canceled.html.haml +9 -0
  94. data/app/views/effective/orders_mailer/subscription_payment_failed.html.haml +9 -0
  95. data/app/views/effective/orders_mailer/subscription_payment_succeeded.html.haml +9 -0
  96. data/app/views/effective/orders_mailer/subscription_trial_expired.html.haml +5 -0
  97. data/app/views/effective/orders_mailer/subscription_trial_expiring.html.haml +7 -0
  98. data/app/views/effective/subscriptions/_fields.html.haml +16 -0
  99. data/app/views/effective/subscriptions/_plan.html.haml +21 -0
  100. data/app/views/layouts/effective_orders_mailer_layout.html.haml +6 -8
  101. data/config/effective_orders.rb +41 -20
  102. data/config/routes.rb +48 -48
  103. data/db/migrate/01_create_effective_orders.rb.erb +19 -5
  104. data/lib/effective_orders.rb +78 -42
  105. data/lib/effective_orders/engine.rb +36 -82
  106. data/lib/effective_orders/version.rb +1 -1
  107. data/lib/generators/effective_orders/install_generator.rb +2 -2
  108. data/lib/generators/templates/effective_orders_mailer_preview.rb +39 -4
  109. data/lib/tasks/effective_orders_tasks.rake +42 -0
  110. data/spec/controllers/carts_controller_spec.rb +1 -1
  111. data/spec/controllers/moneris_orders_controller_spec.rb +4 -4
  112. data/spec/controllers/orders_controller_spec.rb +4 -4
  113. data/spec/controllers/stripe_orders_controller_spec.rb +2 -2
  114. data/spec/controllers/webhooks_controller_spec.rb +1 -1
  115. data/spec/dummy/config/initializers/effective_orders.rb +1 -7
  116. data/spec/dummy/db/schema.rb +1 -0
  117. data/spec/dummy/db/test.sqlite3 +0 -0
  118. data/spec/dummy/log/test.log +3 -0
  119. data/spec/models/acts_as_purchasable_spec.rb +0 -56
  120. data/spec/models/customer_spec.rb +3 -3
  121. data/spec/models/order_spec.rb +2 -2
  122. data/spec/spec_helper.rb +1 -1
  123. data/spec/support/factories.rb +2 -1
  124. metadata +37 -49
  125. data/active_admin/effective_carts.rb +0 -14
  126. data/active_admin/effective_orders.rb +0 -112
  127. data/app/assets/javascripts/effective_orders/providers/stripe_subscriptions.js.coffee +0 -28
  128. data/app/controllers/concerns/acts_as_active_admin_controller.rb +0 -69
  129. data/app/controllers/effective/subscriptions_controller.rb +0 -126
  130. data/app/models/effective/datatables/customers.rb +0 -40
  131. data/app/models/effective/datatables/order_items.rb +0 -101
  132. data/app/models/effective/datatables/orders.rb +0 -91
  133. data/app/models/inputs/price_field.rb +0 -63
  134. data/app/models/inputs/price_form_input.rb +0 -7
  135. data/app/models/inputs/price_formtastic_input.rb +0 -9
  136. data/app/models/inputs/price_input.rb +0 -19
  137. data/app/models/inputs/price_simple_form_input.rb +0 -8
  138. data/app/views/admin/orders/_form_mark_as_paid.html.haml +0 -33
  139. data/app/views/admin/orders/_order_payment_details.html.haml +0 -5
  140. data/app/views/admin/orders/mark_as_paid.html.haml +0 -7
  141. data/app/views/effective/orders/stripe/_subscription_fields.html.haml +0 -7
  142. data/app/views/effective/subscriptions/index.html.haml +0 -22
  143. data/app/views/effective/subscriptions/new.html.haml +0 -9
  144. data/app/views/effective/subscriptions/show.html.haml +0 -49
  145. data/db/upgrade/02_upgrade_effective_orders_from03x.rb.erb +0 -29
  146. data/db/upgrade/03_upgrade_effective_orders_from1x.rb.erb +0 -98
  147. data/db/upgrade/upgrade_price_column_on_table.rb.erb +0 -17
  148. data/lib/generators/effective_orders/upgrade_from03x_generator.rb +0 -31
  149. data/lib/generators/effective_orders/upgrade_from1x_generator.rb +0 -27
  150. data/lib/generators/effective_orders/upgrade_price_column_generator.rb +0 -30
@@ -1,8 +1,8 @@
1
1
  module EffectiveOrdersHelper
2
- def price_to_currency(price, options = {})
3
- raise 'price_to_currency expects an Integer representing the number of cents in a price' unless price.kind_of?(Integer)
4
- options[:precision] ||= 2
5
- number_to_currency(price / 100.0, options)
2
+ def price_to_currency(price)
3
+ price = price || 0
4
+ raise 'price_to_currency expects an Integer representing the number of cents' unless price.kind_of?(Integer)
5
+ number_to_currency(price / 100.0)
6
6
  end
7
7
 
8
8
  def tax_rate_to_percentage(tax_rate, options = {})
@@ -33,11 +33,15 @@ module EffectiveOrdersHelper
33
33
  end
34
34
 
35
35
  def order_checkout_label(processor = nil)
36
- return 'Checkout' if (EffectiveOrders.single_payment_processor? && processor != :pretend)
36
+ return 'Checkout' if (EffectiveOrders.single_payment_processor? && ![:pretend, :mark_as_paid, :free, :refund].include?(processor))
37
37
 
38
38
  case processor
39
+ when :mark_as_paid
40
+ 'Mark as paid'
39
41
  when :free
40
- 'Checkout'
42
+ 'Checkout free'
43
+ when :refund
44
+ 'Complete refund'
41
45
  when :moneris, :stripe, :ccbill
42
46
  'Checkout with credit card'
43
47
  when :paypal
@@ -45,7 +49,7 @@ module EffectiveOrdersHelper
45
49
  when :pretend
46
50
  EffectiveOrders.allow_pretend_purchase_in_production ? 'Purchase Order' : 'Purchase Order (development only)'
47
51
  when :cheque
48
- 'Pay by Cheque'
52
+ 'Pay by cheque'
49
53
  when :app_checkout
50
54
  EffectiveOrders.app_checkout[:checkout_label].presence || 'Checkout'
51
55
  else
@@ -82,27 +86,38 @@ module EffectiveOrdersHelper
82
86
  end
83
87
 
84
88
  def render_order(order)
85
- render(partial: 'effective/orders/order', locals: {order: order})
89
+ render(partial: 'effective/orders/order', locals: { order: order })
86
90
  end
87
91
 
88
- def render_checkout_step1(order, opts = {})
89
- raise ArgumentError.new('unable to checkout an order without a user') unless order.user.present?
92
+ def render_checkout_step1(order, namespace: nil, purchased_url: nil, declined_url: nil)
93
+ raise 'unable to checkout an order without a user' unless order && order.user
90
94
 
91
- locals = { purchased_redirect_url: nil, declined_redirect_url: nil }.merge(opts)
95
+ locals = { order: order, purchased_url: purchased_url, declined_url: declined_url, namespace: namespace }
92
96
 
93
- render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
97
+ render partial: 'effective/orders/checkout_step1', locals: locals
94
98
  end
95
99
  alias_method :render_checkout, :render_checkout_step1
96
100
 
97
- def render_checkout_step2(order, opts = {})
98
- raise ArgumentError.new('unable to checkout an order without a user') unless order.user.present?
101
+ def render_checkout_step2(order, namespace: nil, purchased_url: nil, declined_url: nil)
102
+ raise 'unable to checkout an order without a user' unless order && order.user
99
103
 
100
- locals = { purchased_redirect_url: nil, declined_redirect_url: nil }.merge(opts)
104
+ locals = { order: order, purchased_url: purchased_url, declined_url: declined_url, namespace: namespace }
101
105
 
102
106
  if order.new_record? || !order.valid?
103
- render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
107
+ render(partial: 'effective/orders/checkout_step1', locals: locals)
108
+ else
109
+ render(partial: 'effective/orders/checkout_step2', locals: locals)
110
+ end
111
+ end
112
+
113
+ def checkout_step1_form_url(order, namespace = nil)
114
+ raise 'expected an order' unless order
115
+ raise 'invalid namespace, expecting nil or :admin' unless [nil, :admin].include?(namespace)
116
+
117
+ if order.new_record?
118
+ namespace == nil ? effective_orders.orders_path : effective_orders.admin_orders_path
104
119
  else
105
- render(partial: 'effective/orders/checkout_step2', locals: locals.merge({order: order}))
120
+ namespace == nil ? effective_orders.order_path(order) : effective_orders.checkout_admin_order_path(order)
106
121
  end
107
122
  end
108
123
 
@@ -116,56 +131,25 @@ module EffectiveOrdersHelper
116
131
  label = options.delete(:label)
117
132
  options[:class] = ((options[:class] || '') + ' btn-my-purchases')
118
133
 
119
- link_to(label, effective_orders.my_purchases_path, options)
134
+ link_to(label, effective_orders.my_purchases_orders_path, options)
120
135
  end
121
136
  alias_method :link_to_order_history, :link_to_my_purchases
122
137
 
123
- def render_orders(user_or_orders, opts = {})
124
- if user_or_orders.kind_of?(User)
125
- orders = Effective::Order.purchased_by(user_or_orders)
126
- elsif user_or_orders.respond_to?(:to_a)
127
- orders = user_or_orders.to_a
128
- else
129
- raise ArgumentError.new('expecting an instance of User or an array/collection of Effective::Order objects')
138
+
139
+ def render_orders(obj, opts = {})
140
+ orders = Array(obj.kind_of?(User) ? Effective::Order.purchased_by(obj) : obj)
141
+
142
+ if orders.any? { |order| order.kind_of?(Effective::Order) == false }
143
+ raise 'expected a User or Effective::Order'
130
144
  end
131
145
 
132
- render(partial: 'effective/orders/orders_table', locals: {orders: orders}.merge(opts))
146
+ render(partial: 'effective/orders/orders_table', locals: { orders: orders }.merge(opts))
133
147
  end
134
148
 
135
149
  alias_method :render_purchases, :render_orders
136
150
  alias_method :render_my_purchases, :render_orders
137
151
  alias_method :render_order_history, :render_orders
138
152
 
139
- # Used by the _payment_details partial
140
- def tableize_order_payment(hash, options = {class: 'table table-bordered'})
141
- if hash.present? && hash.kind_of?(Hash)
142
- content_tag(:table, class: options[:class]) do
143
- title = options.delete(:title)
144
-
145
- content = content_tag(:tbody) do
146
- hash.map do |k, v|
147
- content_tag(:tr) do
148
- content_tag((options[:th] ? :th : :td), k) +
149
- content_tag(:td) do
150
- if v.kind_of?(Hash)
151
- tableize_order_payment(v, options.merge(th: (options.key?(:sub_th) ? options[:sub_th] : options[:th])))
152
- elsif v.kind_of?(Array)
153
- '[' + v.join(', ') + ']'
154
- else
155
- v
156
- end
157
- end
158
- end
159
- end.join.html_safe
160
- end
161
-
162
- title.blank? ? content : (content_tag(:thead) { content_tag(:tr) { content_tag(:th, title, colspan: 2) } } + content)
163
- end
164
- else
165
- hash.to_s.html_safe
166
- end
167
- end
168
-
169
153
  def payment_card_label(card)
170
154
  card = card.to_s.downcase.gsub(' ', '').strip
171
155
 
@@ -15,9 +15,9 @@ module EffectivePaypalHelper
15
15
  APP_KEY_PEM = ConfigReader.cert_or_key(:app_key)
16
16
 
17
17
  def paypal_encrypted_payload(order)
18
- raise ArgumentError.new("unable to read EffectiveOrders PayPal paypal_cert #{EffectiveOrders.paypal[:paypal_cert]}") unless PAYPAL_CERT_PEM.present?
19
- raise ArgumentError.new("unable to read EffectiveOrders PayPal app_cert #{EffectiveOrders.paypal[:app_cert]}") unless APP_CERT_PEM.present?
20
- raise ArgumentError.new("unable to read EffectiveOrders PayPal app_key #{EffectiveOrders.paypal[:app_key]}") unless APP_KEY_PEM.present?
18
+ raise "unable to read EffectiveOrders PayPal paypal_cert #{EffectiveOrders.paypal[:paypal_cert]}" unless PAYPAL_CERT_PEM.present?
19
+ raise "unable to read EffectiveOrders PayPal app_cert #{EffectiveOrders.paypal[:app_cert]}" unless APP_CERT_PEM.present?
20
+ raise "unable to read EffectiveOrders PayPal app_key #{EffectiveOrders.paypal[:app_key]}" unless APP_KEY_PEM.present?
21
21
 
22
22
  values = {
23
23
  business: EffectiveOrders.paypal[:seller_email],
@@ -4,13 +4,13 @@ module EffectiveStripeHelper
4
4
  STRIPE_CONNECT_TOKEN_URL = 'https://connect.stripe.com/oauth/token'
5
5
 
6
6
  def is_stripe_connect_seller?(user)
7
- Effective::Customer.for_user(user).try(:is_stripe_connect_seller?) == true
7
+ Effective::Customer.for_buyer(user).stripe_connect_access_token.present?
8
8
  end
9
9
 
10
10
  def link_to_new_stripe_connect_customer(opts = {})
11
11
  client_id = EffectiveOrders.stripe[:connect_client_id]
12
12
 
13
- raise ArgumentError.new('effective_orders config: stripe.connect_client_id has not been set') unless client_id.present?
13
+ raise 'effective_orders config: stripe.connect_client_id has not been set' unless client_id.present?
14
14
 
15
15
  authorize_params = {
16
16
  response_type: :code,
@@ -26,28 +26,35 @@ module EffectiveStripeHelper
26
26
  stripe_user_params = opts.delete :stripe_user
27
27
  authorize_params.merge!({stripe_user: stripe_user_params}) if stripe_user_params.is_a?(Hash)
28
28
 
29
- authorize_url = STRIPE_CONNECT_AUTHORIZE_URL.chomp('/') + '?' + authorize_params.to_query
29
+ authorize_url = STRIPE_CONNECT_AUTHORIZE_URL + '?' + authorize_params.to_query
30
30
  options = {}.merge(opts)
31
31
  link_to image_tag('/assets/effective_orders/stripe_connect.png'), authorize_url, options
32
32
  end
33
33
 
34
- ### Subscriptions Helpers
35
- def stripe_plans_collection(plans)
36
- (plans || []).map { |plan| [stripe_plan_description(plan), plan.id, {'data-amount' => plan.amount}] }
37
- end
34
+ def stripe_plan_description(obj)
35
+ plan = (
36
+ case obj
37
+ when Hash ; obj
38
+ when ::Stripe::Plan ; EffectiveOrders.stripe_plans.find { |plan| plan.id == obj.id }
39
+ else ; raise 'unexpected object'
40
+ end
41
+ )
38
42
 
39
- def stripe_plan_description(plan)
40
- occurrence = case plan.interval
41
- when 'weekly' ; '/week'
42
- when 'monthly' ; '/month'
43
- when 'yearly' ; '/year'
44
- when 'week' ; plan.interval_count == 1 ? '/week' : " every #{plan.interval_count} weeks"
45
- when 'month' ; plan.interval_count == 1 ? '/month' : " every #{plan.interval_count} months"
46
- when 'year' ; plan.interval_count == 1 ? '/year' : " every #{plan.interval_count} years"
47
- else ; plan.interval
48
- end
43
+ raise("unknown stripe plan: #{obj}") unless plan.kind_of?(Hash) && plan[:id].present?
44
+
45
+ plan[:description]
46
+ end
49
47
 
50
- "#{plan.name} - #{ActionController::Base.helpers.price_to_currency(plan.amount)} #{plan.currency.upcase}#{occurrence}"
48
+ def stripe_invoice_line_description(line, simple: false)
49
+ [
50
+ "#{line.quantity}x",
51
+ line.plan.name,
52
+ price_to_currency(line.amount),
53
+ ("#{Time.zone.at(line.period.start).strftime('%F')}" unless simple),
54
+ ('to' unless simple),
55
+ ("#{Time.zone.at(line.period.end).strftime('%F')}" unless simple),
56
+ line.description.presence
57
+ ].compact.join(' ')
51
58
  end
52
59
 
53
60
  def stripe_coupon_description(coupon)
@@ -60,4 +67,26 @@ module EffectiveStripeHelper
60
67
  end
61
68
  end
62
69
 
70
+ def stripe_site_image_url
71
+ return nil unless EffectiveOrders.stripe && (url = EffectiveOrders.stripe[:site_image].to_s).present?
72
+ url.start_with?('http') ? url : asset_url(url)
73
+ end
74
+
75
+ def stripe_order_description(order)
76
+ "#{order.num_items} items (#{price_to_currency(order.total)})"
77
+ end
78
+
79
+ def stripe_charge_data(order)
80
+ {
81
+ stripe: {
82
+ key: EffectiveOrders.stripe[:publishable_key],
83
+ name: EffectiveOrders.stripe[:site_title],
84
+ image: stripe_site_image_url,
85
+ email: order.user.email,
86
+ amount: order.total,
87
+ description: stripe_order_description(order)
88
+ }.to_json
89
+ }
90
+ end
91
+
63
92
  end
@@ -0,0 +1,79 @@
1
+ module EffectiveSubscriptionsHelper
2
+
3
+ def effective_customer_fields(form, submit: true)
4
+ raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
5
+ raise 'form object must be an Effective::Subscripter object' unless form.object.class.name == 'Effective::Subscripter'
6
+
7
+ render(
8
+ partial: 'effective/customers/fields',
9
+ locals: {
10
+ f: form,
11
+ submit: submit,
12
+ stripe: {
13
+ email: form.object.customer.user.email,
14
+ image: stripe_site_image_url,
15
+ key: EffectiveOrders.stripe[:publishable_key],
16
+ name: EffectiveOrders.stripe[:site_title],
17
+ }
18
+ }
19
+ )
20
+ end
21
+
22
+ def stripe_plans_collection(form, include_trial: nil, selected_class: 'selected panel-primary')
23
+ raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
24
+ raise 'form object must be an acts_as_subscribable object' unless form.object.subscribable.subscripter.present?
25
+
26
+ include_trial = form.object.subscribable.subscribed?('trial') if include_trial.nil?
27
+
28
+ plans = include_trial ? EffectiveOrders.stripe_plans : EffectiveOrders.stripe_plans.except('trial')
29
+ plans = plans.values.sort { |x, y| (amount = x[:amount] <=> y[:amount]) != 0 ? amount : x[:name] <=> y[:name] }
30
+
31
+ plans.map do |plan|
32
+ partial = (
33
+ if lookup_context.template_exists?("effective/subscriptions/#{plan[:id].downcase}", [], true)
34
+ "effective/subscriptions/#{plan[:id].downcase}" # Render the app's views/effective/subscriptions/_gold.html.haml
35
+ else
36
+ 'effective/subscriptions/plan' # Render effective_orders default plan panel
37
+ end
38
+ )
39
+
40
+ content = render(partial: partial, locals: {
41
+ f: form,
42
+ plan: plan,
43
+ selected: Array(form.object.stripe_plan_id).include?(plan[:id]),
44
+ selected_class: selected_class,
45
+ subscribable: form.object.subscribable,
46
+ subscribed: form.object.subscribable.subscribed?(plan[:id])
47
+ })
48
+
49
+ [content, plan[:id]]
50
+ end
51
+ end
52
+
53
+ def effective_subscription_fields(form, label: false, required: true, include_trial: nil, item_wrapper_class: 'col-sm-6 col-md-4 col-lg-3', selected_class: 'selected panel-primary', wrapper_class: 'row')
54
+ raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
55
+ raise 'form object must be an acts_as_subscribable object' unless form.object.subscripter.present?
56
+
57
+ render(
58
+ partial: 'effective/subscriptions/fields',
59
+ locals: {
60
+ form: form,
61
+ label: label,
62
+ required: required,
63
+ include_trial: include_trial,
64
+ item_wrapper_class: item_wrapper_class,
65
+ selected_class: selected_class,
66
+ stripe: {
67
+ email: form.object.buyer.email,
68
+ image: stripe_site_image_url,
69
+ key: EffectiveOrders.stripe[:publishable_key],
70
+ name: EffectiveOrders.stripe[:site_title],
71
+ plans: EffectiveOrders.stripe_plans.values,
72
+ token_required: form.object.subscripter.token_required?
73
+ },
74
+ wrapper_class: wrapper_class
75
+ }
76
+ )
77
+ end
78
+
79
+ end
@@ -5,6 +5,8 @@ module Effective
5
5
  layout EffectiveOrders.mailer[:layout].presence || 'effective_orders_mailer_layout'
6
6
 
7
7
  def order_receipt_to_admin(order_param)
8
+ return true unless EffectiveOrders.mailer[:send_order_receipt_to_admin]
9
+
8
10
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
9
11
 
10
12
  mail(
@@ -15,6 +17,8 @@ module Effective
15
17
  end
16
18
 
17
19
  def order_receipt_to_buyer(order_param) # Buyer
20
+ return true unless EffectiveOrders.mailer[:send_order_receipt_to_buyer]
21
+
18
22
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
19
23
 
20
24
  mail(
@@ -25,6 +29,8 @@ module Effective
25
29
  end
26
30
 
27
31
  def order_receipt_to_seller(order_param, seller, order_items)
32
+ return true unless EffectiveOrders.mailer[:send_order_receipt_to_seller]
33
+
28
34
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
29
35
  @user = seller.user
30
36
  @order_items = order_items
@@ -40,6 +46,8 @@ module Effective
40
46
  # This is sent when an admin creates a new order or /admin/orders/new
41
47
  # Or uses the order action Send Payment Request
42
48
  def payment_request_to_buyer(order_param)
49
+ return true unless EffectiveOrders.mailer[:send_payment_request_to_buyer]
50
+
43
51
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
44
52
 
45
53
  mail(
@@ -51,6 +59,8 @@ module Effective
51
59
 
52
60
  # This is sent when someone chooses to Pay by Cheque
53
61
  def pending_order_invoice_to_buyer(order_param)
62
+ return true unless EffectiveOrders.mailer[:send_pending_order_invoice_to_buyer]
63
+
54
64
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
55
65
 
56
66
  mail(
@@ -60,6 +70,71 @@ module Effective
60
70
  )
61
71
  end
62
72
 
73
+ # Sent by the invoice.payment_succeeded webhook event
74
+ def subscription_payment_succeeded(customer_param)
75
+ return true unless EffectiveOrders.mailer[:send_subscription_payment_succeeded]
76
+
77
+ @customer = (customer_param.kind_of?(Effective::Customer) ? customer_param : Effective::Customer.find(customer_param))
78
+
79
+ mail(
80
+ to: @customer.user.email,
81
+ from: EffectiveOrders.mailer[:default_from],
82
+ subject: subject_for_subscription_payment_succeeded(@customer)
83
+ )
84
+ end
85
+
86
+ # Sent by the invoice.payment_failed webhook event
87
+ def subscription_payment_failed(customer_param)
88
+ return true unless EffectiveOrders.mailer[:send_subscription_payment_failed]
89
+
90
+ @customer = (customer_param.kind_of?(Effective::Customer) ? customer_param : Effective::Customer.find(customer_param))
91
+
92
+ mail(
93
+ to: @customer.user.email,
94
+ from: EffectiveOrders.mailer[:default_from],
95
+ subject: subject_for_subscription_payment_failed(@customer)
96
+ )
97
+ end
98
+
99
+ # Sent by the invoice.payment_failed webhook event
100
+ def subscription_canceled(customer_param)
101
+ return true unless EffectiveOrders.mailer[:send_subscription_canceled]
102
+
103
+ @customer = (customer_param.kind_of?(Effective::Customer) ? customer_param : Effective::Customer.find(customer_param))
104
+
105
+ mail(
106
+ to: @customer.user.email,
107
+ from: EffectiveOrders.mailer[:default_from],
108
+ subject: subject_for_subscription_canceled(@customer)
109
+ )
110
+ end
111
+
112
+ # Sent by the effective_orders:notify_trial_users rake task.
113
+ def subscription_trial_expiring(subscribable)
114
+ return true unless EffectiveOrders.mailer[:send_subscription_trial_expiring]
115
+
116
+ @subscribable = subscribable
117
+
118
+ mail(
119
+ to: @subscribable.buyer.email,
120
+ from: EffectiveOrders.mailer[:default_from],
121
+ subject: subject_for_subscription_trial_expiring(@subscribable)
122
+ )
123
+ end
124
+
125
+ # Sent by the effective_orders:notify_trial_users rake task.
126
+ def subscription_trial_expired(subscribable)
127
+ return true unless EffectiveOrders.mailer[:send_subscription_trial_expired]
128
+
129
+ @subscribable = subscribable
130
+
131
+ mail(
132
+ to: @subscribable.buyer.email,
133
+ from: EffectiveOrders.mailer[:default_from],
134
+ subject: subject_for_subscription_trial_expired(@subscribable)
135
+ )
136
+ end
137
+
63
138
  def order_error(order: nil, error: nil, to: nil, from: nil, subject: nil, template: 'order_error')
64
139
  if order.present?
65
140
  @order = (order.kind_of?(Effective::Order) ? order : Effective::Order.find(order))
@@ -77,7 +152,6 @@ module Effective
77
152
  ) do |format|
78
153
  format.html { render(template) }
79
154
  end
80
-
81
155
  end
82
156
 
83
157
  private
@@ -103,7 +177,7 @@ module Effective
103
177
  end
104
178
 
105
179
  def subject_for_order_receipt_to_seller(order, order_items, seller)
106
- string_or_callable = EffectiveOrders.mailer[:subject_for_seller_receipt]
180
+ string_or_callable = EffectiveOrders.mailer[:subject_for_order_receipt_to_seller]
107
181
 
108
182
  if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
109
183
  string_or_callable = self.instance_exec(order, order_items, seller, &string_or_callable)
@@ -132,6 +206,55 @@ module Effective
132
206
  prefix_subject(string_or_callable.presence || "Pending Order: ##{order.to_param}")
133
207
  end
134
208
 
209
+ def subject_for_subscription_payment_succeeded(customer)
210
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_payment_succeeded]
211
+
212
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
213
+ string_or_callable = self.instance_exec(order, &string_or_callable)
214
+ end
215
+
216
+ prefix_subject(string_or_callable.presence || 'Thank you for your payment')
217
+ end
218
+
219
+ def subject_for_subscription_payment_failed(customer)
220
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_payment_failed]
221
+
222
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
223
+ string_or_callable = self.instance_exec(order, &string_or_callable)
224
+ end
225
+
226
+ prefix_subject(string_or_callable.presence || 'Payment failed - please update your card details')
227
+ end
228
+
229
+ def subject_for_subscription_canceled(customer)
230
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_canceled]
231
+
232
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
233
+ string_or_callable = self.instance_exec(order, &string_or_callable)
234
+ end
235
+
236
+ prefix_subject(string_or_callable.presence || 'Subscription canceled')
237
+ end
238
+
239
+ def subject_for_subscription_trial_expiring(customer)
240
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_trial_expiring]
241
+
242
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
243
+ string_or_callable = self.instance_exec(order, &string_or_callable)
244
+ end
245
+
246
+ prefix_subject(string_or_callable.presence || 'Trial expiring soon')
247
+ end
248
+
249
+ def subject_for_subscription_trial_expired(customer)
250
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_trial_expired]
251
+
252
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
253
+ string_or_callable = self.instance_exec(order, &string_or_callable)
254
+ end
255
+
256
+ prefix_subject(string_or_callable.presence || 'Trial expired')
257
+ end
135
258
 
136
259
  def prefix_subject(text)
137
260
  prefix = (EffectiveOrders.mailer[:subject_prefix].to_s rescue '')