effective_orders 4.0.0beta4 → 4.0.0beta5

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