more test cases and bug fixes

This commit is contained in:
tundebabzy 2018-02-28 12:11:19 +01:00
parent 3aaf693abd
commit b3d5777e55
2 changed files with 126 additions and 38 deletions

View File

@ -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

View File

@ -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