effective_orders 4.1.5 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/effective_orders/subscriptions.js.coffee +8 -5
- data/app/helpers/effective_stripe_helper.rb +1 -1
- data/app/helpers/effective_subscriptions_helper.rb +12 -14
- data/app/models/effective/subscripter.rb +6 -2
- data/app/models/effective/subscription.rb +4 -2
- data/app/views/effective/customers/_customer.html.haml +0 -2
- data/app/views/effective/subscripter/_form.html.haml +26 -37
- data/app/views/effective/subscripter/_plan.html.haml +23 -0
- data/lib/effective_orders.rb +35 -6
- data/lib/effective_orders/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cba395b26b2db05cfcbfd3340db1c83318f97a190130b7f009363314a45b2a51
|
4
|
+
data.tar.gz: 5e9e3a2de5aee44d4fbde7374dee306ad9acffa30513f97e073050d04acb2955
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c4bbcc96e0ba2ace04bb7779b4c56aaa2578fb3b7c05ecfb2979b94c4fd8a12581fc3d9a77de1f267cf3ff1e00104f122b49dc29354ea7b95e5c91c2b2c5f1e
|
7
|
+
data.tar.gz: b48693aa3680d77555a459366c7379ba96b6b2894c40dbe01bd1a630440843a414d393a2cc84946130c1a59ae5290d5576f15d0cd16145ad2df31ee22e01f7a7
|
@@ -20,7 +20,9 @@ $(document).on 'change keyup', '.effective-orders-subscripter-plan-quantity', (e
|
|
20
20
|
return unless $plan.length == 1
|
21
21
|
|
22
22
|
# Assign the quantity to each quantity field
|
23
|
-
$plan.closest('form')
|
23
|
+
$plan.closest('form')
|
24
|
+
.find(".effective-orders-stripe-plan:not([data-plan-id='#{$plan.data('id')}'])")
|
25
|
+
.find("input[name$='[quantity]']").val($obj.val())
|
24
26
|
|
25
27
|
quantity = $obj.val() || 0
|
26
28
|
|
@@ -28,11 +30,9 @@ $(document).on 'change keyup', '.effective-orders-subscripter-plan-quantity', (e
|
|
28
30
|
# Assign all totals
|
29
31
|
plan = $(this)
|
30
32
|
amount = parseInt(plan.data('amount'))
|
31
|
-
interval = plan.data('
|
33
|
+
interval = plan.data('interval')
|
32
34
|
|
33
35
|
total = (quantity * amount)
|
34
|
-
total = (total / 12) if interval == 'year'
|
35
|
-
|
36
36
|
total = '$' + (total / 100.0).toFixed(2)
|
37
37
|
|
38
38
|
plan.find('#effective_subscripter_total_amount').text(total)
|
@@ -53,6 +53,9 @@ $(document).on 'click', ".effective-orders-stripe-token-required[type='submit'],
|
|
53
53
|
stripe = $form.data('stripe')
|
54
54
|
return unless stripe?
|
55
55
|
|
56
|
+
plans = $form.data('plans')
|
57
|
+
return unless plans?
|
58
|
+
|
56
59
|
# If we're doing choose button mode
|
57
60
|
if $obj.data('choose-stripe-plan-id')
|
58
61
|
$form.find("input[name$='[stripe_plan_id]']").val($obj.data('choose-stripe-plan-id'))
|
@@ -63,7 +66,7 @@ $(document).on 'click', ".effective-orders-stripe-token-required[type='submit'],
|
|
63
66
|
return unless selected_plan_id.length > 0
|
64
67
|
|
65
68
|
# Match plan
|
66
|
-
plan =
|
69
|
+
plan = plans.find (plan, _) => plan.id == selected_plan_id
|
67
70
|
return unless plan?
|
68
71
|
|
69
72
|
# Okay, we're good to call stripe
|
@@ -4,7 +4,7 @@ module EffectiveStripeHelper
|
|
4
4
|
plan = (
|
5
5
|
case obj
|
6
6
|
when Hash ; obj
|
7
|
-
when ::Stripe::Plan ; EffectiveOrders.stripe_plans.find { |plan| plan
|
7
|
+
when ::Stripe::Plan ; EffectiveOrders.stripe_plans.find { |plan| plan[:id] == obj.id }
|
8
8
|
else ; raise 'unexpected object'
|
9
9
|
end
|
10
10
|
)
|
@@ -1,10 +1,16 @@
|
|
1
1
|
module EffectiveSubscriptionsHelper
|
2
2
|
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
def subscripter_stripe_data(subscripter)
|
4
|
+
{
|
5
|
+
email: current_user.email,
|
6
|
+
image: stripe_site_image_url,
|
7
|
+
key: EffectiveOrders.stripe[:publishable_key],
|
8
|
+
name: EffectiveOrders.stripe[:site_title],
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def subscripter_stripe_plans(subscripter)
|
13
|
+
EffectiveOrders.stripe_plans
|
8
14
|
end
|
9
15
|
|
10
16
|
def subscribable_form_with(subscribable)
|
@@ -25,14 +31,6 @@ module EffectiveSubscriptionsHelper
|
|
25
31
|
render('effective/customers/form', subscripter: subscripter)
|
26
32
|
end
|
27
33
|
|
28
|
-
|
29
|
-
{
|
30
|
-
email: current_user.email,
|
31
|
-
image: stripe_site_image_url,
|
32
|
-
key: EffectiveOrders.stripe[:publishable_key],
|
33
|
-
name: EffectiveOrders.stripe[:site_title],
|
34
|
-
plans: EffectiveOrders.stripe_plans.values
|
35
|
-
}
|
36
|
-
end
|
34
|
+
|
37
35
|
|
38
36
|
end
|
@@ -11,7 +11,11 @@ module Effective
|
|
11
11
|
validates :subscribable, presence: true, if: -> { stripe_plan_id.present? }
|
12
12
|
validates :customer, presence: true
|
13
13
|
|
14
|
-
validates :stripe_plan_id, inclusion: {
|
14
|
+
validates :stripe_plan_id, inclusion: {
|
15
|
+
allow_blank: true,
|
16
|
+
in: EffectiveOrders.stripe_plans.map { |plan| plan[:id] },
|
17
|
+
message: 'unknown plan'
|
18
|
+
}
|
15
19
|
|
16
20
|
validate(if: -> { stripe_plan_id && plan && plan[:amount] > 0 }) do
|
17
21
|
self.errors.add(:stripe_token, 'updated payment card required') if stripe_token.blank? && token_required?
|
@@ -43,7 +47,7 @@ module Effective
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def plan
|
46
|
-
EffectiveOrders.stripe_plans[stripe_plan_id
|
50
|
+
EffectiveOrders.stripe_plans.find { |plan| plan[:id] == stripe_plan_id }
|
47
51
|
end
|
48
52
|
|
49
53
|
def quantity=(value)
|
@@ -45,7 +45,9 @@ module Effective
|
|
45
45
|
validates :customer, presence: true
|
46
46
|
validates :subscribable, presence: true
|
47
47
|
|
48
|
-
validates :stripe_plan_id, presence: true
|
48
|
+
validates :stripe_plan_id, presence: true
|
49
|
+
validates :stripe_plan_id, inclusion: { in: EffectiveOrders.stripe_plans.map { |plan| plan[:id] } }
|
50
|
+
|
49
51
|
validates :stripe_subscription_id, presence: true
|
50
52
|
|
51
53
|
validates :name, presence: true
|
@@ -59,7 +61,7 @@ module Effective
|
|
59
61
|
end
|
60
62
|
|
61
63
|
def plan
|
62
|
-
EffectiveOrders.stripe_plans[stripe_plan_id
|
64
|
+
EffectiveOrders.stripe_plans.find { |plan| plan[:id] == stripe_plan_id }
|
63
65
|
end
|
64
66
|
|
65
67
|
def stripe_subscription
|
@@ -1,23 +1,23 @@
|
|
1
1
|
= javascript_include_tag 'https://checkout.stripe.com/checkout.js'
|
2
2
|
|
3
|
-
|
3
|
+
- stripe = subscripter_stripe_data(subscripter)
|
4
|
+
- plans = subscripter_stripe_plans(subscripter)
|
5
|
+
|
6
|
+
- subscripter.quantity ||= subscripter.subscribable.subscribable_quantity_used
|
7
|
+
- subscripter.stripe_plan_id ||= Hash(plans.first)[:id]
|
8
|
+
|
9
|
+
= effective_form_with(model: subscripter, url: effective_orders.subscripter_path, remote: true, data: { stripe: stripe, plans: plans }) do |f|
|
4
10
|
= f.hidden_field :subscribable_global_id
|
5
11
|
= f.hidden_field :stripe_token, value: nil
|
6
|
-
= f.error :stripe_token
|
7
|
-
|
8
12
|
= f.hidden_field :stripe_plan_id
|
9
|
-
|
10
|
-
- stripe_plans = stripe_plans_collection(f.object)
|
11
|
-
- stripe_plans = stripe_plans.select { |plan| plan[:name].include?('per User') }
|
12
|
-
|
13
|
-
- f.object.stripe_plan_id ||= (stripe_plans.first || {})[:id]
|
13
|
+
= f.error :stripe_token
|
14
14
|
|
15
15
|
- if f.object.subscribable.subscribed?
|
16
16
|
- quantity_used = f.object.subscribable.subscribable_quantity_used.to_i
|
17
17
|
- quantity_purchased = f.object.subscribable.subscription.quantity
|
18
18
|
|
19
19
|
.text-center
|
20
|
-
%p
|
20
|
+
%p
|
21
21
|
You currently have <strong>#{pluralize(quantity_used, 'member')}</strong> in your team
|
22
22
|
and have space for <strong>#{pluralize(quantity_purchased, 'member')}</strong>.
|
23
23
|
|
@@ -26,39 +26,28 @@
|
|
26
26
|
- else
|
27
27
|
%p To add more members you will need to update your plan.
|
28
28
|
|
29
|
-
%p To pay for less than #{pluralize(quantity_used, 'member')}
|
29
|
+
%p To pay for less than #{pluralize(quantity_used, 'member')}, remove some team members first.
|
30
30
|
|
31
|
-
|
32
|
-
.
|
33
|
-
%
|
31
|
+
- else
|
32
|
+
.text-center
|
33
|
+
%p
|
34
|
+
You are not currently subscribed to any plan.
|
35
|
+
%br
|
36
|
+
For full access, upgrade to a paid plan below.
|
37
|
+
%br
|
38
|
+
To upgrade with fewer people, remove some team members first.
|
34
39
|
|
35
|
-
|
40
|
+
.card.mb-4
|
41
|
+
.card-body.card-subscripter
|
42
|
+
%h5.card-title.subscripter-title Billing Cycle
|
36
43
|
|
37
|
-
|
44
|
+
= f.radios :stripe_plan_id, plans.map { |plan| [plan[:name], plan[:id]] }, label: false, buttons: true
|
38
45
|
|
39
|
-
-
|
40
|
-
= f.show_if(:stripe_plan_id, plan[:id]) do
|
41
|
-
.effective-orders-stripe-plan{'data-id': plan[:id], 'data-amount': plan[:amount], 'data-interval': plan[:interval]}
|
42
|
-
.d-flex.justify-content-around.align-items-center
|
43
|
-
= f.number_field :quantity, class: 'effective-orders-subscripter-plan-quantity form-control-lg', autocomplete: 'off', required: true
|
44
|
-
%div= 'x'
|
45
|
-
= f.static_field :price_per_person do
|
46
|
-
= price_to_currency(plan[:amount])
|
47
|
-
%div= '='
|
48
|
-
= f.static_field :total_amount do
|
49
|
-
= price_to_currency(plan[:amount] * f.object.quantity.to_i)
|
46
|
+
%hr.mt-4.mb-4
|
50
47
|
|
51
|
-
-
|
48
|
+
- plans.each do |plan|
|
52
49
|
= f.show_if(:stripe_plan_id, plan[:id]) do
|
53
|
-
|
54
|
-
.d-flex.justify-content-around.align-items-center
|
55
|
-
= f.number_field :quantity, class: 'effective-orders-subscripter-plan-quantity form-control-lg', autocomplete: 'off', required: true
|
56
|
-
%div= 'x'
|
57
|
-
= f.static_field :price_per_person do
|
58
|
-
= price_to_currency(plan[:amount] / 12)
|
59
|
-
%div= '='
|
60
|
-
= f.static_field :total_amount do
|
61
|
-
= price_to_currency(plan[:amount] * f.object.quantity.to_i)
|
50
|
+
= render 'effective/subscripter/plan', plan: plan, f: f
|
62
51
|
|
63
52
|
= f.submit(border: false, center: true) do
|
64
|
-
= f.save('
|
53
|
+
= f.save('Continue to Billing', class: ('effective-orders-stripe-token-required' if f.object.token_required?))
|
@@ -0,0 +1,23 @@
|
|
1
|
+
.effective-orders-stripe-plan{data: plan}
|
2
|
+
.d-flex.justify-content-around.align-top
|
3
|
+
= f.integer_field :quantity, autocomplete: 'off', required: true, label: 'Total People', maxlength: 3,
|
4
|
+
class: 'effective-orders-subscripter-plan-quantity form-control-lg'
|
5
|
+
|
6
|
+
.subscripter-math-symbol= 'x'
|
7
|
+
|
8
|
+
= f.static_field :price_per_person, label: 'Price Per Person' do
|
9
|
+
= price_to_currency(plan[:amount])
|
10
|
+
|
11
|
+
- if plan[:savings]
|
12
|
+
.subscripter-savings.text-danger
|
13
|
+
Save #{price_to_currency(plan[:savings])}/person
|
14
|
+
|
15
|
+
.subscripter-math-symbol= '='
|
16
|
+
|
17
|
+
= f.static_field :total_amount, label: 'Total Amount' do
|
18
|
+
= price_to_currency(plan[:amount] * f.object.quantity.to_i)
|
19
|
+
|
20
|
+
- if plan[:savings]
|
21
|
+
.subscripter-total-savings.text-danger
|
22
|
+
Save
|
23
|
+
%span #{price_to_currency(f.object.quantity.to_i * plan[:savings])}
|
data/lib/effective_orders.rb
CHANGED
@@ -166,7 +166,7 @@ module EffectiveOrders
|
|
166
166
|
end
|
167
167
|
|
168
168
|
def self.stripe_plans
|
169
|
-
return
|
169
|
+
return [] unless (stripe? && subscriptions?)
|
170
170
|
|
171
171
|
@stripe_plans ||= (
|
172
172
|
Rails.logger.info '[STRIPE] index plans'
|
@@ -177,24 +177,53 @@ module EffectiveOrders
|
|
177
177
|
raise e if Rails.env.production?
|
178
178
|
Rails.logger.info "[STRIPE ERROR]: #{e.message}"
|
179
179
|
Rails.logger.info "[STRIPE ERROR]: effective_orders continuing with empty stripe plans. This would fail loudly in Rails.env.production."
|
180
|
-
|
180
|
+
[]
|
181
181
|
end
|
182
182
|
|
183
|
-
plans.
|
184
|
-
|
183
|
+
plans = plans.map do |plan|
|
184
|
+
{
|
185
185
|
id: plan.id,
|
186
186
|
product_id: plan.product,
|
187
187
|
name: plan.nickname,
|
188
188
|
amount: plan.amount,
|
189
189
|
currency: plan.currency,
|
190
|
-
description: "$#{'%0.2f' % (plan.amount / 100.0)}
|
190
|
+
description: ("$#{'%0.2f' % (plan.amount / 100.0)}" + ' ' + plan.currency.upcase + '/' + plan.interval.to_s),
|
191
191
|
interval: plan.interval,
|
192
192
|
interval_count: plan.interval_count
|
193
|
-
}
|
193
|
+
}
|
194
|
+
end.sort do |x, y|
|
195
|
+
val ||= (x[:interval] <=> y[:interval])
|
196
|
+
val = nil if val == 0
|
197
|
+
|
198
|
+
val ||= (x[:amount] <=> y[:amount])
|
199
|
+
val = nil if val == 0
|
200
|
+
|
201
|
+
val ||= (x[:name] <=> y[:name])
|
202
|
+
val = nil if val == 0
|
203
|
+
|
204
|
+
val || (x[:id] <=> y[:id])
|
205
|
+
end
|
206
|
+
|
207
|
+
# Calculate savings for any yearly per user plans, based on their matching monthly plans
|
208
|
+
plans.select { |plan| plan[:interval] == 'year' && plan[:name].downcase.include?('per') }.each do |yearly|
|
209
|
+
monthly_name = yearly[:name].downcase.gsub('year', 'month')
|
210
|
+
monthly = plans.find { |plan| plan[:interval] == 'month' && plan[:name].downcase == monthly_name }
|
211
|
+
next unless monthly
|
212
|
+
|
213
|
+
savings = (monthly[:amount].to_i * 12) - yearly[:amount].to_i
|
214
|
+
next unless savings > 0
|
215
|
+
|
216
|
+
yearly[:savings] = savings
|
194
217
|
end
|
218
|
+
|
219
|
+
plans
|
195
220
|
)
|
196
221
|
end
|
197
222
|
|
223
|
+
def self.stripe_plans_collection
|
224
|
+
stripe_plans.map { |plan| [plan[:name], plan[:id]] }
|
225
|
+
end
|
226
|
+
|
198
227
|
class SoldOutException < Exception; end
|
199
228
|
class AlreadyPurchasedException < Exception; end
|
200
229
|
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.
|
4
|
+
version: 4.2.0
|
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: 2019-
|
11
|
+
date: 2019-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -231,6 +231,7 @@ files:
|
|
231
231
|
- app/views/effective/orders_mailer/subscription_trialing.html.haml
|
232
232
|
- app/views/effective/orders_mailer/subscription_updated.html.haml
|
233
233
|
- app/views/effective/subscripter/_form.html.haml
|
234
|
+
- app/views/effective/subscripter/_plan.html.haml
|
234
235
|
- app/views/layouts/effective_orders_mailer_layout.html.haml
|
235
236
|
- config/effective_orders.rb
|
236
237
|
- config/routes.rb
|