def test_create_subscription_trial_with_wrong_dates(self): subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.trial_period_end = nowdate() subscription.trial_period_start = add_days(nowdate(), 30) subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) self.assertRaises(dataent.ValidationError, subscription.save) subscription.delete()
def restart_subscription(self): """ This sets the subscription as active. The subscription will be made to be like a new subscription and the `Subscription` will lose all the history of generated invoices it has. """ if self.status == 'Cancelled': self.status = 'Active' self.db_set('start', nowdate()) self.update_subscription_period(nowdate()) self.invoices = [] self.save() else: dataent.throw( _('You cannot restart a Subscription that is not cancelled.'))
def cancel_subscription_at_period_end(self): """ Called when `Subscription.cancel_at_period_end` is truthy """ self.status = 'Cancelled' if not self.cancelation_date: self.cancelation_date = nowdate()
def test_prepaid_subscriptions_with_prorate_true(self): settings = dataent.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 settings.save() subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.generate_invoice_at_period_start = True subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() self.assertEqual(len(subscription.invoices), 1) current_inv = subscription.get_current_invoice() self.assertEqual(current_inv.status, "Unpaid") diff = flt( date_diff(nowdate(), subscription.current_invoice_start) + 1) plan_days = flt( date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1) prorate_factor = flt(diff / plan_days) self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2)) settings.prorate = to_prorate settings.save() subscription.delete()
def test_create_subscription_without_trial_with_correct_period(self): subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertEqual(subscription.trial_period_start, None) self.assertEqual(subscription.trial_period_end, None) self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) # No invoice is created self.assertEqual(len(subscription.invoices), 0) self.assertEqual(subscription.status, 'Active') subscription.delete()
def test_subscription_cancellation_invoices_with_prorata_true(self): settings = dataent.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 settings.save() subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() invoice = subscription.get_current_invoice() diff = flt( date_diff(nowdate(), subscription.current_invoice_start) + 1) plan_days = flt( date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1) prorate_factor = flt(diff / plan_days) self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2)) settings.prorate = to_prorate settings.save() subscription.delete()
def test_create_subscription_with_trial_with_correct_period(self): subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.trial_period_start = nowdate() subscription.trial_period_end = add_days(nowdate(), 30) subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertEqual(subscription.trial_period_start, nowdate()) self.assertEqual(subscription.trial_period_end, add_days(nowdate(), 30)) self.assertEqual(subscription.trial_period_start, subscription.current_invoice_start) self.assertEqual(subscription.trial_period_end, subscription.current_invoice_end) self.assertEqual(subscription.invoices, []) self.assertEqual(subscription.status, 'Trialling') subscription.delete()
def is_prepaid_to_invoice(self): if not self.generate_invoice_at_period_start: return False if self.is_new_subscription(): return True # Check invoice dates and make sure it doesn't have outstanding invoices return getdate(nowdate()) >= getdate( self.current_invoice_start) and not self.has_outstanding_invoice()
def period_has_passed(end_date): """ Returns true if the given `end_date` has passed """ # todo: test for illegal time if not end_date: return True end_date = getdate(end_date) return getdate(nowdate()) > getdate(end_date)
def is_past_grace_period(self): """ Returns `True` if the grace period for the `Subscription` has passed """ current_invoice = self.get_current_invoice() if self.current_invoice_is_past_due(current_invoice): subscription_settings = dataent.get_single('Subscription Settings') grace_period = cint(subscription_settings.grace_period) return getdate(nowdate()) > add_days(current_invoice.due_date, grace_period)
def current_invoice_is_past_due(self, current_invoice=None): """ Returns `True` if the current generated invoice is overdue """ if not current_invoice: current_invoice = self.get_current_invoice() if not current_invoice: return False else: return getdate(nowdate()) > getdate(current_invoice.due_date)
def process_for_active(self): """ Called by `process` if the status of the `Subscription` is 'Active'. The possible outcomes of this method are: 1. Generate a new invoice 2. Change the `Subscription` status to 'Past Due Date' 3. Change the `Subscription` status to 'Cancelled' """ if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice(): self.generate_invoice() if self.current_invoice_is_past_due(): self.status = 'Past Due Date' if self.current_invoice_is_past_due() and getdate(nowdate()) > getdate( self.current_invoice_end): self.status = 'Past Due Date' if self.cancel_at_period_end and getdate(nowdate()) > getdate( self.current_invoice_end): self.cancel_subscription_at_period_end()
def set_current_invoice_start(self, date=None): """ This sets the date of the beginning of the current billing period. If the `date` parameter is not given , it will be automatically set as today's date. """ if self.trial_period_start and self.is_trialling(): self.current_invoice_start = self.trial_period_start elif date: self.current_invoice_start = date else: self.current_invoice_start = nowdate()
def test_subscription_invoice_days_until_due(self): subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.days_until_due = 10 subscription.start = add_months(nowdate(), -1) subscription.insert() subscription.process() # generate first invoice self.assertEqual(len(subscription.invoices), 1) self.assertEqual(subscription.status, 'Active') subscription.delete()
def test_subscription_remains_active_during_invoice_period(self): subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.process() # no changes expected self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) self.assertEqual(len(subscription.invoices), 0) subscription.process() # no changes expected still self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) self.assertEqual(len(subscription.invoices), 0) subscription.process() # no changes expected yet still self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) self.assertEqual(len(subscription.invoices), 0) subscription.delete()
def cancel_subscription(self): """ This sets the subscription as cancelled. It will stop invoices from being generated but it will not affect already created invoices. """ if self.status != 'Cancelled': to_generate_invoice = True if self.status == 'Active' else False to_prorate = dataent.db.get_single_value('Subscription Settings', 'prorate') self.status = 'Cancelled' self.cancelation_date = nowdate() if to_generate_invoice: self.generate_invoice(prorate=to_prorate) self.save()
def test_subscription_cancellation_invoices(self): settings = dataent.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 settings.save() subscription = dataent.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertEqual(subscription.status, 'Active') subscription.cancel_subscription() # Invoice must have been generated self.assertEqual(len(subscription.invoices), 1) invoice = subscription.get_current_invoice() diff = flt( date_diff(nowdate(), subscription.current_invoice_start) + 1) plan_days = flt( date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1) prorate_factor = flt(diff / plan_days) self.assertEqual( flt( get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start), 2), flt(prorate_factor, 2)) self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2)) self.assertEqual(subscription.status, 'Cancelled') subscription.delete() settings.prorate = to_prorate settings.save()
def get_prorata_factor(period_end, period_start): diff = flt(date_diff(nowdate(), period_start) + 1) plan_days = flt(date_diff(period_end, period_start) + 1) prorate_factor = diff / plan_days return prorate_factor
def is_postpaid_to_invoice(self): return getdate(nowdate()) > getdate(self.current_invoice_end) or \ (getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \ not self.has_outstanding_invoice()