Class: Billingly::BaseSubscription
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Billingly::BaseSubscription
- Defined in:
- app/models/billingly/base_subscription.rb
Overview
A customer will always have at least one subscription to your application. Everytime there is a change in a customer’s subscription, the current one is terminated immediately and a new one is created.
For example, changing a Plan consists on terminating the current subscription and creating a new one for the new plan. Also, a new subscription is created when customers reactivate their accounts after being deactivated.
The most recent subscription is the one currently being charged for, unless the customer is deactivated at the moment, in which case the last subscription should not be considered to be active.
Direct Known Subclasses
Constant Summary collapse
- TERMINATION_REASONS =
Subscriptions are terminated for a reason which could be:
* trial_expired: Subscription was a trial and it just expired. * debtor: The customer owed an invoice for this subscription and did not pay. * changed_subscription: This subscription was immediately replaced by another one. * left_voluntarily: This subscription was terminated because the customer left.TERMINATION_REASONS are important for auditing and for the mailing tasks to notify about subscriptions terminated automatically by the system.
%w(trial_expired debtor changed_subscription left_voluntarily)
- GENERATE_AHEAD =
Invoices will be generated before their due_date, as soon as possible, but not sooner than GENERATE_AHEAD days.
3.days
Instance Method Summary collapse
-
#generate_next_invoice ⇒ Object
The invoice generation process should run frequently, at least on a daily basis.
-
#grace_period ⇒ ActiveSupport::Duration
The grace period we use when calculating an invoices due date.
-
#notify_trial_expired ⇒ self?
When a trial subscription ends the customer is notified about it via email.
-
#plan ⇒ Billingly::Plan?
When a subscription was started from a Plan a reference to the plan is saved.
-
#subscribed_on ⇒ DateTime
The date in which this subscription started.
-
#terminate(reason) ⇒ self?
Terminates this subscription, it could be either because we deactivate a debtor or because the customer decided to end his subscription on his own terms.
-
#terminated? ⇒ Boolean
Was this subscription terminated?.
- #trial? ⇒ Boolean
-
#unsubscribed_because ⇒ DateTime
The reason why this subscription ended.
-
#unsubscribed_on ⇒ DateTime
The date in which this subscription ended.
Instance Method Details
#generate_next_invoice ⇒ Object
The invoice generation process should run frequently, at least on a daily basis. It will create invoices some time before they are due, to give customers a chance to pay and settle them. If there is any #signup_price then the first invoice will be for that amount instead of the regular amount. This method is idempotent, if an upcoming invoice for a subscription already exists, it does not create yet another one.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'app/models/billingly/base_subscription.rb', line 125 def generate_next_invoice return if terminated? return if trial? from = invoices.empty? ? subscribed_on : invoices.last.period_end to = from + periodicity due_on = (payable_upfront ? from : to) + grace_period return if GENERATE_AHEAD.from_now < from payable_amount = (invoices.empty? && signup_price) ? signup_price : amount invoice = invoices.create!(customer: customer, amount: payable_amount, due_on: due_on, period_start: from, period_end: to) invoice.charge return invoice end |
#grace_period ⇒ ActiveSupport::Duration
The grace period we use when calculating an invoices due date. If a subscription is payable_upfront, then the customer effectively owes us since the day in which a given period starts. If a subscription is payable on ‘due-month’, then the customer effectively owes us money since the date in which a given period ended. When we invoice for a given period we will set the due_date a few days ahead of the date in which the debt was made effective, we call this a grace_period.
70 |
# File 'app/models/billingly/base_subscription.rb', line 70 has_duration :grace_period |
#notify_trial_expired ⇒ self?
When a trial subscription ends the customer is notified about it via email.
168 169 170 171 172 173 174 175 176 |
# File 'app/models/billingly/base_subscription.rb', line 168 def notify_trial_expired return unless trial? return unless terminated? && unsubscribed_because == 'trial_expired' return unless notified_trial_expired_on.nil? return if customer.do_not_email? Billingly::Mailer.trial_expired_notification(self).deliver! update_attribute(:notified_trial_expired_on, Time.now) return self end |
#plan ⇒ Billingly::Plan?
When a subscription was started from a Plan a reference to the plan is saved. Although, all the plan’s fields are denormalized in this subscription.
If the subscription was not started from a plan, then this will be nil.
89 |
# File 'app/models/billingly/base_subscription.rb', line 89 belongs_to :plan |
#subscribed_on ⇒ DateTime
The date in which this subscription started. This subscription’s first invoice will have it’s period_start date matching the date in which the subscription started.
27 |
# File 'app/models/billingly/base_subscription.rb', line 27 validates :subscribed_on, presence: true |
#terminate(reason) ⇒ self?
Terminates this subscription, it could be either because we deactivate a debtor or because the customer decided to end his subscription on his own terms.
Use the shortcuts:
{#terminate_left_voluntarily}, {#terminate_trial_expired},
{#terminate_debtor}, {#terminate_changed_subscription}
Once terminated, a subscription cannot be re-open, just create a new one.
151 152 153 154 155 156 157 158 |
# File 'app/models/billingly/base_subscription.rb', line 151 def terminate(reason) return if terminated? self.unsubscribed_on = Time.now self.unsubscribed_because = reason invoices.last.truncate unless trial? save! return self end |
#terminated? ⇒ Boolean
Was this subscription terminated?
56 57 58 |
# File 'app/models/billingly/base_subscription.rb', line 56 def terminated? not unsubscribed_on.nil? end |
#trial? ⇒ Boolean
94 95 96 |
# File 'app/models/billingly/base_subscription.rb', line 94 def trial? not is_trial_expiring_on.nil? end |
#unsubscribed_because ⇒ DateTime
The reason why this subscription ended.
Every ended subscription ended for a reason, look at TERMINATION_REASONS.
51 |
# File 'app/models/billingly/base_subscription.rb', line 51 validates :unsubscribed_because, inclusion: TERMINATION_REASONS, if: :terminated? |
#unsubscribed_on ⇒ DateTime
The date in which this subscription ended.
Every ended subscription ended for a reason, look at TERMINATION_REASONS.
44 |
# File 'app/models/billingly/base_subscription.rb', line 44 validates :unsubscribed_on, presence: true, if: :unsubscribed_because |