mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-06 21:59:13 +00:00
more test cases and bug fixes
This commit is contained in:
@@ -13,9 +13,6 @@ SUBSCRIPTION_SETTINGS = frappe.get_single('Subscription Settings')
|
|||||||
|
|
||||||
|
|
||||||
class Subscriptions(Document):
|
class Subscriptions(Document):
|
||||||
def before_save(self):
|
|
||||||
self.set_status()
|
|
||||||
|
|
||||||
def before_insert(self):
|
def before_insert(self):
|
||||||
# update start just before the subscription doc is created
|
# update start just before the subscription doc is created
|
||||||
self.update_subscription_period()
|
self.update_subscription_period()
|
||||||
@@ -29,6 +26,8 @@ class Subscriptions(Document):
|
|||||||
self.current_invoice_start = self.trial_period_start
|
self.current_invoice_start = self.trial_period_start
|
||||||
elif not date:
|
elif not date:
|
||||||
self.current_invoice_start = nowdate()
|
self.current_invoice_start = nowdate()
|
||||||
|
elif date:
|
||||||
|
self.current_invoice_start = date
|
||||||
|
|
||||||
def set_current_invoice_end(self):
|
def set_current_invoice_end(self):
|
||||||
if self.is_trialling():
|
if self.is_trialling():
|
||||||
@@ -81,15 +80,19 @@ class Subscriptions(Document):
|
|||||||
|
|
||||||
return data
|
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():
|
if self.is_trialling():
|
||||||
self.status = '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'
|
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'
|
self.status = 'Active'
|
||||||
elif self.current_invoice_is_past_due():
|
elif self.current_invoice_is_past_due():
|
||||||
self.status = 'Past Due'
|
self.status = 'Past Due Date'
|
||||||
elif self.is_new_subscription():
|
elif self.is_new_subscription():
|
||||||
self.status = 'Active'
|
self.status = 'Active'
|
||||||
# todo: then generate new invoice
|
# todo: then generate new invoice
|
||||||
@@ -110,7 +113,7 @@ class Subscriptions(Document):
|
|||||||
if self.current_invoice_is_past_due(current_invoice):
|
if self.current_invoice_is_past_due(current_invoice):
|
||||||
grace_period = cint(SUBSCRIPTION_SETTINGS.grace_period)
|
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):
|
def current_invoice_is_past_due(self, current_invoice=None):
|
||||||
if not current_invoice:
|
if not current_invoice:
|
||||||
@@ -124,8 +127,11 @@ class Subscriptions(Document):
|
|||||||
def get_current_invoice(self):
|
def get_current_invoice(self):
|
||||||
if len(self.invoices):
|
if len(self.invoices):
|
||||||
current = self.invoices[-1]
|
current = self.invoices[-1]
|
||||||
doc = frappe.get_doc('Sales Invoice', current.invoice)
|
if frappe.db.exists('Sales Invoice', current.invoice):
|
||||||
return doc
|
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):
|
def is_new_subscription(self):
|
||||||
return len(self.invoices) == 0
|
return len(self.invoices) == 0
|
||||||
@@ -144,7 +150,7 @@ class Subscriptions(Document):
|
|||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
# todo: deal with users who collect prepayments. Maybe a new Subscription Invoice doctype?
|
# todo: deal with users who collect prepayments. Maybe a new Subscription Invoice doctype?
|
||||||
pass
|
self.set_subscription_status()
|
||||||
|
|
||||||
def generate_invoice(self):
|
def generate_invoice(self):
|
||||||
invoice = self.create_invoice()
|
invoice = self.create_invoice()
|
||||||
@@ -156,6 +162,8 @@ class Subscriptions(Document):
|
|||||||
|
|
||||||
def create_invoice(self):
|
def create_invoice(self):
|
||||||
invoice = frappe.new_doc('Sales Invoice')
|
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)
|
invoice.customer = self.get_customer(self.subscriber)
|
||||||
|
|
||||||
# Subscription is better suited for service items. I won't update `update_stock`
|
# Subscription is better suited for service items. I won't update `update_stock`
|
||||||
@@ -220,28 +228,36 @@ class Subscriptions(Document):
|
|||||||
"""
|
"""
|
||||||
if self.status == 'Active':
|
if self.status == 'Active':
|
||||||
self.process_for_active()
|
self.process_for_active()
|
||||||
elif self.status == 'Past Due':
|
elif self.status == 'Past Due Date':
|
||||||
self.process_for_past_due_date()
|
self.process_for_past_due_date()
|
||||||
|
self.save()
|
||||||
# process_for_unpaid()
|
# process_for_unpaid()
|
||||||
|
|
||||||
def process_for_active(self):
|
def process_for_active(self):
|
||||||
print 'processing for active'
|
|
||||||
if nowdate() > self.current_invoice_end and not self.has_outstanding_invoice():
|
if nowdate() > self.current_invoice_end and not self.has_outstanding_invoice():
|
||||||
print 'generating invoice'
|
|
||||||
self.generate_invoice()
|
self.generate_invoice()
|
||||||
print 'invoice generated'
|
|
||||||
|
|
||||||
if self.current_invoice_is_past_due():
|
if self.current_invoice_is_past_due():
|
||||||
print 'current invoice is past due'
|
self.status = 'Past Due Date'
|
||||||
self.status = 'Past Due'
|
|
||||||
|
|
||||||
def process_for_past_due_date(self):
|
def process_for_past_due_date(self):
|
||||||
if not self.has_outstanding_invoice():
|
current_invoice = self.get_current_invoice()
|
||||||
self.status = 'Active'
|
if not current_invoice:
|
||||||
self.update_subscription_period()
|
frappe.throw('Current invoice is missing')
|
||||||
else:
|
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):
|
def has_outstanding_invoice(self):
|
||||||
current_invoice = self.get_current_invoice()
|
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
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
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):
|
class TestSubscriptions(unittest.TestCase):
|
||||||
@@ -14,7 +15,7 @@ class TestSubscriptions(unittest.TestCase):
|
|||||||
plan = frappe.new_doc('Subscription Plan')
|
plan = frappe.new_doc('Subscription Plan')
|
||||||
plan.plan_name = '_Test Plan Name'
|
plan.plan_name = '_Test Plan Name'
|
||||||
plan.item = '_Test Non Stock Item'
|
plan.item = '_Test Non Stock Item'
|
||||||
plan.cost = 999.99
|
plan.cost = 900
|
||||||
plan.billing_interval = 'Month'
|
plan.billing_interval = 'Month'
|
||||||
plan.billing_interval_count = 1
|
plan.billing_interval_count = 1
|
||||||
plan.insert()
|
plan.insert()
|
||||||
@@ -23,7 +24,7 @@ class TestSubscriptions(unittest.TestCase):
|
|||||||
plan = frappe.new_doc('Subscription Plan')
|
plan = frappe.new_doc('Subscription Plan')
|
||||||
plan.plan_name = '_Test Plan Name 2'
|
plan.plan_name = '_Test Plan Name 2'
|
||||||
plan.item = '_Test Non Stock Item'
|
plan.item = '_Test Non Stock Item'
|
||||||
plan.cost = 1999.99
|
plan.cost = 1999
|
||||||
plan.billing_interval = 'Month'
|
plan.billing_interval = 'Month'
|
||||||
plan.billing_interval_count = 1
|
plan.billing_interval_count = 1
|
||||||
plan.insert()
|
plan.insert()
|
||||||
@@ -32,7 +33,7 @@ class TestSubscriptions(unittest.TestCase):
|
|||||||
plan = frappe.new_doc('Subscription Plan')
|
plan = frappe.new_doc('Subscription Plan')
|
||||||
plan.plan_name = '_Test Plan Name 3'
|
plan.plan_name = '_Test Plan Name 3'
|
||||||
plan.item = '_Test Non Stock Item'
|
plan.item = '_Test Non Stock Item'
|
||||||
plan.cost = 1999.99
|
plan.cost = 1999
|
||||||
plan.billing_interval = 'Day'
|
plan.billing_interval = 'Day'
|
||||||
plan.billing_interval_count = 14
|
plan.billing_interval_count = 14
|
||||||
plan.insert()
|
plan.insert()
|
||||||
@@ -89,6 +90,7 @@ class TestSubscriptions(unittest.TestCase):
|
|||||||
subscription.append('plans', {'plan': '_Test Plan Name'})
|
subscription.append('plans', {'plan': '_Test Plan Name'})
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, subscription.save)
|
self.assertRaises(frappe.ValidationError, subscription.save)
|
||||||
|
subscription.delete()
|
||||||
|
|
||||||
def test_create_subscription_multi_with_different_billing_fails(self):
|
def test_create_subscription_multi_with_different_billing_fails(self):
|
||||||
subscription = frappe.new_doc('Subscriptions')
|
subscription = frappe.new_doc('Subscriptions')
|
||||||
@@ -99,22 +101,92 @@ class TestSubscriptions(unittest.TestCase):
|
|||||||
subscription.append('plans', {'plan': '_Test Plan Name 3'})
|
subscription.append('plans', {'plan': '_Test Plan Name 3'})
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, subscription.save)
|
self.assertRaises(frappe.ValidationError, subscription.save)
|
||||||
|
subscription.delete()
|
||||||
|
|
||||||
# def test_subscription_invoice_days_until_due(self):
|
def test_invoice_is_generated_at_end_of_billing_period(self):
|
||||||
# subscription = frappe.new_doc('Subscriptions')
|
subscription = frappe.new_doc('Subscriptions')
|
||||||
# subscription.subscriber = '_Test Customer'
|
subscription.subscriber = '_Test Customer'
|
||||||
# subscription.append('plans', {'plan': '_Test Plan Name'})
|
subscription.append('plans', {'plan': '_Test Plan Name'})
|
||||||
# subscription.save()
|
subscription.insert()
|
||||||
|
|
||||||
# generated_invoice_name = subscription.invoices[-1].invoice
|
self.assertEqual(subscription.status, 'Active')
|
||||||
# invoice = frappe.get_doc('Sales Invoice', generated_invoice_name)
|
|
||||||
|
|
||||||
# self.assertEqual(
|
subscription.set_current_invoice_start('2018-01-01')
|
||||||
# invoice.due_date,
|
subscription.set_current_invoice_end()
|
||||||
# add_days(subscription.current_invoice_end, cint(subscription.days_until_due))
|
|
||||||
# )
|
|
||||||
|
|
||||||
# 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):
|
def test_subscription_creation_with_multiple_plans(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user