billingly 0.0.0 → 0.1.1
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.
- data/README.md +36 -0
- data/TUTORIAL.rdoc +158 -0
- data/app/controllers/billingly/subscriptions_controller.rb +33 -13
- data/app/models/billingly/base_customer.rb +306 -0
- data/app/models/billingly/base_plan.rb +16 -0
- data/app/models/billingly/base_subscription.rb +118 -0
- data/app/models/billingly/customer.rb +2 -115
- data/app/models/billingly/invoice.rb +20 -21
- data/app/models/billingly/journal_entry.rb +15 -0
- data/app/models/billingly/ledger_entry.rb +2 -3
- data/app/models/billingly/payment.rb +1 -1
- data/app/models/billingly/plan.rb +2 -4
- data/app/models/billingly/subscription.rb +2 -81
- data/app/views/billingly/subscriptions/_current_subscription.html.haml +22 -0
- data/app/views/billingly/subscriptions/_deactivation_notice.html.haml +12 -0
- data/app/views/billingly/subscriptions/_invoice_details.html.haml +9 -0
- data/app/views/billingly/subscriptions/_invoices.html.haml +30 -0
- data/app/views/billingly/subscriptions/_plans.html.haml +30 -0
- data/app/views/billingly/subscriptions/index.html.haml +10 -0
- data/app/views/billingly/subscriptions/invoice.html.haml +8 -0
- data/app/views/billingly/subscriptions/new.html.erb +1 -1
- data/app/views/billingly_mailer/paid_notification.html.erb +1 -1
- data/lib/billingly/engine.rb +1 -3
- data/lib/billingly/rails/routes.rb +3 -1
- data/lib/billingly/version.rb +1 -1
- data/lib/generators/billingly_mailer_views_generator.rb +9 -0
- data/lib/generators/billingly_views_generator.rb +9 -0
- data/lib/generators/templates/create_billingly_tables.rb +11 -16
- data/lib/tasks/billingly_tasks.rake +2 -0
- metadata +46 -22
- data/README.rdoc +0 -3
- data/app/views/billingly/subscriptions/index.html.erb +0 -2
@@ -0,0 +1,16 @@
|
|
1
|
+
module Billingly
|
2
|
+
require 'has_duration'
|
3
|
+
class BasePlan < ActiveRecord::Base
|
4
|
+
self.abstract_class = true
|
5
|
+
self.table_name = 'billingly_plans'
|
6
|
+
|
7
|
+
attr_accessible :name, :plan_code, :description, :periodicity,
|
8
|
+
:amount, :payable_upfront, :grace_period
|
9
|
+
|
10
|
+
has_duration :grace_period
|
11
|
+
validates :grace_period, presence: true
|
12
|
+
|
13
|
+
has_duration :periodicity
|
14
|
+
validates :periodicity, presence: true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Billingly
|
2
|
+
require 'has_duration'
|
3
|
+
|
4
|
+
# A customer will always have at least one subscription to your application.
|
5
|
+
# Everytime there is a change in a {Customer customer's} subscription, the current one
|
6
|
+
# is terminated immediately and a new one is created.
|
7
|
+
#
|
8
|
+
# For example, changing a {Plan} consists on terminating the current
|
9
|
+
# subscription and creating a new one for the new plan.
|
10
|
+
# Also, a new subscription is created when {Customer customers} reactivate their accounts
|
11
|
+
# after being deactivated.
|
12
|
+
#
|
13
|
+
# The most recent subscription is the one currently being charged for, unless the customer
|
14
|
+
# is deactivated at the moment, in which case the last subscription should not be considered
|
15
|
+
# to be active.
|
16
|
+
class BaseSubscription < ActiveRecord::Base
|
17
|
+
self.abstract_class = true
|
18
|
+
self.table_name = 'billingly_subscriptions'
|
19
|
+
|
20
|
+
has_many :ledger_entries, foreign_key: :subscription_id
|
21
|
+
|
22
|
+
# The date in which this subscription started.
|
23
|
+
# This subscription's first invoice will have it's period_start date
|
24
|
+
# matching the date in which the subscription started.
|
25
|
+
# @property subscribed_on
|
26
|
+
# @return [DateTime]
|
27
|
+
validates :subscribed_on, presence: true
|
28
|
+
|
29
|
+
# The grace period we use when calculating an invoices due date.
|
30
|
+
# If a subscription is payable_upfront, then the customer effectively owes us
|
31
|
+
# since the day in which a given period starts.
|
32
|
+
# If a subscription is payable on 'due-month', then the customer effectively
|
33
|
+
# owes us money since the date in which a given period ended.
|
34
|
+
# When we invoice for a given period we will set the due_date a few days
|
35
|
+
# ahead of the date in which the debt was made effective, we call this
|
36
|
+
# a grace_period.
|
37
|
+
# @property grace_period
|
38
|
+
# @return [ActiveSupport::Duration] It's what you get by doing '1.month', '10.days', etc.
|
39
|
+
has_duration :grace_period
|
40
|
+
validates :grace_period, presence: true
|
41
|
+
|
42
|
+
# A subscription can be a free trial, ending on the date stored in `is_trial_expiring_on'.
|
43
|
+
# Free trial subscriptions don't have {Invoice invoices}. The {Customer customer} is required
|
44
|
+
# to subscribe to a non-trial plan before the trial subscription expires.
|
45
|
+
#
|
46
|
+
# The {#trial?} convenience method returns wheter this subscription is a trial or not.
|
47
|
+
#
|
48
|
+
# @property is_trial_expiring_on
|
49
|
+
# @return [DateTime]
|
50
|
+
|
51
|
+
# When a subscription was started from a {Plan} a reference to the plan is saved.
|
52
|
+
# Although, all the plan's fields are denormalized in this subscription.
|
53
|
+
#
|
54
|
+
# If the subscription was not started from a plan, then this will be nil.
|
55
|
+
#
|
56
|
+
# @property plan
|
57
|
+
# @return [Billingly::Plan, nil]
|
58
|
+
belongs_to :plan
|
59
|
+
|
60
|
+
# (see #is_trial_expiring_on)
|
61
|
+
# @property trial?
|
62
|
+
# @return [Boolean]
|
63
|
+
def trial?
|
64
|
+
not is_trial_expiring_on.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Invoices will be generated before their due_date, as soon as possible,
|
68
|
+
# but not sooner than GENERATE_AHEAD days.
|
69
|
+
GENERATE_AHEAD = 3.days
|
70
|
+
|
71
|
+
belongs_to :customer
|
72
|
+
has_many :invoices, foreign_key: :subscription_id
|
73
|
+
|
74
|
+
has_duration :periodicity
|
75
|
+
validates :periodicity, presence: true
|
76
|
+
|
77
|
+
# The invoice generation process should run frequently, at least on a daily basis.
|
78
|
+
# It will create invoices some time before they are due, to give customers a chance
|
79
|
+
# to pay and settle them.
|
80
|
+
# This method is idempotent, if an upcoming invoice for a subscription already exists,
|
81
|
+
# it does not create yet another one.
|
82
|
+
def generate_next_invoice
|
83
|
+
return if terminated?
|
84
|
+
return if trial?
|
85
|
+
from = invoices.empty? ? subscribed_on : invoices.last.period_end
|
86
|
+
to = from + periodicity
|
87
|
+
due_on = (payable_upfront ? from : to) + grace_period
|
88
|
+
return if GENERATE_AHEAD.from_now < from
|
89
|
+
|
90
|
+
invoice = invoices.create!(customer: customer, amount: amount,
|
91
|
+
due_on: due_on, period_start: from, period_end: to)
|
92
|
+
invoice.charge
|
93
|
+
return invoice
|
94
|
+
end
|
95
|
+
|
96
|
+
# Terminates this subscription, it could be either because we deactivate a debtor
|
97
|
+
# or because the customer decided to end his subscription on his own terms.
|
98
|
+
def terminate
|
99
|
+
return if terminated?
|
100
|
+
update_attribute(:unsubscribed_on, Time.now)
|
101
|
+
invoices.last.truncate unless trial?
|
102
|
+
return self
|
103
|
+
end
|
104
|
+
|
105
|
+
def terminated?
|
106
|
+
not unsubscribed_on.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
# This class method is called from a cron job, it creates invoices for all the subscriptions
|
110
|
+
# that still need their invoice created.
|
111
|
+
# TODO: This goes through all the active subscriptions, make it smarter so that the batch job runs quicker.
|
112
|
+
def self.generate_next_invoices
|
113
|
+
where(is_trial_expiring_on: nil, unsubscribed_on: nil).each do |subscription|
|
114
|
+
subscription.generate_next_invoice
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -1,116 +1,3 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# * Customers are charged for one-time expenses.
|
4
|
-
# * Payments are received on a Customer's behalf and credited to their account.
|
5
|
-
# * Invoices are generated periodically calculating charges a Customer incurred in.
|
6
|
-
# * Receipts are sent to Customers when their invoices are paid.
|
7
|
-
|
8
|
-
require 'validates_email_format_of'
|
9
|
-
|
10
|
-
module Billingly
|
11
|
-
class Customer < ActiveRecord::Base
|
12
|
-
has_many :subscriptions
|
13
|
-
has_many :one_time_charges
|
14
|
-
has_many :invoices
|
15
|
-
has_many :ledger_entries
|
16
|
-
|
17
|
-
attr_accessible :email
|
18
|
-
validates_email_format_of :email
|
19
|
-
|
20
|
-
# Customers subscribe to the service and perform periodic payments to continue using it.
|
21
|
-
# We offer common plans stating how much and how often they should pay, also, if the
|
22
|
-
# payment is to be done at the beginning or end of the period (upfront or due-month)
|
23
|
-
# Every customer can potentially get a special deal, but we offer common
|
24
|
-
# deals as 'plans' from which a proper subscription is created.
|
25
|
-
def subscribe_to_plan(plan)
|
26
|
-
subscriptions.last.terminate if subscriptions.last
|
27
|
-
|
28
|
-
subscriptions.build.tap do |new|
|
29
|
-
[:payable_upfront, :description, :periodicity, :amount].each do |k|
|
30
|
-
new[k] = plan[k]
|
31
|
-
end
|
32
|
-
new.subscribed_on = Time.now
|
33
|
-
new.save!
|
34
|
-
new.generate_next_invoice
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Returns the actual subscription of the customer. while working with the
|
39
|
-
# customer API a customer should only have 1 active subscription at a time.
|
40
|
-
def active_subscription
|
41
|
-
subscriptions.last
|
42
|
-
end
|
43
|
-
|
44
|
-
# Every transaction is registered in the journal from where a general ledger can
|
45
|
-
# be retrieved.
|
46
|
-
# Due to silly rounding errors on sqlite we need to convert decimals to float and then to
|
47
|
-
# decimals again. :S
|
48
|
-
def ledger
|
49
|
-
Hash.new(0.0).tap do |all|
|
50
|
-
ledger_entries.group_by(&:account).collect do |account, entries|
|
51
|
-
values = entries.collect(&:amount).collect(&:to_f)
|
52
|
-
all[account.to_sym] = values.inject(0.0) do |sum,item|
|
53
|
-
(BigDecimal.new(sum.to_s) + BigDecimal.new(item.to_s)).to_f
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Shortcut for adding ledger_entries for a particular customer.
|
60
|
-
def add_to_ledger(amount, *accounts, extra)
|
61
|
-
accounts = [] if accounts.nil?
|
62
|
-
unless extra.is_a?(Hash)
|
63
|
-
accounts << extra
|
64
|
-
extra = {}
|
65
|
-
end
|
66
|
-
|
67
|
-
accounts.each do |account|
|
68
|
-
ledger_entries.create!(extra.merge(amount: amount, account: account.to_s))
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# This class method is run periodically deactivate all customers who have overdue invoices.
|
73
|
-
def self.deactivate_all_debtors
|
74
|
-
debtors.where(deactivated_since: nil).all.each{|debtor| debtor.deactivate }
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.debtors
|
78
|
-
joins(:invoices).readonly(false)
|
79
|
-
.where("#{Billingly::Invoice.table_name}.due_on < ?", Time.now)
|
80
|
-
.where(billingly_invoices: {deleted_on: nil, receipt_id: nil})
|
81
|
-
end
|
82
|
-
|
83
|
-
# Credits a payment for a customer, settling invoices if possible.
|
84
|
-
def credit_payment(amount)
|
85
|
-
Billingly::Payment.credit_for(self, amount)
|
86
|
-
Billingly::Invoice.charge_all(self.invoices)
|
87
|
-
reactivate
|
88
|
-
end
|
89
|
-
|
90
|
-
def deactivate
|
91
|
-
return if deactivated?
|
92
|
-
active_subscription.terminate
|
93
|
-
update_attribute(:deactivated_since, Time.now)
|
94
|
-
return self
|
95
|
-
end
|
96
|
-
|
97
|
-
# Reactivates a customer that was deactivated when missed a previous payment.
|
98
|
-
# The new subscription is parametrized the same as the old one. The old subscription
|
99
|
-
# is terminated.
|
100
|
-
def reactivate(new_plan = active_subscription)
|
101
|
-
return unless deactivated?
|
102
|
-
return if debtor?
|
103
|
-
update_attribute(:deactivated_since, nil)
|
104
|
-
subscribe_to_plan(new_plan)
|
105
|
-
return self
|
106
|
-
end
|
107
|
-
|
108
|
-
def deactivated?
|
109
|
-
not deactivated_since.nil?
|
110
|
-
end
|
111
|
-
|
112
|
-
def debtor?
|
113
|
-
not self.class.debtors.find_by_id(self.id).nil?
|
114
|
-
end
|
115
|
-
end
|
1
|
+
# (see Billingly::BaseCustomer)
|
2
|
+
class Billingly::Customer < Billingly::BaseCustomer
|
116
3
|
end
|
@@ -2,12 +2,11 @@ module Billingly
|
|
2
2
|
class Invoice < ActiveRecord::Base
|
3
3
|
belongs_to :subscription
|
4
4
|
belongs_to :customer
|
5
|
-
|
6
|
-
has_many :ledger_entries
|
5
|
+
has_many :journal_entries
|
7
6
|
attr_accessible :customer, :amount, :due_on, :period_start, :period_end
|
8
7
|
|
9
8
|
def paid?
|
10
|
-
not
|
9
|
+
not paid_on.nil?
|
11
10
|
end
|
12
11
|
|
13
12
|
def deleted?
|
@@ -15,19 +14,19 @@ module Billingly
|
|
15
14
|
end
|
16
15
|
|
17
16
|
# Settle an invoice by moving money from the cash balance into an expense,
|
18
|
-
#
|
17
|
+
# after charged the invoice becomes paid.
|
19
18
|
def charge
|
20
19
|
return if paid?
|
21
20
|
return unless deleted_on.nil?
|
21
|
+
return if period_start > Time.now
|
22
22
|
return if customer.ledger[:cash] < amount
|
23
|
-
|
24
|
-
|
25
|
-
extra = {
|
26
|
-
customer.
|
27
|
-
customer.
|
28
|
-
|
29
|
-
|
30
|
-
return receipt
|
23
|
+
|
24
|
+
update_attribute(:paid_on, Time.now)
|
25
|
+
extra = {invoice: self, subscription: subscription}
|
26
|
+
customer.add_to_journal(-(amount), :cash, extra)
|
27
|
+
customer.add_to_journal(amount, :spent, extra)
|
28
|
+
|
29
|
+
return self
|
31
30
|
end
|
32
31
|
|
33
32
|
# When a subscription terminates, it's last invoice gets truncated so that it does
|
@@ -48,8 +47,8 @@ module Billingly
|
|
48
47
|
if paid?
|
49
48
|
reimburse = (old_amount - self.amount).round(2)
|
50
49
|
extra = {invoice: self, subscription: subscription}
|
51
|
-
customer.
|
52
|
-
customer.
|
50
|
+
customer.add_to_journal(reimburse, :cash, extra)
|
51
|
+
customer.add_to_journal(-(reimburse), :spent, extra)
|
53
52
|
end
|
54
53
|
save!
|
55
54
|
self.charge
|
@@ -58,15 +57,14 @@ module Billingly
|
|
58
57
|
|
59
58
|
# Charges all invoices that can be charged from the existing customer cash balances
|
60
59
|
def self.charge_all(collection = self)
|
61
|
-
collection.where(deleted_on: nil,
|
60
|
+
collection.where(deleted_on: nil, paid_on: nil).order('period_start').each do |invoice|
|
62
61
|
invoice.charge
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
66
|
-
#
|
65
|
+
# This method is called by Billingly's recurring task to notify all pending invoices.
|
67
66
|
def self.notify_all_pending
|
68
|
-
where(
|
69
|
-
.where(deleted_on: nil, receipt_id: nil, notified_pending_on: nil)
|
67
|
+
where(deleted_on: nil, paid_on: nil, notified_pending_on: nil)
|
70
68
|
.each do |invoice|
|
71
69
|
invoice.notify_pending
|
72
70
|
end
|
@@ -76,7 +74,7 @@ module Billingly
|
|
76
74
|
return unless notified_pending_on.nil?
|
77
75
|
return if paid?
|
78
76
|
return if deleted?
|
79
|
-
return if due_on >
|
77
|
+
return if due_on > subscription.grace_period.from_now
|
80
78
|
BillinglyMailer.pending_notification(self).deliver!
|
81
79
|
update_attribute(:notified_pending_on, Time.now)
|
82
80
|
end
|
@@ -85,7 +83,7 @@ module Billingly
|
|
85
83
|
# being cancelled
|
86
84
|
def self.notify_all_overdue
|
87
85
|
where('due_on <= ?', Time.now)
|
88
|
-
.where(deleted_on: nil,
|
86
|
+
.where(deleted_on: nil, paid_on: nil, notified_overdue_on: nil)
|
89
87
|
.each do |invoice|
|
90
88
|
invoice.notify_overdue
|
91
89
|
end
|
@@ -104,11 +102,12 @@ module Billingly
|
|
104
102
|
# Send the email notifying that this invoice being overdue and the subscription
|
105
103
|
# being cancelled
|
106
104
|
def self.notify_all_paid
|
107
|
-
where('
|
105
|
+
where('paid_on is not null')
|
108
106
|
.where(deleted_on: nil, notified_paid_on: nil).each do |invoice|
|
109
107
|
invoice.notify_paid
|
110
108
|
end
|
111
109
|
end
|
110
|
+
|
112
111
|
def notify_paid
|
113
112
|
return unless paid?
|
114
113
|
return unless notified_paid_on.nil?
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Billingly
|
2
|
+
class JournalEntry < ActiveRecord::Base
|
3
|
+
belongs_to :customer
|
4
|
+
belongs_to :invoice
|
5
|
+
belongs_to :payment
|
6
|
+
belongs_to :subscription
|
7
|
+
|
8
|
+
validates :amount, presence: true
|
9
|
+
validates :customer, presence: true
|
10
|
+
validates :account, presence: true, inclusion: %w(paid cash spent)
|
11
|
+
|
12
|
+
attr_accessible :customer, :account, :invoice, :payment, :subscription, :amount
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
module Billingly
|
2
|
-
class
|
2
|
+
class JournalEntry < ActiveRecord::Base
|
3
3
|
belongs_to :customer
|
4
4
|
belongs_to :invoice
|
5
5
|
belongs_to :payment
|
6
|
-
belongs_to :receipt
|
7
6
|
belongs_to :subscription
|
8
7
|
|
9
8
|
validates :amount, presence: true
|
10
9
|
validates :customer, presence: true
|
11
10
|
validates :account, presence: true, inclusion: %w(paid cash spent)
|
12
11
|
|
13
|
-
attr_accessible :customer, :account, :invoice, :payment, :
|
12
|
+
attr_accessible :customer, :account, :invoice, :payment, :subscription, :amount
|
14
13
|
|
15
14
|
end
|
16
15
|
end
|
@@ -12,7 +12,7 @@ module Billingly
|
|
12
12
|
# is enough to cover them.
|
13
13
|
def self.credit_for(customer, amount)
|
14
14
|
create!(amount: amount, customer: customer).tap do |payment|
|
15
|
-
customer.
|
15
|
+
customer.add_to_journal(amount, :cash, :paid, payment: payment)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -1,82 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
has_many :ledger_entries
|
4
|
-
# If a subscription is payable_upfront, then the customer effectively owes us
|
5
|
-
# since the day in which a given period starts.
|
6
|
-
# If a subscription is payable on 'due-month', then the customer effectively
|
7
|
-
# owes us money since the date in which a given period ended.
|
8
|
-
# When we invoice for a given period we will set the due_date a few days
|
9
|
-
# ahead of the date in which the debt was made effective, we call this
|
10
|
-
# a GRACE_PERIOD.
|
11
|
-
GRACE_PERIOD = 10.days
|
12
|
-
|
13
|
-
# Invoices will be generated before their due_date, as soon as possible,
|
14
|
-
# but not sooner than GENERATE_AHEAD days.
|
15
|
-
GENERATE_AHEAD = 3.days
|
16
|
-
|
17
|
-
# Subscriptions are to be charged periodically. Their periodicity is
|
18
|
-
# stored semantically on the database, but we want to convert it
|
19
|
-
# to actual ruby time ranges to do date arithmetic.
|
20
|
-
PERIODICITIES = {'monthly' => 1.month, 'yearly' => 1.year}
|
21
|
-
|
22
|
-
belongs_to :customer
|
23
|
-
has_many :invoices
|
24
|
-
|
25
|
-
validates :periodicity, inclusion: PERIODICITIES.keys
|
26
|
-
|
27
|
-
# The periodicity can be set using a symbol, for convenience.
|
28
|
-
# It's still a string under the hood.
|
29
|
-
def periodicity=(value)
|
30
|
-
self[:periodicity] = value.to_s if value
|
31
|
-
end
|
32
|
-
|
33
|
-
def period_size
|
34
|
-
case periodicity
|
35
|
-
when 'monthly' then 1.month
|
36
|
-
when 'yearly' then 1.year
|
37
|
-
else
|
38
|
-
raise ArgumentError.new 'Cannot get period size without periodicity'
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# The invoice generation process should run frequently, at least on a daily basis.
|
43
|
-
# It will create invoices some time before they are due, to give customers a chance
|
44
|
-
# to pay and settle them.
|
45
|
-
# This method is idempotent, if an upcoming invoice for a subscription already exists,
|
46
|
-
# it does not create yet another one.
|
47
|
-
def generate_next_invoice
|
48
|
-
return if terminated?
|
49
|
-
from = invoices.empty? ? subscribed_on : invoices.last.period_end
|
50
|
-
to = from + period_size
|
51
|
-
due_on = (payable_upfront ? from : to) + GRACE_PERIOD
|
52
|
-
return if GENERATE_AHEAD.from_now < from
|
53
|
-
|
54
|
-
invoice = invoices.create!(customer: customer, amount: amount,
|
55
|
-
due_on: due_on, period_start: from, period_end: to)
|
56
|
-
invoice.charge
|
57
|
-
return invoice
|
58
|
-
end
|
59
|
-
|
60
|
-
# Terminates this subscription, it could be either because we deactivate a debtor
|
61
|
-
# or because the customer decided to end his subscription on his own terms.
|
62
|
-
def terminate
|
63
|
-
return if terminated?
|
64
|
-
update_attribute(:unsubscribed_on, Time.now)
|
65
|
-
invoices.last.truncate
|
66
|
-
return self
|
67
|
-
end
|
68
|
-
|
69
|
-
def terminated?
|
70
|
-
not unsubscribed_on.nil?
|
71
|
-
end
|
72
|
-
|
73
|
-
# This class method is called from a cron job, it creates invoices for all the subscriptions
|
74
|
-
# that still need their invoice created.
|
75
|
-
# TODO: This goes through all the active subscriptions, make it smarter so that the batch job runs quicker.
|
76
|
-
def self.generate_next_invoices
|
77
|
-
where(unsubscribed_on: nil).each do |subscription|
|
78
|
-
subscription.generate_next_invoice
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
1
|
+
# @see BaseSubscription
|
2
|
+
class Billingly::Subscription < Billingly::BaseSubscription
|
82
3
|
end
|