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