effective_orders 4.0.0beta4 → 4.0.0beta5

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +28 -10
  3. data/app/assets/javascripts/effective_orders.js +3 -1
  4. data/app/assets/javascripts/effective_orders/customers.js.coffee +10 -15
  5. data/app/assets/javascripts/effective_orders/providers/stripe.js.coffee +4 -14
  6. data/app/assets/javascripts/effective_orders/subscriptions.js.coffee +18 -35
  7. data/app/assets/stylesheets/effective_orders.scss +0 -1
  8. data/app/controllers/admin/orders_controller.rb +1 -1
  9. data/app/datatables/effective_order_items_datatable.rb +2 -2
  10. data/app/helpers/effective_orders_helper.rb +3 -3
  11. data/app/helpers/effective_paypal_helper.rb +1 -1
  12. data/app/helpers/effective_subscriptions_helper.rb +7 -11
  13. data/app/mailers/effective/orders_mailer.rb +2 -2
  14. data/app/models/concerns/acts_as_purchasable.rb +3 -6
  15. data/app/models/concerns/acts_as_subscribable.rb +34 -20
  16. data/app/models/effective/cart.rb +1 -1
  17. data/app/models/effective/cart_item.rb +9 -5
  18. data/app/models/effective/customer.rb +4 -2
  19. data/app/models/effective/order.rb +2 -2
  20. data/app/models/effective/order_item.rb +3 -3
  21. data/app/models/effective/product.rb +3 -3
  22. data/app/models/effective/subscripter.rb +4 -4
  23. data/app/models/effective/subscription.rb +3 -14
  24. data/app/views/admin/orders/_order_item_fields.html.haml +1 -1
  25. data/app/views/effective/carts/_cart.html.haml +1 -1
  26. data/app/views/effective/customers/_customer.html.haml +2 -9
  27. data/app/views/effective/customers/_fields.html.haml +6 -9
  28. data/app/views/effective/customers/_form.html.haml +3 -4
  29. data/app/views/effective/orders/_order_items.html.haml +1 -1
  30. data/app/views/effective/orders/moneris/_form.html.haml +1 -1
  31. data/app/views/effective/subscriptions/_fields.html.haml +5 -9
  32. data/app/views/effective/subscriptions/_plan.html.haml +16 -17
  33. data/config/effective_orders.rb +18 -12
  34. data/db/migrate/01_create_effective_orders.rb.erb +2 -5
  35. data/lib/effective_orders.rb +22 -7
  36. data/lib/effective_orders/version.rb +1 -1
  37. data/lib/generators/templates/effective_orders_mailer_preview.rb +4 -4
  38. data/lib/tasks/effective_orders_tasks.rake +9 -9
  39. metadata +2 -3
  40. data/app/assets/stylesheets/effective_orders/_subscriptions.scss +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4c93ba62efdec37fd8c277afca2a28090303b8c
4
- data.tar.gz: 8e89c28503192d4373aca8b13762ab8fede41744
3
+ metadata.gz: bd54f1217fe33988f924bf40803efd702916d971
4
+ data.tar.gz: 291f290fd3cf4b8636ce5103a86b976760e12122
5
5
  SHA512:
6
- metadata.gz: 5013c1845227a4117f94eb1a7f414b0fa048ecf8e617da3dd82eee2ad16326246edc726877eb898ba0b97ccc739f8909373126d4b1162cb122f393a68bbc0ee3
7
- data.tar.gz: 51328fba62557431d4ed163a67bb9c61996cb38cff150fb8706c9398b55aef69139b1264bcd1dac3028dba64583bf5833f4daa7695ca6a56d519566404d38d81
6
+ metadata.gz: 546166ac0b6f8948af732a82bdd2ba8b2e59f11ce6f734d10b1d6dd49350926d764331fe179d71f01a3a9d4510786254dfd6bbfeac0cffb992b821d03ea70c6c
7
+ data.tar.gz: 1d1602f8456cbf830c82ca6f298b85ca465b384240a5867479df1280a7384c1693e5b1970ff21e9b353bba3371268da37fb21ae4fb17a82bef993be0c74c5418
data/README.md CHANGED
@@ -118,12 +118,12 @@ class Product < ActiveRecord::Base
118
118
  acts_as_purchasable
119
119
 
120
120
  # Attributes
121
- # title :string
121
+ # name :string
122
122
  # price :integer, default: 0
123
123
  # tax_exempt :boolean, default: false
124
124
  # timestamps
125
125
 
126
- validates_presence_of :title
126
+ validates_presence_of :name
127
127
  validates_numericality_of :price, greater_than_or_equal_to: 0
128
128
  end
129
129
  ```
@@ -134,7 +134,7 @@ The database migration will look like the following:
134
134
  class CreateProducts < ActiveRecord::Migration
135
135
  def self.up
136
136
  create_table :products do |t|
137
- t.string :title
137
+ t.string :name
138
138
  t.integer :price, :default=>0
139
139
  t.boolean :tax_exempt, :default=>false
140
140
  t.datetime :updated_at
@@ -160,7 +160,7 @@ This is available for simple_form, formtastic and Rails default FormBuilder.
160
160
 
161
161
  ```haml
162
162
  = simple_form_for(@product) do |f|
163
- = f.input :title
163
+ = f.input :name
164
164
  = f.input :tax_exempt
165
165
  = f.input :price, as: :effective_price
166
166
  = f.button :submit
@@ -185,7 +185,7 @@ or
185
185
  So back on the Product#show page, we will render the product with an Add To Cart link
186
186
 
187
187
  ```haml
188
- %h4= @product.title
188
+ %h4= @product
189
189
  %p= price_to_currency(@product.price)
190
190
  %p= link_to_add_to_cart(@product, class: 'btn btn-primary', label: 'Add To My Shopping Cart')
191
191
  ```
