diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 32b97ba80b5..c17f3aeb846 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "ACC-SUB-.YYYY.-.#####", "creation": "2017-07-18 17:50:43.967266", "doctype": "DocType", @@ -184,7 +183,8 @@ "fieldname": "invoices", "fieldtype": "Table", "label": "Invoices", - "options": "Subscription Invoice" + "options": "Subscription Invoice", + "read_only": 1 }, { "collapsible": 1, @@ -197,8 +197,7 @@ "fieldtype": "Column Break" } ], - "links": [], - "modified": "2020-01-27 14:37:32.845173", + "modified": "2020-08-27 23:30:02.504042", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 98d07c71a3a..bc34816e375 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -326,8 +326,7 @@ class Subscription(Document): 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() + (getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) def is_prepaid_to_invoice(self): if not self.generate_invoice_at_period_start: @@ -337,8 +336,16 @@ class Subscription(Document): 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() - + return getdate(nowdate()) >= getdate(self.current_invoice_start) + + def is_current_invoice_generated(self): + invoice = self.get_current_invoice() + + if invoice and getdate(self.current_invoice_start) <= getdate(invoice.posting_date) <= getdate(self.current_invoice_end): + return True + + return False + def is_current_invoice_paid(self): if self.is_new_subscription(): return False @@ -346,7 +353,7 @@ class Subscription(Document): last_invoice = frappe.get_doc('Sales Invoice', self.invoices[-1].invoice) if getdate(last_invoice.posting_date) == getdate(self.current_invoice_start) and last_invoice.status == 'Paid': return True - + return False def process_for_active(self): @@ -358,7 +365,8 @@ class Subscription(Document): 2. Change the `Subscription` status to 'Past Due Date' 3. Change the `Subscription` status to 'Cancelled' """ - if not self.is_current_invoice_paid() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): + if not self.is_current_invoice_generated() and not self.is_current_invoice_paid() and \ + (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' @@ -369,6 +377,9 @@ class Subscription(Document): if self.cancel_at_period_end and getdate(nowdate()) > getdate(self.current_invoice_end): self.cancel_subscription_at_period_end() + if self.is_current_invoice_generated() and getdate() > getdate(self.current_invoice_end): + self.update_subscription_period(add_days(self.current_invoice_end, 1)) + def cancel_subscription_at_period_end(self): """ Called when `Subscription.cancel_at_period_end` is truthy diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 3d96f233b40..e38de252699 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -101,19 +101,19 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_invoice_is_generated_at_end_of_billing_period(self): + start_date = add_to_date(nowdate(), months=-1) subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' - subscription.start = '2018-01-01' + subscription.start = start_date subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.insert() self.assertEqual(subscription.status, 'Active') - self.assertEqual(subscription.current_invoice_start, '2018-01-01') - self.assertEqual(subscription.current_invoice_end, '2018-01-31') + self.assertEqual(subscription.current_invoice_start, start_date) + self.assertEqual(subscription.current_invoice_end, add_days(nowdate(), -1)) subscription.process() self.assertEqual(len(subscription.invoices), 1) - self.assertEqual(subscription.current_invoice_start, '2018-01-01') self.assertEqual(subscription.status, 'Past Due Date') subscription.delete() @@ -137,7 +137,6 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(subscription.status, 'Active') - self.assertEqual(subscription.current_invoice_start, add_months(subscription.start, 1)) self.assertEqual(len(subscription.invoices), 1) subscription.delete() @@ -538,3 +537,23 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() + + def test_duplicate_invoice_check(self): + subscription = frappe.new_doc('Subscription') + subscription.customer = '_Test Customer' + subscription.generate_invoice_at_period_start = True + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) + subscription.start = nowdate() + subscription.save() + + # Generate invoice for the current invoicing period + subscription.process() + subscription.load_from_db() + self.assertEqual(len(subscription.invoices), 1) + + # Proccess subscription again for the same period + subscription.process() + subscription.load_from_db() + + # No new invoice should be created for current period + self.assertEqual(len(subscription.invoices), 1)