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): 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

View File

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