@@ -570,6 +570,24 @@ render_datatable @datatable
570
570
  Please refer to [effective_datatables](https://github.com/code-and-effect/effective_datatables/) for more information about that gem.
571
571
 
572
572
 
573
+ ### Subscriptions
574
+
575
+ All subscriptions are completed via stripe subscriptions.
576
+
577
+ There is a hardcoded trial mode, that does not reach out to stripe.
578
+
579
+ Every `acts_as_subscribable` object starts as trial mode.
580
+
581
+ Every `acts_as_subscribable_buyer` gets a single stripe subscription, and then can buy quantities of stripe products
582
+
583
+
584
+ #### Stripe setup
585
+
586
+ Create a Product with the name you want customers to see on their receipts
587
+
588
+ Yearly and a Monthly per team
589
+
590
+
573
591
  ### Admin Screen
574
592
 
575
593
  To use the Admin screen, please also install the effective_datatables gem:
@@ -598,16 +616,16 @@ render_datatable @datatable
598
616
 
599
617
  ## Rake Tasks
600
618
 
601
- ### Overwrite order item titles
619
+ ### Overwrite order item names
602
620
 
603
- When an order is purchased, the `title()` of each `acts_as_purchasable` object is saved to the database.
621
+ When an order is purchased, the `purchasable_name()` of each `acts_as_purchasable` object is saved to the database. Normally this is just `to_s`.
604
622
 
605
- If you change the output of `acts_as_purchasable`.`title`, any existing order items will remain unchanged.
623
+ If you change the output of `acts_as_purchasable`.`purchasable_name`, any existing order items will remain unchanged.
606
624
 
607
- Run this script to overwrite all saved order item titles with the current `acts_as_purchasable`.`title`.
625
+ Run this script to overwrite all saved order item names with the current `acts_as_purchasable`.`purchasable_name`.
608
626
 
609
627
  ```ruby
610
- rake effective_orders:overwrite_order_item_titles
628
+ rake effective_orders:overwrite_order_item_names
611
629
  ```
612
630
 
613
631
  ## Testing in Development
@@ -1,4 +1,6 @@
1
1
  //= require effective_addresses
2
2
 
3
- //= require_tree ./effective_orders
3
+ //= require ./effective_orders/customers
4
+ //= require ./effective_orders/subscriptions
5
+ //= require_tree ./effective_orders/providers
4
6
 
@@ -1,25 +1,22 @@
1
1
  stripeCustomerChangeCardHandler = (key, form) ->
2
2
  StripeCheckout.configure
3
3
  key: key
4
- closed: ->
5
- form.find("input[type='submit']").removeAttr('disabled')
6
- $('input[data-disable-with]').each -> try $.rails.enableFormElement($(this))
4
+ closed: -> EffectiveBootstrap.enable(form) unless form.hasClass('stripe-success')
7
5
  token: (token, args) ->
8
6
  if token.error
9
- form.find("input[type='submit']").removeAttr('disabled')
10
- $('input[data-disable-with]').each -> try $.rails.enableFormElement($(this))
7
+ message = "An error ocurred when contacting Stripe. Your card has not been charged. Your subscription has not changed. Please refresh the page and try again. #{token.error.message}"
11
8
 
12
- alert("An error ocurred when contacting Stripe. Your card has not been charged. Your subscription has not changed. Please refresh the page and try again. #{token.error.message}")
9
+ form.removeClass('stripe-success')
10
+ form.find('.effective-orders-customer').find('.invalid-feedback').html(message).show()
11
+ alert(message)
13
12
  else
14
13
  form.find("input[name$='[stripe_token]']").val('' + token['id'])
15
14
 
16
- customer = form.find('.effective-orders-customer')
17
- customer.find('.active-card').html("**** **** **** #{token.card.last4} #{token.card.brand} #{token.card.exp_month}/#{token.card.exp_year}")
15
+ $customer = form.find('.effective-orders-customer')
16
+ $customer.find('.payment-status').html("**** **** **** #{token.card.last4} #{token.card.brand} #{token.card.exp_month}/#{token.card.exp_year}")
18
17
 
19
- if customer.data('submit')
20
- form.find("input[type='submit']").prop('disabled', true)
21
- $('input[data-disable-with]').each -> try $.rails.disableFormElement($(this))
22
- form.submit()
18
+ if $customer.data('submit')
19
+ form.addClass('stripe-success').submit()
23
20
 
24
21
  # When we click 'Change credit card', make sure the form collects a credit card
25
22
  $(document).on 'click', '.effective-orders-customer .btn-change-card', (event) ->
@@ -28,9 +25,7 @@ $(document).on 'click', '.effective-orders-customer .btn-change-card', (event) -
28
25
  $form = $(event.currentTarget).closest('form')
29
26
  stripe = $(event.currentTarget).closest('.effective-orders-customer').data('stripe')
30
27
 
31
- # Disable the form
32
- $form.find("input[type='submit']").prop('disabled', true)
33
- $('input[data-disable-with]').each -> try $.rails.disableFormElement($(this))
28
+ EffectiveBootstrap.submitting($form)
34
29
 
35
30
  stripeCustomerChangeCardHandler(stripe.key, $form).open
36
31
  image: stripe.image
@@ -1,32 +1,22 @@
1
1
  stripeCheckoutHandler = (key, form) ->
2
2
  StripeCheckout.configure
3
3
  key: key
4
- closed: ->
5
- form.find("input[type='submit']").removeAttr('disabled')
6
- $('input[data-disable-with]').each -> try $.rails.enableFormElement($(this))
4
+ closed: -> EffectiveBootstrap.enable(form) unless form.hasClass('stripe-success')
7
5
  token: (token, args) ->
8
6
  if token.error
9
- form.find("input[type='submit']").removeAttr('disabled')
10
- $('input[data-disable-with]').each -> try $.rails.enableFormElement($(this))
11
-
12
7
  alert("An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}")
13
8
  else
14
9
  form.find("input[name$='[stripe_token]']").val('' + token['id'])
10
+ form.addClass('stripe-success').submit()
15
11
 
16
- form.find("input[type='submit']").prop('disabled', true)
17
- $('input[data-disable-with]').each -> try $.rails.disableFormElement($(this))
18
- form.submit()
19
-
20
- $(document).on 'click', "#effective-orders-new-charge-form form input[type='submit']", (event) ->
12
+ $(document).on 'click', "#effective-orders-new-charge-form form [type='submit']", (event) ->
21
13
  event.preventDefault()
22
14
 
23
15
  obj = $('#effective-orders-new-charge-form')
24
16
  $form = obj.find('form').first()
25
17
  stripe = obj.data('stripe')
26
18
 
27
- # Disable the form
28
- $form.find("input[type='submit']").prop('disabled', true)
29
- $('input[data-disable-with]').each -> try $.rails.disableFormElement($(this))
19
+ EffectiveBootstrap.submitting($form)
30
20
 
31
21
  stripeCheckoutHandler(stripe.key, $form).open
32
22
  image: stripe.image
@@ -1,35 +1,29 @@
1
1
  stripeSubscriptionHandler = (key, form) ->
2
2
  StripeCheckout.configure
3
3
  key: key
4
- closed: ->
5
- form.find("input[type='submit']").removeAttr('disabled')
6
- $('input[data-disable-with]').each -> try $.rails.enableFormElement($(this))
4
+ closed: -> EffectiveBootstrap.enable(form) unless form.hasClass('stripe-success')
7
5
  token: (token, args) ->
8
6
  if token.error
9
- form.find("input[type='submit']").removeAttr('disabled')
10
- $('input[data-disable-with]').each -> try $.rails.enableFormElement($(this))
7
+ message = "An error ocurred when contacting Stripe. Your card has not been charged. Your subscription has not changed. Please refresh the page and try again. #{token.error.message}"
11
8
 
12
- alert("An error ocurred when contacting Stripe. Your card has not been charged. Your subscription has not changed. Please refresh the page and try again. #{token.error.message}")
9
+ form.removeClass('stripe-success')
10
+ form.find('.effective-orders-stripe-plans').find('.invalid-feedback').html(message).show()
11
+ alert(message)
13
12
  else
14
13
  form.find("input[name$='[stripe_token]']").val('' + token['id'])
14
+ form.addClass('stripe-success').submit()
15
15
 
16
- form.find("input[type='submit']").prop('disabled', true)
17
- $('input[data-disable-with]').each -> try $.rails.disableFormElement($(this))
18
- form.submit()
19
-
20
- $(document).on 'click', "input[type='submit'].effective-orders-subscripter-token-required", (event) ->
16
+ # When I submit a for that needs a subscripter token, do the stripe thing.
17
+ $(document).on 'click', ".effective-orders-subscripter-token-required[type='submit']", (event) ->
21
18
  event.preventDefault()
22
-
23
- $submit = $(event.currentTarget)
24
- $form = $submit.closest('form')
25
-
26
- # Disable the form
27
- $submit.prop('disabled', true)
28
- $('input[data-disable-with]').each -> try $.rails.disableFormElement($(this))
19
+ $form = $(event.currentTarget).closest('form')
29
20
 
30
21
  # Get the stripe data
31
22
  $plans = $form.find('.effective-orders-stripe-plans').first()
32
23
  selected_plan_id = $plans.find("input[name$='[subscripter][stripe_plan_id]']:checked").val()
24
+ return unless $plans.length > 0 && selected_plan_id.length > 0
25
+
26
+ EffectiveBootstrap.submitting($form)
33
27
 
34
28
  stripe = $plans.data('stripe')
35
29
  plan = stripe.plans.find (plan, _) => plan.id == selected_plan_id
@@ -42,32 +36,21 @@ $(document).on 'click', "input[type='submit'].effective-orders-subscripter-token
42
36
  amount: plan.amount
43
37
  panelLabel: "{{amount}}#{plan.occurrence} Go!"
44
38
 
45
- # When a plan is selected, toggle the selected-class on each plan.
46
- # Set the submit button's class if a customer token is required
39
+ # When I click on a stripe plan ID radio button, add .effective-orders-subscripter-token-required to the form if required
47
40
  $(document).on 'change', "input[name$='[subscripter][stripe_plan_id]']", (event) ->
48
41
  $plan = $(event.currentTarget)
49
42
  return unless $plan.is(':checked')
50
43
 
51
- $plans = $plan.closest('.effective-orders-stripe-plans').first()
52
- selected_class = $plans.data('selected-class')
53
44
  selected_plan_id = $plan.val()
54
45
 
55
- $plans.find("input[name$='[subscripter][stripe_plan_id]']").each (_, item) =>
56
- if $(item).is(':checked')
57
- $(item).siblings('.panel').addClass(selected_class)
58
- else
59
- $(item).siblings('.panel').removeClass(selected_class)
60
-
46
+ $plans = $plan.closest('.effective-orders-stripe-plans').first()
61
47
  plan = $plans.data('stripe').plans.find (plan, _) => plan.id == selected_plan_id
48
+
62
49
  token_required = $plans.data('stripe').token_required
63
50
 
64
51
  if (plan.amount || 0) > 0 && token_required
65
- $plans.closest('form').find("input[type='submit']").addClass('effective-orders-subscripter-token-required')
52
+ $plans.closest('form').find("input[type='submit'],button[type='submit']").addClass('effective-orders-subscripter-token-required')
66
53
  else
67
- $plans.closest('form').find("input[type='submit']").removeClass('effective-orders-subscripter-token-required')
54
+ $plans.closest('form').find("input[type='submit'],button[type='submit']").removeClass('effective-orders-subscripter-token-required')
68
55
 
69
- # When the 'Select' button is clicked, set the radio button input
70
- $(document).on 'click', '.effective-orders-stripe-plan .btn-select-plan', (event) ->
71
- val = $(event.currentTarget).closest('.effective-orders-stripe-plan').find('input:radio').val()
72
- $(event.currentTarget).closest('.effective-orders-stripe-plans').find('input:radio').val([val]).trigger('change')
73
- false
56
+ true
@@ -1,3 +1,2 @@
1
1
  @import 'effective_orders/cart';
2
2
  @import 'effective_orders/order';
3
- @import 'effective_orders/subscriptions';
@@ -188,7 +188,7 @@ module Admin
188
188
  :payment_provider, :payment_card, :payment, :send_mark_as_paid_email_to_buyer,
189
189
  order_items_attributes: [
190
190
  :quantity, :_destroy, purchasable_attributes: [
191
- :title, :price, :tax_exempt
191
+ :name, :price, :tax_exempt
192
192
  ]
193
193
  ]
194
194
  )
