From b3d5777e55566cdc4009ec4c2619459fef0fb5a8 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 28 Feb 2018 12:11:19 +0100 Subject: [PATCH] more test cases and bug fixes --- .../doctype/subscriptions/subscriptions.py | 60 ++++++---- .../subscriptions/test_subscriptions.py | 104 +++++++++++++++--- 2 files changed, 126 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/subscriptions/subscriptions.py b/erpnext/accounts/doctype/subscriptions/subscriptions.py index e5c2cc55df..9a130f3360 100644 --- a/erpnext/accounts/doctype/subscriptions/subscriptions.py +++ b/erpnext/accounts/doctype/subscriptions/subscriptions.py @@ -13,9 +13,6 @@ SUBSCRIPTION_SETTINGS = frappe.get_single('Subscription Settings') class Subscriptions(Document): - def before_save(self): - self.set_status() - def before_insert(self): # update start just before the subscription doc is created self.update_subscription_period() @@ -29,6 +26,8 @@ class Subscriptions(Document): self.current_invoice_start = self.trial_period_start elif not date: self.current_invoice_start = nowdate() + elif date: + self.current_invoice_start = date def set_current_invoice_end(self): if self.is_trialling(): @@ -81,15 +80,19 @@ class Subscriptions(Document): return data - def set_status(self): + def set_status_grace_period(self): + if self.status == 'Past Due Date' and self.is_past_grace_period(): + self.status = 'Canceled' if cint(SUBSCRIPTION_SETTINGS.cancel_after_grace) else 'Unpaid' + + def set_subscription_status(self): if self.is_trialling(): self.status = 'Trialling' - elif self.status == 'Past Due' and self.is_past_grace_period(): + elif self.status == 'Past Due Date' and self.is_past_grace_period(): self.status = 'Canceled' if cint(SUBSCRIPTION_SETTINGS.cancel_after_grace) else 'Unpaid' - elif self.status == 'Past Due' and not self.has_outstanding_invoice(): + elif self.status == 'Past Due Date' and not self.has_outstanding_invoice(): self.status = 'Active' elif self.current_invoice_is_past_due(): - self.status = 'Past Due' + self.status = 'Past Due Date' elif self.is_new_subscription(): self.status = 'Active' # todo: then generate new invoice @@ -110,7 +113,7 @@ class Subscriptions(Document): if self.current_invoice_is_past_due(current_invoice): grace_period = cint(SUBSCRIPTION_SETTINGS.grace_period) - return nowdate() > add_days(current_invoice.due_date, grace_period) + return getdate(nowdate()) > add_days(current_invoice.due_date, grace_period) def current_invoice_is_past_due(self, current_invoice=None): if not current_invoice: @@ -124,8 +127,11 @@ class Subscriptions(Document): def get_current_invoice(self): if len(self.invoices): current = self.invoices[-1] - doc = frappe.get_doc('Sales Invoice', current.invoice) - return doc + if frappe.db.exists('Sales Invoice', current.invoice): + doc = frappe.get_doc('Sales Invoice', current.invoice) + return doc + else: + frappe.throw(_('Invoice {0} no longer exists'.format(invoice.invoice))) def is_new_subscription(self): return len(self.invoices) == 0 @@ -144,7 +150,7 @@ class Subscriptions(Document): def after_insert(self): # todo: deal with users who collect prepayments. Maybe a new Subscription Invoice doctype? - pass + self.set_subscription_status() def generate_invoice(self): invoice = self.create_invoice() @@ -156,6 +162,8 @@ class Subscriptions(Document): def create_invoice(self): invoice = frappe.new_doc('Sales Invoice') + invoice.set_posting_time = 1 + invoice.posting_date = self.current_invoice_start invoice.customer = self.get_customer(self.subscriber) # Subscription is better suited for service items. I won't update `update_stock` @@ -220,28 +228,36 @@ class Subscriptions(Document): """ if self.status == 'Active': self.process_for_active() - elif self.status == 'Past Due': + elif self.status == 'Past Due Date': self.process_for_past_due_date() + self.save() # process_for_unpaid() def process_for_active(self): - print 'processing for active' if nowdate() > self.current_invoice_end and not self.has_outstanding_invoice(): - print 'generating invoice' self.generate_invoice() - print 'invoice generated' if self.current_invoice_is_past_due(): - print 'current invoice is past due' - self.status = 'Past Due' + self.status = 'Past Due Date' def process_for_past_due_date(self): - if not self.has_outstanding_invoice(): - self.status = 'Active' - self.update_subscription_period() + current_invoice = self.get_current_invoice() + if not current_invoice: + frappe.throw('Current invoice is missing') else: - self.set_status() + if self.is_not_outstanding(current_invoice): + self.status = 'Active' + self.update_subscription_period() + else: + self.set_status_grace_period() + + def is_not_outstanding(self, invoice): + return invoice.status == 'Paid' def has_outstanding_invoice(self): current_invoice = self.get_current_invoice() - return current_invoice.posting_date != self.current_invoice_start + if not current_invoice: + return False + else: + return not self.is_not_outstanding(current_invoice) + return True diff --git a/erpnext/accounts/doctype/subscriptions/test_subscriptions.py b/erpnext/accounts/doctype/subscriptions/test_subscriptions.py index abd2f3f64f..63177e4eae 100644 --- a/erpnext/accounts/doctype/subscriptions/test_subscriptions.py +++ b/erpnext/accounts/doctype/subscriptions/test_subscriptions.py @@ -5,7 +5,8 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.utils.data import nowdate, add_days, get_last_day, cint, getdate, add_to_date +from frappe.utils.data import nowdate, add_days, get_last_day, cint, getdate, add_to_date, get_datetime_str +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry class TestSubscriptions(unittest.TestCase): @@ -14,7 +15,7 @@ class TestSubscriptions(unittest.TestCase): plan = frappe.new_doc('Subscription Plan') plan.plan_name = '_Test Plan Name' plan.item = '_Test Non Stock Item' - plan.cost = 999.99 + plan.cost = 900 plan.billing_interval = 'Month' plan.billing_interval_count = 1 plan.insert() @@ -23,7 +24,7 @@ class TestSubscriptions(unittest.TestCase): plan = frappe.new_doc('Subscription Plan') plan.plan_name = '_Test Plan Name 2' plan.item = '_Test Non Stock Item' - plan.cost = 1999.99 + plan.cost = 1999 plan.billing_interval = 'Month' plan.billing_interval_count = 1 plan.insert() @@ -32,7 +33,7 @@ class TestSubscriptions(unittest.TestCase): plan = frappe.new_doc('Subscription Plan') plan.plan_name = '_Test Plan Name 3' plan.item = '_Test Non Stock Item' - plan.cost = 1999.99 + plan.cost = 1999 plan.billing_interval = 'Day' plan.billing_interval_count = 14 plan.insert() @@ -89,6 +90,7 @@ class TestSubscriptions(unittest.TestCase): subscription.append('plans', {'plan': '_Test Plan Name'}) self.assertRaises(frappe.ValidationError, subscription.save) + subscription.delete() def test_create_subscription_multi_with_different_billing_fails(self): subscription = frappe.new_doc('Subscriptions') @@ -99,22 +101,92 @@ class TestSubscriptions(unittest.TestCase): subscription.append('plans', {'plan': '_Test Plan Name 3'}) self.assertRaises(frappe.ValidationError, subscription.save) + subscription.delete() - # def test_subscription_invoice_days_until_due(self): - # subscription = frappe.new_doc('Subscriptions') - # subscription.subscriber = '_Test Customer' - # subscription.append('plans', {'plan': '_Test Plan Name'}) - # subscription.save() + def test_invoice_is_generated_at_end_of_billing_period(self): + subscription = frappe.new_doc('Subscriptions') + subscription.subscriber = '_Test Customer' + subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.insert() - # generated_invoice_name = subscription.invoices[-1].invoice - # invoice = frappe.get_doc('Sales Invoice', generated_invoice_name) + self.assertEqual(subscription.status, 'Active') - # self.assertEqual( - # invoice.due_date, - # add_days(subscription.current_invoice_end, cint(subscription.days_until_due)) - # ) + subscription.set_current_invoice_start('2018-01-01') + subscription.set_current_invoice_end() - # subscription.delete() + self.assertEqual(subscription.current_invoice_start, '2018-01-01') + self.assertEqual(subscription.current_invoice_end, '2018-01-31') + subscription.process() + + self.assertEqual(len(subscription.invoices), 1) + self.assertEqual(subscription.current_invoice_start, nowdate()) + self.assertEqual(subscription.status, 'Past Due Date') + subscription.delete() + + def test_status_goes_back_to_active_after_invoice_is_paid(self): + subscription = frappe.new_doc('Subscriptions') + subscription.subscriber = '_Test Customer' + subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.insert() + subscription.set_current_invoice_start('2018-01-01') + subscription.set_current_invoice_end() + subscription.process() # generate first invoice + self.assertEqual(len(subscription.invoices), 1) + self.assertEqual(subscription.status, 'Past Due Date') + + subscription.get_current_invoice() + current_invoice = subscription.get_current_invoice() + + self.assertIsNotNone(current_invoice) + + current_invoice.db_set('outstanding_amount', 0) + current_invoice.db_set('status', 'Paid') + subscription.process() + + self.assertEqual(subscription.status, 'Active') + self.assertEqual(subscription.current_invoice_start, nowdate()) + self.assertEqual(len(subscription.invoices), 1) + + subscription.delete() + + def test_subscription_cancel_after_grace_period(self): + settings = frappe.get_single('Subscription Settings') + default_grace_period_action = settings.cancel_after_grace + settings.cancel_after_grace = 1 + settings.save() + + subscription = frappe.new_doc('Subscriptions') + subscription.subscriber = '_Test Customer' + subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.insert() + subscription.set_current_invoice_start('2018-01-01') + subscription.set_current_invoice_end() + subscription.process() # generate first invoice + + self.assertEqual(subscription.status, 'Past Due Date') + + subscription.process() + # This should change status to Canceled since grace period is 0 + self.assertEqual(subscription.status, 'Canceled') + + settings.cancel_after_grace = default_grace_period_action + settings.save() + subscription.delete() + + + def test_subscription_invoice_days_until_due(self): + subscription = frappe.new_doc('Subscriptions') + subscription.subscriber = '_Test Customer' + subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.days_until_due = 10 + subscription.insert() + subscription.set_current_invoice_start(get_datetime_str(add_to_date(nowdate(), months=-1))) + subscription.set_current_invoice_end() + subscription.process() # generate first invoice + self.assertEqual(len(subscription.invoices), 1) + self.assertEqual(subscription.status, 'Active') + + subscription.delete() def test_subscription_creation_with_multiple_plans(self): pass