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