@@ -37,8 +37,8 @@ class EffectiveOrderItemsDatatable < Effective::Datatable
37
37
  order_item[:state] || 'abandoned'
38
38
  end
39
39
 
40
- col :title do |order_item|
41
- order_item.quantity == 1 ? order_item.title : "#{order_item.title} (#{order_item.quantity} purchased)"
40
+ col :name do |order_item|
41
+ order_item.quantity == 1 ? order_item.name : "#{order_item.name} (#{order_item.quantity} purchased)"
42
42
  end
43
43
 
44
44
  # col :subtotal, as: :price
@@ -14,9 +14,9 @@ module EffectiveOrdersHelper
14
14
  order_item_list = content_tag(:ul) do
15
15
  order.order_items.map do |item|
16
16
  content_tag(:li) do
17
- title = item.title.split('<br>')
18
- "#{item.quantity}x #{title.first} for #{price_to_currency(item.price)}".tap do |output|
19
- title[1..-1].each { |line| output << "<br>#{line}" }
17
+ names = item.name.split('<br>')
18
+ "#{item.quantity}x #{names.first} for #{price_to_currency(item.price)}".tap do |output|
19
+ names[1..-1].each { |line| output << "<br>#{line}" }
20
20
  end.html_safe
21
21
  end
22
22
  end.join.html_safe
@@ -37,7 +37,7 @@ module EffectivePaypalHelper
37
37
 
38
38
  order.order_items.each_with_index do |item, x|
39
39
  values["item_number_#{x+1}"] = x+1
40
- values["item_name_#{x+1}"] = item.title
40
+ values["item_name_#{x+1}"] = item.name
41
41
  values["quantity_#{x+1}"] = item.quantity
42
42
  values["amount_#{x+1}"] = '%.2f' % (item.price / 100.0)
43
43
  values["tax_#{x+1}"] = '%.2f' % ((item.tax / 100.0) / item.quantity) # Tax for 1 of these items
@@ -1,7 +1,7 @@
1
1
  module EffectiveSubscriptionsHelper
2
2
 
3
3
  def effective_customer_fields(form, submit: true)
4
- raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
4
+ raise 'expected an Effective::FormBuilder object' unless form.class.name == 'Effective::FormBuilder'
5
5
  raise 'form object must be an Effective::Subscripter object' unless form.object.class.name == 'Effective::Subscripter'
6
6
 
7
7
  render(
@@ -19,11 +19,11 @@ module EffectiveSubscriptionsHelper
19
19
  )
20
20
  end
21
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'
22
+ def stripe_plans_collection(form, include_trial: nil)
23
+ raise 'expected an Effective::FormBuilder object' unless form.class.name == 'Effective::FormBuilder'
24
24
  raise 'form object must be an acts_as_subscribable object' unless form.object.subscribable.subscripter.present?
25
25
 
26
- include_trial = form.object.subscribable.subscribed?('trial') if include_trial.nil?
26
+ include_trial = form.object.subscribable.trialing? if include_trial.nil?
27
27
 
28
28
  plans = include_trial ? EffectiveOrders.stripe_plans : EffectiveOrders.stripe_plans.except('trial')
