billingly 0.1.1 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Billingly
2
2
 
3
+ ![Travis Build Status](https://secure.travis-ci.org/nubis/billingly.png)
4
+
3
5
  Billingly is a rails 3 engine that manages paid subscriptions and free trials to your web application.
4
6
 
5
7
  Billingly can:
@@ -1,5 +1,8 @@
1
- class BillinglyMailer < ActionMailer::Base
1
+ class Billingly::BaseMailer < ActionMailer::Base
2
2
  default from: 'example@example.com'
3
+
4
+ cattr_accessor :admin_emails
5
+ self.admin_emails = 'admin@example.com'
3
6
 
4
7
  def pending_notification(invoice)
5
8
  @invoice = invoice
@@ -17,4 +20,16 @@ class BillinglyMailer < ActionMailer::Base
17
20
  @invoice = invoice
18
21
  mail(to: invoice.customer.email, subject: I18n.t('billingly.payment_receipt'))
19
22
  end
23
+
24
+ def task_results(runner)
25
+ @runner = runner
26
+ mail to: self.class.admin_emails, subject: "Your Billingly Status Report"
27
+ end
28
+
29
+ # Sends the email about an expired trial.
30
+ # param trial [Subscription] a trial which should be expired.
31
+ def trial_expired_notification(subscription)
32
+ @subscription = subscription
33
+ mail to: subscription.customer.email, subject: I18n.t('billingly.your_trial_has_expired')
34
+ end
20
35
  end
@@ -0,0 +1,3 @@
1
+ # (see Billingly::BaseMailer)
2
+ class Billingly::Mailer < Billingly::BaseMailer
3
+ end
@@ -21,7 +21,7 @@ module Billingly
21
21
  # we won't try to reactivate their account when we receive a payment from them.
22
22
  # The message shown to them when they reactivate will also be different depending on
23
23
  # how they left.
24
- DEACTIVATION_REASONS = [:trial_expired, :debtor, :left_voluntarily]
24
+ DEACTIVATION_REASONS = %w(trial_expired debtor left_voluntarily)
25
25
 
26
26
  # The Date and Time in which the Customer's account was deactivated (see {#deactivated?}).
27
27
  # This field denormalizes the date in which this customer's last subscription was ended.
@@ -101,7 +101,7 @@ module Billingly
101
101
  # @return [Integer]
102
102
  def trial_days_left
103
103
  return unless doing_trial?
104
- (active_subscription.is_trial_expiring_on.to_date - Date.today).to_i
104
+ (active_subscription.is_trial_expiring_on.to_date - Time.now.utc.to_date).to_i
105
105
  end
106
106
 
107
107
  # Customers subscribe to the service under certain conditions referred to as a {Plan},
@@ -115,7 +115,7 @@ module Billingly
115
115
  # @param [Plan, Subscription]
116
116
  # @return [Subscription] The newly created {Subscription}
117
117
  def subscribe_to_plan(plan, is_trial_expiring_on = nil)
118
- subscriptions.last.terminate if subscriptions.last
118
+ subscriptions.last.terminate_changed_subscription if subscriptions.last
119
119
 
120
120
  subscriptions.build.tap do |new|
121
121
  [:payable_upfront, :description, :periodicity,
@@ -173,12 +173,6 @@ module Billingly
173
173
  end
174
174
  end
175
175
 
176
- # This method will deactivate all customers who have overdue {Invoice Invoices}.
177
- # It's run periodically through Billingly's Rake Task.
178
- def self.deactivate_all_debtors
179
- debtors.where(deactivated_since: nil).all.each{|debtor| debtor.deactivate_debtor }
180
- end
181
-
182
176
  # A customer who has overdue invoices at the time of asking this question is
183
177
  # considered a debtor.
184
178
  #
@@ -210,8 +204,8 @@ module Billingly
210
204
  # @param amount [BigDecimal, float] the amount to be credited.
211
205
  def credit_payment(amount)
212
206
  Billingly::Payment.credit_for(self, amount)
213
- Billingly::Invoice.charge_all(self.invoices)
214
- reactivate if deactivated? && deactivation_reason == :debtor
207
+ charge_pending_invoices
208
+ reactivate if deactivated? && deactivation_reason == 'debtor'
215
209
  end
216
210
 
217
211
  # Terminate a customer's subscription to the service.
@@ -226,10 +220,10 @@ module Billingly
226
220
  # @return [self, nil] nil if the account was already deactivated, self otherwise.
227
221
  def deactivate(reason)
228
222
  return if deactivated?
229
- active_subscription.terminate
223
+ active_subscription.terminate(reason)
230
224
  self.deactivated_since = Time.now
231
225
  self.deactivation_reason = reason
232
- self.save!
226
+ save!
233
227
  return self
234
228
  end
235
229
 
@@ -241,17 +235,17 @@ module Billingly
241
235
 
242
236
  # @see #deactivate
243
237
  def deactivate_left_voluntarily
244
- deactivate(:left_voluntarily)
238
+ deactivate('left_voluntarily')
245
239
  end
246
240
 
247
241
  # @see #deactivate
248
242
  def deactivate_trial_expired
249
- deactivate(:trial_expired)
243
+ deactivate('trial_expired')
250
244
  end
251
245
 
252
246
  # @see #deactivate
253
247
  def deactivate_debtor
254
- deactivate(:debtor)
248
+ deactivate('debtor')
255
249
  end
256
250
 
257
251
  # Customers whose account has been {#deactivate deactivated} can always re-join the service
@@ -266,25 +260,18 @@ module Billingly
266
260
  subscribe_to_plan(new_plan)
267
261
  return self
268
262
  end
269
-
270
- # Customers may be subscribed for a trial period, and they are supposed to re-subscribe
271
- # before their trial expires.
272
- #
273
- # When their trial expires and they have not yet subscribed to another plan, we
274
- # deactivate their account immediately.
263
+
264
+ # Charges all invoices for which the customer has enough balance.
265
+ # Oldest invoices are charged first, newer invoices should not be charged until
266
+ # the oldest ones are paid.
275
267
  #
276
- # This method will deactivate all customers whose trial has expired.
277
- # It's run periodically through Billingly's Rake Task.
278
- def self.deactivate_all_expired_trials
279
- customers = joins(:subscriptions).readonly(false)
280
- .where("#{Billingly::Subscription.table_name}.is_trial_expiring_on < ?", Time.now)
281
- .where(billingly_subscriptions: {unsubscribed_on: nil})
282
-
283
- customers.each do |customer|
284
- customer.deactivate_trial_expired
285
- end
268
+ # See {Billingly::Invoice#charge Invoice#charge} for more information
269
+ # on how invoices are charged from the customer's balance.
270
+ def charge_pending_invoices
271
+ invoices.where(deleted_on: nil, paid_on: nil).order('period_start')
272
+ .each{|invoice| break unless invoice.charge}
286
273
  end
287
-
274
+
288
275
  # Can this customer subscribe to a plan?.
289
276
  # You may want to prevent customers from upgrading or downgrading to other plans
290
277
  # depending on their usage of your service.
@@ -25,7 +25,38 @@ module Billingly
25
25
  # @property subscribed_on
26
26
  # @return [DateTime]
27
27
  validates :subscribed_on, presence: true
28
+
29
+ # Subscriptions are terminated for a reason which could be:
30
+ # * trial_expired: Subscription was a trial and it just expired.
31
+ # * debtor: The customer owed an invoice for this subscription and did not pay.
32
+ # * changed_subscription: This subscription was immediately replaced by another one.
33
+ # * left_voluntarily: This subscription was terminated because the customer left.
34
+ #
35
+ # TERMINATION_REASONS are important for auditing and for the mailing tasks to notify
36
+ # about subscriptions terminated automatically by the system.
37
+ TERMINATION_REASONS = %w(trial_expired debtor changed_subscription left_voluntarily)
38
+
39
+ # The date in which this subscription ended.
40
+ #
41
+ # Every ended subscription ended for a reason, look at {TERMINATION_REASONS}.
42
+ # @property unsubscribed_on
43
+ # @return [DateTime]
44
+ validates :unsubscribed_on, presence: true, if: :unsubscribed_because
28
45
 
46
+ # The reason why this subscription ended.
47
+ #
48
+ # Every ended subscription ended for a reason, look at {TERMINATION_REASONS}.
49
+ # @property unsubscribed_because
50
+ # @return [DateTime]
51
+ validates :unsubscribed_because, inclusion: TERMINATION_REASONS, if: :terminated?
52
+
53
+ # Was this subscription terminated?
54
+ # @property [r] terminated?
55
+ # @return [Boolean] Whether the subscription was terminated or not.
56
+ def terminated?
57
+ not unsubscribed_on.nil?
58
+ end
59
+
29
60
  # The grace period we use when calculating an invoices due date.
30
61
  # If a subscription is payable_upfront, then the customer effectively owes us
31
62
  # since the day in which a given period starts.
@@ -58,7 +89,7 @@ module Billingly
58
89
  belongs_to :plan
59
90
 
60
91
  # (see #is_trial_expiring_on)
61
- # @property trial?
92
+ # @property [r] trial?
62
93
  # @return [Boolean]
63
94
  def trial?
64
95
  not is_trial_expiring_on.nil?
@@ -95,24 +126,38 @@ module Billingly
95
126
 
96
127
  # Terminates this subscription, it could be either because we deactivate a debtor
97
128
  # or because the customer decided to end his subscription on his own terms.
98
- def terminate
129
+ #
130
+ # Use the shortcuts:
131
+ # {#terminate_left_voluntarily}, {#terminate_trial_expired},
132
+ # {#terminate_debtor}, {#terminate_changed_subscription}
133
+ #
134
+ # Once terminated, a subscription cannot be re-open, just create a new one.
135
+ # @param reason [Symbol] the reason to terminate this subscription, see {TERMINATION_REASONS}
136
+ # @return [self, nil] nil if the account was already terminated, self otherwise.
137
+ def terminate(reason)
99
138
  return if terminated?
100
- update_attribute(:unsubscribed_on, Time.now)
139
+ self.unsubscribed_on = Time.now
140
+ self.unsubscribed_because = reason
101
141
  invoices.last.truncate unless trial?
142
+ save!
102
143
  return self
103
144
  end
104
-
105
- def terminated?
106
- not unsubscribed_on.nil?
145
+
146
+ TERMINATION_REASONS.each do |reason|
147
+ define_method("terminate_#{reason}") do
148
+ terminate(reason)
149
+ end
107
150
  end
108
151
 
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
152
+ # When a trial subscription ends the customer is notified about it via email.
153
+ # @return [self, nil] not nil means the notification was sent successfully.
154
+ def notify_trial_expired
155
+ return unless trial?
156
+ return unless terminated? && unsubscribed_because == 'trial_expired'
157
+ return unless notified_trial_expired_on.nil?
158
+ Billingly::Mailer.trial_expired_notification(self).deliver!
159
+ update_attribute(:notified_trial_expired_on, Time.now)
160
+ return self
116
161
  end
117
162
  end
118
163
  end
@@ -55,64 +55,29 @@ module Billingly
55
55
  return self
56
56
  end
57
57
 
58
- # Charges all invoices that can be charged from the existing customer cash balances
59
- def self.charge_all(collection = self)
60
- collection.where(deleted_on: nil, paid_on: nil).order('period_start').each do |invoice|
61
- invoice.charge
62
- end
63
- end
64
-
65
- # This method is called by Billingly's recurring task to notify all pending invoices.
66
- def self.notify_all_pending
67
- where(deleted_on: nil, paid_on: nil, notified_pending_on: nil)
68
- .each do |invoice|
69
- invoice.notify_pending
70
- end
71
- end
72
-
73
58
  def notify_pending
74
59
  return unless notified_pending_on.nil?
75
60
  return if paid?
76
61
  return if deleted?
77
62
  return if due_on > subscription.grace_period.from_now
78
- BillinglyMailer.pending_notification(self).deliver!
63
+ Billingly::Mailer.pending_notification(self).deliver!
79
64
  update_attribute(:notified_pending_on, Time.now)
80
65
  end
81
66
 
82
- # Send the email notifying that this invoice being overdue and the subscription
83
- # being cancelled
84
- def self.notify_all_overdue
85
- where('due_on <= ?', Time.now)
86
- .where(deleted_on: nil, paid_on: nil, notified_overdue_on: nil)
87
- .each do |invoice|
88
- invoice.notify_overdue
89
- end
90
- end
91
-
92
67
  def notify_overdue
93
68
  return unless notified_overdue_on.nil?
94
69
  return if paid?
95
70
  return if deleted?
96
71
  return if due_on > Time.now
97
- BillinglyMailer.overdue_notification(self).deliver!
72
+ Billingly::Mailer.overdue_notification(self).deliver!
98
73
  update_attribute(:notified_overdue_on, Time.now)
99
74
  end
100
75
 
101
- # Notifies that the invoice has been charged successfully.
102
- # Send the email notifying that this invoice being overdue and the subscription
103
- # being cancelled
104
- def self.notify_all_paid
105
- where('paid_on is not null')
106
- .where(deleted_on: nil, notified_paid_on: nil).each do |invoice|
107
- invoice.notify_paid
108
- end
109
- end
110
-
111
76
  def notify_paid
112
77
  return unless paid?
113
78
  return unless notified_paid_on.nil?
114
79
  return if deleted?
115
- BillinglyMailer.paid_notification(self).deliver!
80
+ Billingly::Mailer.paid_notification(self).deliver!
116
81
  update_attribute(:notified_paid_on, Time.now)
117
82
  end
118
83
  end
@@ -0,0 +1,200 @@
1
+ # The Tasks model has all the tasks that should be run periodically through rake.
2
+ # A special log is created for the tasks being run and the results are reported
3
+ # back to the website administrator.
4
+ class Billingly::Tasks
5
+ # The date in which the tasks started running.
6
+ # @!attribute started
7
+ # @return [DateTime]
8
+ attr_accessor :started
9
+
10
+ # The date in which the tasks ended
11
+ # @!attribute ended
12
+ # @return [DateTime]
13
+ attr_accessor :ended
14
+
15
+ # A summary of all the tasks that were run and their overall results.
16
+ # @!attribute summary
17
+ # @return [String]
18
+ attr_accessor :summary
19
+
20
+ # A detailed description of errors that ocurred while running all the tasks.
21
+ #
22
+ # @!attribute extended
23
+ # @return [File]
24
+ attr_accessor :extended
25
+
26
+ # Runs all of Billingly's periodic tasks and creates a report with the results at the end.
27
+ def run_all
28
+ self.started = Time.now
29
+
30
+ generate_next_invoices
31
+ charge_invoices
32
+ deactivate_all_debtors
33
+ deactivate_all_expired_trials
34
+ notify_all_paid
35
+ notify_all_pending
36
+ notify_all_overdue
37
+
38
+ self.ended = Time.now
39
+ self.extended.close unless self.extended.nil?
40
+ Billingly::Mailer.task_results(self).deliver
41
+ end
42
+
43
+ # Writes a line to the {#extended} section of this tasks results report.
44
+ # @param text [String]
45
+ def log_extended(text)
46
+ if self.extended.nil?
47
+ time = Time.now.utc.strftime("%Y%m%d%H%M%S")
48
+ self.extended = File.open("#{Rails.root}/log/billingly_#{time}.log", 'w')
49
+ end
50
+ self.extended.write("#{text}\n\n")
51
+ end
52
+
53
+ # Writes a line to the {#summary} section of this task results report.
54
+ # @param text [String]
55
+ def log_summary(text)
56
+ self.summary ||= ''
57
+ self.summary += "#{text}\n"
58
+ end
59
+
60
+ # The batch runner is a helper function for running a method on each item of a
61
+ # collection logging the results, without aborting excecution if calling the rest of the
62
+ # items if any of them fails.
63
+ #
64
+ # The method called on each item will not receive parameters and should return
65
+ # a Truthy value if successfull, or raise an exception otherwise.
66
+ # Returning nil means that there was nothing to be done on that item.
67
+ #
68
+ # The collection to be used should be returned by a block provided to this method.
69
+ # Any problem fetching the collection will also be universally captured
70
+ #
71
+ # See {#generate_next_invoices} for an example use.
72
+ #
73
+ # @param task_name [String] the name to use for this task in the generated log.
74
+ # @param method [Symbol] the method to call on each one of the given items.
75
+ # @param collection_getter [Proc] a block which should return the collection to use.
76
+ def batch_runner(task_name, method, &collection_getter)
77
+ collection = begin
78
+ collection_getter.call
79
+ rescue Exception => e
80
+ failure += 1
81
+ log_extended("#{task_name}:\nCollection getter failed\n#{e.message}\n\n#{e.backtrace}")
82
+ return
83
+ end
84
+
85
+ success = 0
86
+ failure = 0
87
+
88
+ collection.each do |item|
89
+ begin
90
+ success += 1 if item.send(method)
91
+ rescue Exception => e
92
+ failure += 1
93
+ log_extended("#{task_name}:\n#{e.message}\n#{item.inspect}\n\n#{e.backtrace}")
94
+ end
95
+ end
96
+
97
+ if failure == 0
98
+ log_summary("Success: #{task_name}, #{success} OK.")
99
+ else
100
+ log_summary("Failure: #{task_name}, #{success} OK, #{failure} failed.")
101
+ end
102
+ end
103
+
104
+ # Invoices for running subscriptions which are not trials are generated by this task.
105
+ # See {Billingly::Subscription#generate_next_invoice} for more information about
106
+ # how the next invoice for a subscription is created.
107
+ def generate_next_invoices
108
+ batch_runner('Generating Invoices', :generate_next_invoice) do
109
+ Billingly::Subscription
110
+ .where(is_trial_expiring_on: nil, unsubscribed_on: nil)
111
+ .readonly(false)
112
+ end
113
+ end
114
+
115
+ # Charges all invoices for which the customer has enough balance.
116
+ # Oldest invoices are charged first, newer invoices should not be charged until
117
+ # the oldest ones are paid.
118
+ # See {Billingly::Invoice#charge Invoice#Charge} for more information on
119
+ # how invoices are charged from the customer's balance.
120
+ # @param collection [Array<Invoice>] The list of invoices to attempt charging.
121
+ # Defaults to all invoices in the system.
122
+ def charge_invoices
123
+ batch_runner('Charging pending invoices', :charge_pending_invoices) do
124
+ Billingly::Customer
125
+ .joins(:invoices)
126
+ .where(billingly_invoices: {deleted_on: nil, paid_on: nil})
127
+ .readonly(false)
128
+ end
129
+ end
130
+
131
+ # Notifies invoices that have been charged successfully, sending a receipt.
132
+ # See {Billingly::Invoice#notify_paid Invoice#notify_paid} for more information on
133
+ # how receipts are sent for paid invoices.
134
+ def notify_all_paid
135
+ batch_runner('Notifying Paid Invoices', :notify_paid) do
136
+ Billingly::Invoice
137
+ .where('paid_on is not null')
138
+ .where(deleted_on: nil, notified_paid_on: nil)
139
+ .readonly(false)
140
+ end
141
+ end
142
+
143
+ # Customers are notified about their pending invoices by this task.
144
+ # See {Billingly::Invoice#notify_pending Invoice#notify_pending} for more info
145
+ # on how pending invoices are notified.
146
+ def notify_all_pending
147
+ batch_runner('Notifying Pending Invoices', :notify_pending) do
148
+ Billingly::Invoice
149
+ .where(deleted_on: nil, paid_on: nil, notified_pending_on: nil)
150
+ .readonly(false)
151
+ end
152
+ end
153
+
154
+ # This task notifies customers when one of their invoices is overdue.
155
+ # Overdue invoices go together with account deactivations so the email sent
156
+ # by this task also includes the deactivation notice.
157
+ #
158
+ # This task does not perform the actual deactivation, {#deactivate_all_debtors} does.
159
+ #
160
+ # See {Billingly::Invoice#notify_overdue Invoice#notify_overdue} for more info
161
+ # on how overdue invoices are notified.
162
+ def notify_all_overdue
163
+ batch_runner('Notifying Pending Invoices', :notify_overdue) do
164
+ Billingly::Invoice
165
+ .where('due_on <= ?', Time.now)
166
+ .where(deleted_on: nil, paid_on: nil, notified_overdue_on: nil)
167
+ .readonly(false)
168
+ end
169
+ end
170
+
171
+ # This method will deactivate all customers who have overdue {Billingly::Invoice Invoices}.
172
+ #
173
+ # This only deactivates the debtor, it does not notify them via email.
174
+ # Look at {#notify_all_overdue} to see the email notification customers receive.
175
+ #
176
+ # See {Billingly::Customer#deactivate_debtor Customer#deactivate_debtor} for more info
177
+ # on how debtors are deactivated.
178
+ def deactivate_all_debtors
179
+ batch_runner('Deactivating Debtors', :deactivate_debtor) do
180
+ Billingly::Customer.debtors.where(deactivated_since: nil).readonly(false)
181
+ end
182
+ end
183
+
184
+ # Customers may be subscribed for a trial period, and they are supposed to re-subscribe
185
+ # before their trial expires.
186
+ #
187
+ # When their trial expires and they have not yet subscribed to another plan, we
188
+ # deactivate their account immediately. This method does not email them about the
189
+ # expired trial.
190
+ #
191
+ # See {Billingly::Customer#deactivate_trial_expired Customer#deactivate_trial_expired}
192
+ # for more info on how trials are deactivated.
193
+ def deactivate_all_expired_trials
194
+ batch_runner('Deactivating Expired Trials', :deactivate_trial_expired) do
195
+ Billingly::Customer.joins(:subscriptions).readonly(false)
196
+ .where("#{Billingly::Subscription.table_name}.is_trial_expiring_on < ?", Time.now)
197
+ .where(billingly_subscriptions: {unsubscribed_on: nil})
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,9 @@
1
+ This is a report on Billingly's activity.
2
+ Started on "<%= @runner.started %>" and ended on "<%= @runner.ended %>"
3
+
4
+ Summary:
5
+ <%= @runner.summary %>:
6
+
7
+ Extended description:
8
+ <%= @runner.extended %>:
9
+
@@ -0,0 +1,4 @@
1
+ <%= t( 'billingly.your_trial_has_expired_please_choose_a_plan') % { end_date: @subscription.unsubscribed_on.to_date} %>
2
+
3
+ <%=t 'billingly.please_visit' %> <%= url_for subscriptions_path %>
4
+
@@ -21,3 +21,8 @@ en:
21
21
  paid_on: Paid on
22
22
  plan: Plan
23
23
  amount: Amount
24
+ your_trial_has_expired: Your trial period has expired
25
+ your_trial_has_expired_please_choose_a_plan:
26
+ "Your trial period has ended on %{end_date} please choose a plan and sign up again."
27
+ please_visit: Please visit
28
+
@@ -1,3 +1,3 @@
1
1
  module Billingly
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -46,7 +46,10 @@ class CreateBillinglyTables < ActiveRecord::Migration
46
46
  t.boolean 'payable_upfront', null: false, default: false
47
47
  t.decimal 'amount', precision: 11, scale: 2, default: 0.0, null: false
48
48
  t.datetime 'unsubscribed_on'
49
+ t.string 'unsubscribed_because'
49
50
  t.datetime 'is_trial_expiring_on'
51
+ t.datetime 'notified_trial_will_expire_on'
52
+ t.datetime 'notified_trial_expired_on'
50
53
  t.references :plan
51
54
  t.timestamps
52
55
  end
@@ -5,19 +5,6 @@ You can run it as often as you want.
5
5
  """
6
6
  namespace :billingly do
7
7
  task all: :environment do
8
- puts 'Generating invoices'
9
- Billingly::Subscription.generate_next_invoices
10
- puts 'Charging invoices if possible'
11
- Billingly::Invoice.charge_all
12
- puts 'Deactivating debtors'
13
- Billingly::Customer.deactivate_all_debtors
14
- puts 'Deactivating all expired trials'
15
- Billingly::Customer.deactivate_all_expired_trials
16
- puts 'Sending payment receipts'
17
- Billingly::Invoice.notify_all_paid
18
- puts 'Notifying pending invoices'
19
- Billingly::Invoice.notify_all_pending
20
- puts 'Notifying deactivated debtors'
21
- Billingly::Invoice.notify_all_overdue
8
+ Billingly::Tasks.new.run_all
22
9
  end
23
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: billingly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-04 00:00:00.000000000 Z
12
+ date: 2012-10-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70322875786160 !ruby/object:Gem::Requirement
16
+ requirement: &70310037569740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.2.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70322875786160
24
+ version_requirements: *70310037569740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: validates_email_format_of
27
- requirement: &70322875785740 !ruby/object:Gem::Requirement
27
+ requirement: &70310037569280 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70322875785740
35
+ version_requirements: *70310037569280
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: has_duration
38
- requirement: &70322875785280 !ruby/object:Gem::Requirement
38
+ requirement: &70310037568740 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70322875785280
46
+ version_requirements: *70310037568740
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: timecop
49
- requirement: &70322875784860 !ruby/object:Gem::Requirement
49
+ requirement: &70310037568220 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70322875784860
57
+ version_requirements: *70310037568220
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: sqlite3
60
- requirement: &70322875784440 !ruby/object:Gem::Requirement
60
+ requirement: &70310037608420 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70322875784440
68
+ version_requirements: *70310037608420
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec-rails
71
- requirement: &70322875784020 !ruby/object:Gem::Requirement
71
+ requirement: &70310037607540 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70322875784020
79
+ version_requirements: *70310037607540
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: factory_girl_rails
82
- requirement: &70322875783600 !ruby/object:Gem::Requirement
82
+ requirement: &70310037607120 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70322875783600
90
+ version_requirements: *70310037607120
91
91
  description: Rails Engine for SaaS subscription management. Manage subscriptions,
92
92
  plan changes, free trials and more!!!
93
93
  email:
@@ -97,7 +97,8 @@ extensions: []
97
97
  extra_rdoc_files: []
98
98
  files:
99
99
  - app/controllers/billingly/subscriptions_controller.rb
100
- - app/mailers/billingly_mailer.rb
100
+ - app/mailers/billingly/base_mailer.rb
101
+ - app/mailers/billingly/mailer.rb
101
102
  - app/models/billingly/base_customer.rb
102
103
  - app/models/billingly/base_plan.rb
103
104
  - app/models/billingly/base_subscription.rb
@@ -111,6 +112,14 @@ files:
111
112
  - app/models/billingly/plan.rb
112
113
  - app/models/billingly/receipt.rb
113
114
  - app/models/billingly/subscription.rb
115
+ - app/models/billingly/tasks.rb
116
+ - app/views/billingly/mailer/overdue_notification.html.erb
117
+ - app/views/billingly/mailer/paid_notification.html.erb
118
+ - app/views/billingly/mailer/pending_notification.html.erb
119
+ - app/views/billingly/mailer/pending_notification.plain.erb
120
+ - app/views/billingly/mailer/pending_notification.text.erb
121
+ - app/views/billingly/mailer/task_results.text.erb
122
+ - app/views/billingly/mailer/trial_expired_notification.text.erb
114
123
  - app/views/billingly/subscriptions/_current_subscription.html.haml
115
124
  - app/views/billingly/subscriptions/_deactivation_notice.html.haml
116
125
  - app/views/billingly/subscriptions/_invoice_details.html.haml
@@ -120,11 +129,6 @@ files:
120
129
  - app/views/billingly/subscriptions/index.html.haml
121
130
  - app/views/billingly/subscriptions/invoice.html.haml
122
131
  - app/views/billingly/subscriptions/new.html.erb
123
- - app/views/billingly_mailer/overdue_notification.html.erb
124
- - app/views/billingly_mailer/paid_notification.html.erb
125
- - app/views/billingly_mailer/pending_notification.html.erb
126
- - app/views/billingly_mailer/pending_notification.plain.erb
127
- - app/views/billingly_mailer/pending_notification.text.erb
128
132
  - config/locales/en.yml
129
133
  - lib/billingly/engine.rb
130
134
  - lib/billingly/rails/routes.rb
@@ -141,7 +145,21 @@ files:
141
145
  - TUTORIAL.rdoc
142
146
  homepage: http://billing.ly
143
147
  licenses: []
144
- post_install_message:
148
+ post_install_message: ! " Add the following migration:\n \n class CreateBillinglyTables
149
+ < ActiveRecord::Migration\n def up\n add_column :billingly_subscriptions,
150
+ :notified_trial_will_expire_on, :datetime\n add_column :billingly_subscriptions,
151
+ :notified_trial_expired_on, :datetime\n add_column :billingly_subscriptions,
152
+ :unsubscribed_because, :string\n \n Billingly::Subscription.where('unsubscribed_on
153
+ IS NOT NULL').find_each do |s|\n # Notice: You should pre-populate unsubscribed
154
+ because\n # with an appropriate value for each terminated subscription.\n
155
+ \ # \n reason = if s == s.customer.subscriptions.last && s.customer.deactivation_reason\n
156
+ \ s.deactivation_reason\n elsif s.trial? && s.is_trial_expiring_on
157
+ <= s.unsubscribed_on\n 'trial_expired'\n else\n 'changed_subscription'\n
158
+ \ end\n s.update_attribute(unsubscribed_because: reason)\n end\n
159
+ \ end\n \n def down\n remove_column :billingly_subscriptions,
160
+ :notified_trial_will_expire_on\n remove_column :billingly_subscriptions,
161
+ :notified_trial_expired_on\n remove_column :billingly_subscriptions, :unsubscribed_because\n
162
+ \ end\n end\n"
145
163
  rdoc_options: []
146
164
  require_paths:
147
165
  - lib