29
29
  plans = plans.values.sort { |x, y| (amount = x[:amount] <=> y[:amount]) != 0 ? amount : x[:name] <=> y[:name] }
@@ -41,7 +41,6 @@ module EffectiveSubscriptionsHelper
41
41
  f: form,
42
42
  plan: plan,
43
43
  selected: Array(form.object.stripe_plan_id).include?(plan[:id]),
44
- selected_class: selected_class,
45
44
  subscribable: form.object.subscribable,
46
45
  subscribed: form.object.subscribable.subscribed?(plan[:id])
47
46
  })
@@ -50,8 +49,8 @@ module EffectiveSubscriptionsHelper
50
49
  end
51
50
  end
52
51
 
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'
52
+ def effective_subscription_fields(form, label: false, required: true, include_trial: nil)
53
+ raise 'expected an Effective::FormBuilder object' unless form.class.name == 'Effective::FormBuilder'
55
54
  raise 'form object must be an acts_as_subscribable object' unless form.object.subscripter.present?
56
55
 
57
56
  render(
@@ -61,17 +60,14 @@ module EffectiveSubscriptionsHelper
61
60
  label: label,
62
61
  required: required,
63
62
  include_trial: include_trial,
64
- item_wrapper_class: item_wrapper_class,
65
- selected_class: selected_class,
66
63
  stripe: {
67
- email: form.object.buyer.email,
64
+ email: form.object.subscribable_buyer.email,
68
65
  image: stripe_site_image_url,
69
66
  key: EffectiveOrders.stripe[:publishable_key],
70
67
  name: EffectiveOrders.stripe[:site_title],
71
68
  plans: EffectiveOrders.stripe_plans.values,
72
69
  token_required: form.object.subscripter.token_required?
73
70
  },
74
- wrapper_class: wrapper_class
75
71
  }
76
72
  )
77
73
  end
@@ -116,7 +116,7 @@ module Effective
116
116
  @subscribable = subscribable
117
117
 
118
118
  mail(
119
- to: @subscribable.buyer.email,
119
+ to: @subscribable.subscribable_buyer.email,
120
120
  from: EffectiveOrders.mailer[:default_from],
121
121
  subject: subject_for_subscription_trial_expiring(@subscribable)
122
122
  )
@@ -129,7 +129,7 @@ module Effective
129
129
  @subscribable = subscribable
130
130
 
131
131
  mail(
132
- to: @subscribable.buyer.email,
132
+ to: @subscribable.subscribable_buyer.email,
133
133
  from: EffectiveOrders.mailer[:default_from],
134
134
  subject: subject_for_subscription_trial_expired(@subscribable)
135
135
  )
@@ -1,8 +1,6 @@
1
1
  module ActsAsPurchasable
2
2
  extend ActiveSupport::Concern
3
3
 
4
- mattr_accessor :descendants
5
-
6
4
  module ActiveRecord
7
5
  def acts_as_purchasable(*options)
8
6
  @acts_as_purchasable = options || []
@@ -20,7 +18,7 @@ module ActsAsPurchasable
20
18
  end
21
19
 
22
20
  included do
23
- belongs_to :purchased_order, class_name: 'Effective::Order' # Set when purchased
21
+ belongs_to :purchased_order, class_name: 'Effective::Order', optional: true # Set when purchased
24
22
 
25
23
  has_many :cart_items, as: :purchasable, dependent: :delete_all, class_name: 'Effective::CartItem'
26
24
 
@@ -76,8 +74,8 @@ module ActsAsPurchasable
76
74
  end
77
75
  end
78
76
 
79
- def title
80
- self[:title] || to_s
77
+ def purchasable_name
78
+ to_s
81
79
  end
82
80
 
83
81
  def tax_exempt
@@ -113,4 +111,3 @@ module ActsAsPurchasable
113
111
  end
114
112
 
115
113
  end
116
-
@@ -6,6 +6,11 @@ module ActsAsSubscribable
6
6
  module ActiveRecord
7
7
  def acts_as_subscribable(*options)
8
8
  @acts_as_subscribable = options || []
9
+
10
+ instance = new()
11
+ raise 'must respond to trialing_until' unless instance.respond_to?(:trialing_until)
12
+ raise 'must respond to subscription_status' unless instance.respond_to?(:subscription_status)
13
+
9
14
  include ::ActsAsSubscribable
10
15
  (ActsAsSubscribable.descendants ||= []) << self
11
16
  end
@@ -15,17 +20,29 @@ module ActsAsSubscribable
15
20
  has_one :subscription, as: :subscribable, class_name: 'Effective::Subscription'
16
21
  has_one :customer, through: :subscription, class_name: 'Effective::Customer'
17
22
 
23
+ before_validation(if: -> { trialing_until.blank? && EffectiveOrders.trial? }) do
24
+ self.trialing_until = (Time.zone.now + EffectiveOrders.trial.fetch(:length)).end_of_day
25
+ end
26
+
27
+ validates :trialing_until, presence: true, if: -> { EffectiveOrders.trial? }
28
+ validates :subscription_status, inclusion: { allow_nil: true, in: EffectiveOrders::STATUSES.keys }
29
+
18
30
  validates :subscripter, associated: true
19
31
 
20
- scope :subscribed, -> { where(id: joins(:subscription)) } # All resources with a subscription
21
- scope :trialing, -> { where.not(id: joins(:subscription)) } # All resources without a subscription
32
+ scope :trialing, -> { where(subscription_status: nil).where('trialing_until > ?', Time.zone.now) }
33
+ scope :trial_past_due, -> { where(subscription_status: nil).where('trialing_until < ?', Time.zone.now) }
34
+ scope :not_trialing, -> { where.not(subscription_status: nil) }
35
+
36
+ scope :subscribed, -> { where(subscription_status: EffectiveOrders::ACTIVE) }
37
+ scope :subscription_past_due, -> { where(subscription_status: EffectiveOrders::PAST_DUE) }
38
+ scope :not_subscribed, -> { where(subscription_status: nil) }
22
39
  end
23
40
 
24
41
  module ClassMethods
25
42
  end
26
43
 
27
44
  def subscripter
28
- @_effective_subscripter ||= Effective::Subscripter.new(subscribable: self, user: buyer)
45
+ @_effective_subscripter ||= Effective::Subscripter.new(subscribable: self, user: subscribable_buyer)
29
46
  end
30
47
 
31
48
  def subscripter=(atts)
@@ -33,35 +50,32 @@ module ActsAsSubscribable
33
50
  end
34
51
 
35
52
  def subscribed?(stripe_plan_id = nil)
36
- case stripe_plan_id
37
- when nil
38
- subscription.present? # Subscribed to any subscription?
39
- when (EffectiveOrders.stripe_plans['trial'] || {})[:id]
40
- subscription.blank? || subscription.new_record? || subscription.stripe_plan_id == stripe_plan_id
41
- else
42
- subscription && subscription.persisted? && subscription.errors.blank? && subscription.stripe_plan_id == stripe_plan_id
43
- end
53
+ return false if subscription_status.blank?
54
+ stripe_plan_id ? (subscription&.stripe_plan_id == stripe_plan_id) : true
44
55
  end
45
56
 
46
57
  def subscription_active?
47
- (trialing? && !trial_expired?) || (subscribed? && subscription.active?)
58
+ subscribed? && subscription_status == EffectiveOrders::ACTIVE
59
+ end
60
+
61
+ def subscription_past_due?
62
+ subscribed? && subscription_status == EffectiveOrders::PAST_DUE
48
63
  end
49
64
 
50
65
  def trialing?
51
- !subscribed?
66
+ subscription_status.blank?
52
67
  end
53
68
 
54
- def trial_expired?
55
- trialing? && Time.zone.now > trial_expires_at
69
+ def trial_active?
70
+ trialing? && trialing_until > Time.zone.now
56
71
  end
57
72
 
58
- def trial_expires_at
59
- # The rake task send_trial_expiring_emails depends on this beginning_of_day
60
- ((created_at || Time.zone.now) + EffectiveOrders.subscriptions[:trial_period]).beginning_of_day
73
+ def trial_past_due?
74
+ trialing? && trialing_until < Time.zone.now
61
75
  end
62
76
 
63
- def buyer
64
- raise 'acts_as_subscribable object requires the buyer be defined to return the User buying this item.'
77
+ def subscribable_buyer
78
+ raise 'acts_as_subscribable object requires the subscribable_buyer method be defined to return the User buying this item.'
65
79
  end
66
80
 
67
81
  end
@@ -39,7 +39,7 @@ module Effective
39
39
  end
40
40
 
41
41
  if item.quantity_enabled? && (existing ? existing.quantity : quantity) > item.quantity_remaining
42
- raise EffectiveOrders::SoldOutException, "#{item.title} is sold out"
42
+ raise EffectiveOrders::SoldOutException, "#{item.purchasable_name} is sold out"
43
43
  end
44
44
 
45
45
  existing ||= cart_items.build(purchasable: item, quantity: quantity, unique: (unique.to_s unless unique.kind_of?(Proc)))
@@ -12,6 +12,14 @@ module Effective
12
12
  validates :purchasable, presence: true
13
13
  validates :quantity, presence: true
14
14
 
15
+ def to_s
16
+ name || 'New Cart Item'
17
+ end
18
+
19
+ def name
20
+ purchasable&.purchasable_name
21
+ end
22
+
15
23
  def price
16
24
  if (purchasable.price || 0).kind_of?(Integer)
17
25
  purchasable.price || 0
@@ -20,12 +28,8 @@ module Effective
20
28
  end
21
29
  end
22
30
 
23
- def title
24
- purchasable.try(:title) || 'New Cart Item'
25
- end
26
-
27
31
  def tax_exempt
28
- purchasable.try(:tax_exempt) || false
32
+ purchasable&.tax_exempt || false
29
33
  end
30
34
 
31
35
  def subtotal
@@ -5,10 +5,12 @@ module Effective
5
5
  attr_accessor :stripe_customer, :stripe_subscription
6
6
 
7
7
  belongs_to :user
8
+ has_many :subscribables, as: :subscribable
9
+
8
10
  has_many :subscriptions, class_name: 'Effective::Subscription', foreign_key: 'customer_id'
9
11
  has_many :subscribables, through: :subscriptions, source: :subscribable
10
12
 
11
- accepts_nested_attributes_for :subscriptions
13
+ #accepts_nested_attributes_for :subscriptions
12
14
 
13
15
  # Attributes
14
16
  # stripe_customer_id :string # cus_xja7acoa03
@@ -27,7 +29,7 @@ module Effective
27
29
 
28
30
  validates :user, presence: true
29
31
  validates :stripe_customer_id, presence: true
30
- validates :status, if: -> { stripe_subscription_id.present? }, inclusion: { in: %w(active past_due) }
32
+ validates :status, if: -> { stripe_subscription_id.present? }, inclusion: { in: EffectiveOrders::STATUSES.keys }
31
33
 
32
34
  def self.for_user(user)
33
35
  Effective::Customer.where(user: user).first_or_initialize
@@ -179,7 +179,7 @@ module Effective
179
179
  self.note_internal ||= item.note_internal
180
180
 
181
181
  item.order_items.select { |oi| oi.purchasable.kind_of?(Effective::Product) }.map do |oi|
182
- product = Effective::Product.new(title: oi.purchasable.title, price: oi.purchasable.price, tax_exempt: oi.purchasable.tax_exempt)
182
+ product = Effective::Product.new(name: oi.purchasable.purchasable_name, price: oi.purchasable.price, tax_exempt: oi.purchasable.tax_exempt)
183
183
  Effective::CartItem.new(quantity: oi.quantity, purchasable: product)
184
184
  end
185
185
  else
@@ -194,7 +194,7 @@ module Effective
194
194
 
195
195
  retval = cart_items.map do |item|
196
196
  order_items.build(
197
- title: item.title,
197
+ name: item.name,
198
198
  quantity: item.quantity,
199
199
  price: item.price,
200
200
  tax_exempt: (item.tax_exempt || false),
@@ -9,7 +9,7 @@ module Effective
9
9
  delegate :purchased?, :declined?, to: :order
10
10
 
11
11
  # Attributes
12
- # title :string
12
+ # name :string
13
13
  # quantity :integer
14
14
  # price :integer, default: 0
15
15
  # tax_exempt :boolean
@@ -18,7 +18,7 @@ module Effective
18
18
  validates :purchasable, associated: true, presence: true
19
19
  accepts_nested_attributes_for :purchasable
20
20
 
21
- validates :title, presence: true
21
+ validates :name, presence: true
22
22
  validates :quantity, presence: true, numericality: { greater_than: 0 }
23
23
  validates :price, presence: true
24
24
  validates :tax_exempt, inclusion: { in: [true, false] }
@@ -27,7 +27,7 @@ module Effective
27
27
  scope :sold_by, lambda { |user| sold().where(seller_id: user.id) }
28
28
 
29
29
  def to_s
30
- (quantity || 0) > 1 ? "#{quantity}x #{title}" : title
30
+ (quantity || 0) > 1 ? "#{quantity}x #{name}" : name
31
31
  end
32
32
 
33
33
  def subtotal
@@ -5,18 +5,18 @@ module Effective
5
5
  acts_as_purchasable
6
6
 
7
7
  # Attributes
8
- # title :string
8
+ # name :string
9
9
  # price :integer, default: 0
10
10
  # tax_exempt :boolean, default: false
11
11
  #
12
12
  # timestamps
13
13
 
14
- validates :title, presence: true
14
+ validates :name, presence: true
15
15
  validates :price, presence: true
16
16
  validates :tax_exempt, inclusion: { in: [true, false] }
17
17
 
18
18
  def to_s
19
- self[:title] || 'New Product'
19
+ name || 'New Product'
20
20
  end
21
21
 
22
22
  end
@@ -184,10 +184,10 @@ module Effective
184
184
  end
185
185
 
186
186
  # When upgrading a plan, invoice immediately.
187
- if current_plan && current_plan[:id] != 'trial' && plan[:amount] > current_plan[:amount]
188
- Rails.logger.info " -> INVOICE GENERATED"
189
- Stripe::Invoice.create(customer: customer.stripe_customer_id).pay rescue false
190
- end
187
+ # if current_plan && current_plan[:id] != 'trial' && plan[:amount] > current_plan[:amount]
188
+ # Rails.logger.info " -> INVOICE GENERATED"
189
+ # Stripe::Invoice.create(customer: customer.stripe_customer_id).pay rescue false
190
+ # end
191
191
 
192
192
  # Sync status
193
193
  customer.status = customer.stripe_subscription.status
@@ -8,42 +8,31 @@ module Effective
8
8
  belongs_to :subscribable, polymorphic: true
9
9
 
10
10
  # Attributes
11
- # stripe_plan_id :string # This will be 'bronze' or something like that
12
- # status :string
13
- #
11
+ # stripe_plan_id :string
14
12
  # name :string
15
- # price :integer, default: 0
16
13
  #
17
14
  # timestamps
18
15
 
19
16
  before_validation(if: -> { plan && (stripe_plan_id_changed? || new_record?) }) do
20
17
  self.name = "#{plan[:name]} #{plan[:description]}"
21
- self.price = plan[:amount]
22
18
  end
23
19
 
24
20
  validates :customer, presence: true
25
21
  validates :subscribable, presence: true
26
22
 
27
23
  validates :stripe_plan_id, presence: true, inclusion: { in: EffectiveOrders.stripe_plans.except('trial').keys }
28
- validates :status, presence: true, inclusion: { in: %w(active past_due) }
29
-
30
24
  validates :name, presence: true
31
- validates :price, numericality: { greater_than_or_equal_to: 0, only_integer: true }
32
25
 
33
26
  def to_s
34
- name.presence || 'New Subscription'
27
+ name || 'New Subscription'
35
28
  end
36
29
 
37
30
  def plan
38
31
  EffectiveOrders.stripe_plans[stripe_plan_id]
39
32
  end
40
33
 
41
- def active?
42
- status == 'active'
43
- end
44
-
45
34
  def <=>(other)
46
- (name || '') <=> (other.try(:name) || '')
35
+ name.to_s <=> other&.name.to_s
47
36
  end
48
37
 
49
38
  end
@@ -2,7 +2,7 @@
2
2
  .row.align-items-center
3
3
  = f.fields_for :purchasable, (f.object.purchasable || Effective::Product.new) do |pf|
4
4
  .col-md-1= f.number_field :quantity, input_html: { value: f.object.quantity || 1, min: 1 }
5
- .col-md-4= pf.text_field :title, maxlength: 255
5
+ .col-md-4= pf.text_field :name, maxlength: 255
6
6
  .col-md-2= pf.price_field :price
7
7
 
8
8
  - if defined?(EffectiveQbSync)
@@ -11,7 +11,7 @@
11
11
  %td.quantity
12
12
  = item.quantity
13
13
  %td.item
14
- = item.title.html_safe
14
+ = item.name.html_safe
15
15
  = ' - '
16
16
  = link_to_remove_from_cart(item)
17
17
  %td.price
@@ -76,7 +76,7 @@
76
76
  - if customer.stripe_subscription.items.present?
77
77
  %tr
78
78
  %th Plans
79
- %td= tableize_hash(customer.stripe_subscription.items.inject({}) { |h, item| h[item.plan.name] = item.quantity; h }, th: false)
79
+ %td= tableize_hash(customer.stripe_subscription.items.inject({}) { |h, item| h[item.plan.nickname] = item.quantity; h }, th: false)
80
80
 
81
81
  - if customer.subscriptions.present?
82
82
  %table.table
@@ -118,7 +118,7 @@
118
118
  = Time.zone.at(invoice.lines.first.period.end).strftime('%F')
119
119
  %td
120
120
  - invoice.lines.each do |line|
121
- %p #{line.quantity}x #{line.plan.try(:name)} #{line.description}
121
+ %p= line.description
122
122
 
123
123
  %td= price_to_currency(invoice.total)
124
124
 
@@ -130,12 +130,6 @@
130
130
  %tr
131
131
  %th Date
132
132
  %td= Time.zone.at(customer.upcoming_invoice.date).strftime('%F')
133
- %tr
134
- %th Period
135
- %td
136
- = Time.zone.at(customer.upcoming_invoice.period_start).strftime('%F')
137
- to
138
- = Time.zone.at(customer.upcoming_invoice.period_end).strftime('%F')
139
133
  %tr
140
134
  %th Items
141
135
  %td
@@ -143,7 +137,6 @@
143
137
  %tbody
144
138
  - customer.upcoming_invoice.lines.each do |line|
145
139
  %tr
146
- %td #{line.quantity}x #{line.plan.name}
147
140
  %td #{Time.zone.at(line.period.start).strftime('%F')} to #{Time.zone.at(line.period.end).strftime('%F')}
148
141
  %td= line.description
149
142
  %td= price_to_currency(line.amount)
@@ -1,12 +1,9 @@
1
1
  .effective-orders-customer{ data: { stripe: stripe.to_json, submit: submit.to_json } }
2
- - if f.object.token_required?
3
- = f.input :stripe_token, as: :effective_static_control, label: 'Credit Card', required: true,
4
- value: f.object.customer.payment_status + ' ' + content_tag(:span, '', class: 'active-card')
2
+ .card
3
+ .card-header Credit Card
4
+ .card-body
5
+ = f.static_field :stripe_token, label: false, class: 'payment-status', value: f.object.customer.payment_status
5
6
 
6
- %p= link_to 'Update Card Details', '#', class: 'btn btn-primary btn-change-card'
7
+ %p= link_to 'Update Card Details', '#', class: 'btn btn-primary btn-change-card'
7
8
 
8
- - else
9
- = f.input :stripe_token, as: :effective_static_control, label: 'Credit Card', required: false,
10
- value: f.object.customer.payment_status + ' ' + content_tag(:span, f.object.customer.active_card, class: 'active-card')
11
-
12
- %p= link_to 'Update Card Details', '#', class: 'btn btn-default btn-change-card'
9
+ %p.invalid-feedback
@@ -1,13 +1,12 @@
1
1
  = javascript_include_tag 'https://checkout.stripe.com/checkout.js'
2
2
 
3
- = simple_form_for(subscripter, (EffectiveOrders.simple_form_options || {}).merge(url: effective_orders.customer_settings_path, method: :put)) do |f|
4
- = f.input :stripe_token, as: :hidden, input_html: { value: nil }
3
+ = effective_form_with(model: subscripter, url: effective_orders.customer_settings_path, method: :put) do |f|
4
+ = f.hidden_field :stripe_token, value: nil
5
5
 
6
6
  = render(f.object.customer) do
7
7
  %h3.effective-heading Card
8
8
  = effective_customer_fields(f)
9
9
 
10
- .form-actions
11
- = f.button :submit, 'Save', data: { disable_with: 'Saving...' }
10
+ = f.submit
12
11
 
13
12
 
@@ -24,7 +24,7 @@
24
24
  - else
25
25
  = '-'
26
26
  %td.item
27
- = item.title.html_safe
27
+ = item.name.html_safe
28
28
 
29
29
  %td.price= price_to_currency(item.subtotal)
30
30
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  - order.order_items.each_with_index do |item, x|
21
21
  = hidden_field_tag("id#{x}", x)
22
- = hidden_field_tag("description#{x}", item.title)
22
+ = hidden_field_tag("description#{x}", item.name)
23
23
  = hidden_field_tag("quantity#{x}", item.quantity)
24
24
  = hidden_field_tag("price#{x}", '%.2f' % (item.price / 100.0))
25
25
  = hidden_field_tag("subtotal#{x}", '%.2f' % (item.subtotal / 100.0))
@@ -1,16 +1,12 @@
1
1
  = javascript_include_tag 'https://checkout.stripe.com/checkout.js'
2
2
 
3
- = form.simple_fields_for :subscripter, form.object.subscripter do |fs|
4
- = fs.input :stripe_token, as: :hidden, input_html: { value: nil }
3
+ = form.fields_for :subscripter, form.object.subscripter do |fs|
4
+ = fs.hidden_field :stripe_token, value: nil
5
5
 
6
6
  - fs.object.stripe_plan_id ||= (fs.object.current_plan || {})[:id]
7
7
 
8
- = fs.input :stripe_plan_id, as: :radio_buttons, collection: stripe_plans_collection(fs, include_trial: include_trial, selected_class: selected_class),
8
+ = fs.radios :stripe_plan_id, stripe_plans_collection(fs, include_trial: include_trial),
9
9
  label: label,
10
10
  required: required,
11
- wrapper_html: { class: "effective-orders-stripe-plans #{wrapper_class}", data: { selected_class: selected_class, stripe: stripe } },
12
- item_wrapper_class: "effective-orders-stripe-plan #{item_wrapper_class}",
13
- item_wrapper_tag: 'div'
14
-
15
- - if fs.object.customer.persisted?
16
- = effective_customer_fields(fs)
11
+ cards: true,
12
+ wrapper: { class: 'effective-orders-stripe-plans', data: { stripe: stripe } }
@@ -1,21 +1,20 @@
1
- .panel.panel-default{class: (selected_class if selected)}
2
- .panel-heading.text-center
3
- %h3= plan[:name]
4
- %p= plan[:description]
5
- .panel-body
6
- %p Includes features
1
+ .card-header.text-center
2
+ %h3= plan[:name]
3
+ %p= plan[:description]
7
4
 
8
- - if plan[:amount] == 0
9
- %p Free plan
5
+ .card-body
6
+ %p Includes features
10
7
 
11
- %hr
12
- .visible-when-unselected.text-center
13
- = link_to 'Select', '#', class: 'btn btn-default btn-select-plan'
8
+ - if plan[:amount] == 0
9
+ %p Free plan
14
10
 
15
- .visible-when-selected.text-center
16
- - if subscribed
17
- %p
18
- %strong Your current plan
19
- - else
20
- %p This is #{['an amazing', 'a great', 'an excellent', 'an exceptional', 'a marvelous', 'a wonderful'].sample} plan
11
+ .visible-when-unselected.text-center
12
+ = link_to 'Select', '#', class: 'btn btn-secondary', 'data-toggle': 'card'
13
+
14
+ .visible-when-selected.text-center
15
+ - if subscribed
16
+ %p
17
+ %strong Your current plan
18
+ - else
19
+ %p This is #{['an amazing', 'a great', 'an excellent', 'an exceptional', 'a marvelous', 'a wonderful'].sample} plan
21
20
 
@@ -225,19 +225,19 @@ EffectiveOrders.setup do |config|
225
225
 
226
226
  # if Rails.env.production?
227
227
  # config.stripe = {
228
- # secret_key: '',
229
- # publishable_key: '',
228
+ # secret_key: 'sk_xxx',
229
+ # publishable_key: 'pk_xxx',
230
230
  # currency: 'usd',
231
231
  # site_title: 'My Site',
232
- # site_image: '' # A relative URL pointing to a square image of your brand or product. The recommended minimum size is 128x128px.
232
+ # site_image: 'logo.png' # A relative or absolute URL pointing to a square image of your brand or product. The recommended minimum size is 128x128px.
233
233
  # }
234
234
  # else
235
235
  # config.stripe = {
236
- # secret_key: '',
237
- # publishable_key: '',
236
+ # secret_key: 'sk_test_xxx',
237
+ # publishable_key: 'pk_test_xxx',
238
238
  # currency: 'usd',
239
- # site_title: 'My Development Site', # Displayed on the Embedded Stripe Form
240
- # site_image: '' # A relative URL pointing to a square image of your brand or product. The recommended minimum size is 128x128px.
239
+ # site_title: 'My Site',
240
+ # site_image: 'logo.png' # A relative or absolute URL pointing to a square image of your brand or product. The recommended minimum size is 128x128px.
241
241
  # }
242
242
  # end
243
243
 
@@ -245,11 +245,17 @@ EffectiveOrders.setup do |config|
245
245
  config.subscriptions = false
246
246
 
247
247
  # config.subscriptions = {
248
- # trial_name: 'Free Trial',
249
- # trial_description: '45-Day Free Trial',
250
- # trial_period: 45.days,
251
- # trial_remind_at: [1.day, 3.days, 7.days], # Send email notification to trialing users 1, 3 and 7 days before expiring. false to disable.
252
- # webhook_secret: 'whsec_1234567890'
248
+ # webhook_secret: 'whsec_xxx'
249
+ # }
250
+
251
+ # Trial
252
+ config.trial = false
253
+
254
+ # config.trial = {
255
+ # name: 'Free Trial',
256
+ # description: '45-Day Free Trial',
257
+ # length: 45.days,
258
+ # remind_at: [1.day, 3.days, 7.days], # Send email notification to trialing users 1, 3 and 7 days before expiring. false to disable.
253
259
  # }
254
260
 
255
261
  end
@@ -32,7 +32,7 @@ class CreateEffectiveOrders < ActiveRecord::Migration[4.2]
32
32
  t.string :purchasable_type
33
33
  t.integer :purchasable_id
34
34
 
35
- t.string :title
35
+ t.string :name
36
36
  t.integer :quantity
37
37
  t.integer :price, :default => 0
38
38
  t.boolean :tax_exempt
@@ -91,10 +91,7 @@ class CreateEffectiveOrders < ActiveRecord::Migration[4.2]
91
91
  t.string :subscribable_type
92
92
 
93
93
  t.string :stripe_plan_id
94
- t.string :status
95
-
96
94
  t.string :name
97
- t.integer :price, :default => 0
98
95
 
99
96
  t.timestamps
100
97
  end
@@ -106,7 +103,7 @@ class CreateEffectiveOrders < ActiveRecord::Migration[4.2]
106
103
  create_table <%= @products_table_name %> do |t|
107
104
  t.integer :purchased_order_id
108
105
 
109
- t.string :title
106
+ t.string :name
110
107
  t.integer :price
111
108
  t.boolean :tax_exempt, :default => false
112
109
 
@@ -10,6 +10,12 @@ module EffectiveOrders
10
10
 
11
11
  STATES = { PENDING => PENDING, CONFIRMED => CONFIRMED, PURCHASED => PURCHASED, DECLINED => DECLINED }
12
12
 
13
+ # Subscription statuses (as per stripe)
14
+ ACTIVE = 'active'.freeze
15
+ PAST_DUE = 'past_due'.freeze
16
+
17
+ STATUSES = { ACTIVE => ACTIVE, PAST_DUE => PAST_DUE }
18
+
13
19
  # The following are all valid config keys
14
20
  mattr_accessor :orders_table_name
15
21
  mattr_accessor :order_items_table_name
@@ -57,6 +63,7 @@ module EffectiveOrders
57
63
  mattr_accessor :paypal
58
64
  mattr_accessor :stripe
59
65
  mattr_accessor :subscriptions # Stripe subscriptions
66
+ mattr_accessor :trial # Trial mode
60
67
 
61
68
  def self.setup
62
69
  yield self
@@ -125,6 +132,10 @@ module EffectiveOrders
125
132
  subscriptions.kind_of?(Hash)
126
133
  end
127
134
 
135
+ def self.trial?
136
+ trial.kind_of?(Hash)
137
+ end
138
+
128
139
  def self.single_payment_processor?
129
140
  [cheque?, moneris?, paypal?, stripe?].select { |enabled| enabled }.length == 1
130
141
  end
@@ -157,6 +168,12 @@ module EffectiveOrders
157
168
  return {} unless (stripe? && subscriptions?)
158
169
 
159
170
  @stripe_plans ||= (
171
+ Rails.logger.info "STRIPE PLAN LIST"
172
+
173
+ # {"id":"plan_ChLx90ggbWvB8i","object":"plan","amount":100000,"billing_scheme":"per_unit","created":1523984756,"currency":"usd",
174
+ #{ }"interval":"year","interval_count":1,"livemode":false,"metadata":{},"nickname":"Yearly","product":"prod_ChLwW0XqdykIki",
175
+ #{ }"tiers":null,"tiers_mode":null,"transform_usage":null,"trial_period_days":null,"usage_type":"licensed"},
176
+
160
177
  plans = Stripe::Plan.all.inject({}) do |h, plan|
161
178
  occurrence = case plan.interval
162
179
  when 'daily' ; '/day'
@@ -172,7 +189,8 @@ module EffectiveOrders
172
189
 
173
190
  h[plan.id] = {
174
191
  id: plan.id,
175
- name: plan.name,
192
+ product_id: plan.product,
193
+ name: plan.nickname,
176
194
  amount: plan.amount,
177
195
  currency: plan.currency,
178
196
  description: "$#{'%0.2f' % (plan.amount / 100.0)} #{plan.currency.upcase}#{occurrence}",
@@ -182,12 +200,9 @@ module EffectiveOrders
182
200
  }; h
183
201
  end
184
202
 
185
- plans['trial'] = {
186
- id: 'trial',
187
- amount: 0,
188
- name: (subscriptions[:trial_name] || 'Free Trial'),
189
- description: (subscriptions[:trial_description] || 'Free Trial')
190
- }
203
+ if trial?
204
+ plans['trial'] = { id: 'trial', amount: 0, name: trial.fetch(:name), description: trial.fetch(:description) }
205
+ end
191
206
 
192
207
  plans
193
208
  )
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrders
2
- VERSION = '4.0.0beta4'.freeze
2
+ VERSION = '4.0.0beta5'.freeze
3
3
  end
@@ -68,10 +68,10 @@ class EffectiveOrdersMailerPreview < ActionMailer::Preview
68
68
  # so that this mailer will not have to guess at your app's acts_as_purchasable objects
69
69
  def preview_order_items
70
70
  [
71
- { title: 'Item One', quantity: 2, price: 999, tax_exempt: false },
72
- { title: 'Item Two', quantity: 1, price: 25000, tax_exempt: false },
73
- { title: 'Item Three', quantity: 1, price: 8999, tax_exempt: false },
74
- { title: 'Item Four', quantity: 1, price: 100000, tax_exempt: false }
71
+ { name: 'Item One', quantity: 2, price: 999, tax_exempt: false },
72
+ { name: 'Item Two', quantity: 1, price: 25000, tax_exempt: false },
73
+ { name: 'Item Three', quantity: 1, price: 8999, tax_exempt: false },
74
+ { name: 'Item Four', quantity: 1, price: 100000, tax_exempt: false }
75
75
  ]
76
76
  end
77
77
 
@@ -1,28 +1,28 @@
1
- # rake effective_orders:overwrite_order_item_titles
1
+ # rake effective_orders:overwrite_order_item_names
2
2
 
3
3
  namespace :effective_orders do
4
- desc 'Overwrite all order item titles with current acts_as_purchasable object title'
5
- task overwrite_order_item_titles: :environment do
6
- puts 'WARNING: this task will overwrite all existing order items with new titles. Proceed? (y/n)'
4
+ desc 'Overwrite all order item names with current acts_as_purchasable object names'
5
+ task overwrite_order_item_names: :environment do
6
+ puts 'WARNING: this task will overwrite all existing order items with new names. Proceed? (y/n)'
7
7
  (puts 'Aborted' and exit) unless STDIN.gets.chomp.downcase == 'y'
8
8
 
9
9
  Effective::OrderItem.transaction do
10
10
  begin
11
11
 
12
12
  Effective::OrderItem.includes(:purchasable).find_each do |order_item|
13
- new_title = order_item.purchasable.title
13
+ new_name = order_item.purchasable.purchasable_name
14
14
 
15
- unless new_title
15
+ unless new_name
16
16
  raise "acts_as_purchasable object #{order_item.purchasable_type.try(:classify)}<#{order_item.purchasable_id}>.title() from Effective::OrderItem<#{order_item.id}> cannot be nil."
17
17
  end
18
18
 
19
- order_item.update_column(:title, new_title) # This intentionally skips validation
19
+ order_item.update_column(:name, new_name) # This intentionally skips validation
20
20
  end
21
21
 
22
- puts 'Successfully updated all order item titles.'
22
+ puts 'Successfully updated all order item names.'
23
23
  rescue => e
24
24
  puts "An error has occurred: #{e.message}"
25
- puts "(effective_orders) Rollback. No order item titles have been changed."
25
+ puts "(effective_orders) Rollback. No order item names have been changed."
26
26
  raise ActiveRecord::Rollback
27
27
  end
28
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_orders
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0beta4
4
+ version: 4.0.0beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-04 00:00:00.000000000 Z
11
+ date: 2018-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -125,7 +125,6 @@ files:
125
125
  - app/assets/stylesheets/effective_orders.scss
126
126
  - app/assets/stylesheets/effective_orders/_cart.scss
127
127
  - app/assets/stylesheets/effective_orders/_order.scss
128
- - app/assets/stylesheets/effective_orders/_subscriptions.scss
129
128
  - app/controllers/admin/customers_controller.rb
130
129
  - app/controllers/admin/order_items_controller.rb
131
130
  - app/controllers/admin/orders_controller.rb
@@ -1,14 +0,0 @@
1
- // This is the radio button form input
2
- .effective-orders-stripe-plans {
3
- input { display: none; }
4
- label { width: 100%; padding-left: 0px; }
5
- .radio + .radio { margin-top: 10px; }
6
-
7
- .visible-when-selected { display: none; }
8
- .visible-when-unselected { display: block; }
9
-
10
- label > .selected {
11
- .visible-when-selected { display: block; }
12
- .visible-when-unselected { display: none; }
13
- }
14
- }