This commit is contained in:
tundebabzy 2018-03-02 11:00:15 +01:00
parent 49d34df8f4
commit c15dc21b7d
2 changed files with 44 additions and 39 deletions

View File

@ -3,10 +3,11 @@
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document
from frappe.utils.data import now, nowdate, getdate, cint, add_days, date_diff, get_last_day, get_first_day, add_to_date
from frappe import _ from frappe import _
from frappe.model.document import Document
from frappe.utils.data import nowdate, getdate, cint, add_days, date_diff, get_last_day, add_to_date
class Subscriptions(Document): class Subscriptions(Document):
@ -67,7 +68,8 @@ class Subscriptions(Document):
""" """
return self.get_billing_cycle_data() return self.get_billing_cycle_data()
def validate_plans_billing_cycle(self, billing_cycle_data): @staticmethod
def validate_plans_billing_cycle(billing_cycle_data):
""" """
Makes sure that all `Subscription Plan` in the `Subscription` have the Makes sure that all `Subscription Plan` in the `Subscription` have the
same billing interval same billing interval
@ -112,7 +114,7 @@ class Subscriptions(Document):
elif interval == 'Month': elif interval == 'Month':
data['months'] = interval_count data['months'] = interval_count
elif interval == 'Year': elif interval == 'Year':
data['years'] == interval_count data['years'] = interval_count
# todo: test week # todo: test week
elif interval == 'Week': elif interval == 'Week':
data['days'] = interval_count * 7 - 1 data['days'] = interval_count * 7 - 1
@ -154,7 +156,8 @@ class Subscriptions(Document):
""" """
return not self.period_has_passed(self.trial_period_end) and self.is_new_subscription() return not self.period_has_passed(self.trial_period_end) and self.is_new_subscription()
def period_has_passed(self, end_date): @staticmethod
def period_has_passed(end_date):
""" """
Returns true if the given `end_date` has passed Returns true if the given `end_date` has passed
""" """
@ -198,7 +201,7 @@ class Subscriptions(Document):
doc = frappe.get_doc('Sales Invoice', current.invoice) doc = frappe.get_doc('Sales Invoice', current.invoice)
return doc return doc
else: else:
frappe.throw(_('Invoice {0} no longer exists'.format(invoice.invoice))) frappe.throw(_('Invoice {0} no longer exists'.format(current.invoice)))
def is_new_subscription(self): def is_new_subscription(self):
""" """
@ -282,7 +285,8 @@ class Subscriptions(Document):
return invoice return invoice
def get_customer(self, subscriber_name): @staticmethod
def get_customer(subscriber_name):
""" """
Returns the `Customer` linked to the `Subscriber` Returns the `Customer` linked to the `Subscriber`
""" """
@ -302,7 +306,7 @@ class Subscriptions(Document):
) )
elif plan_items: elif plan_items:
prorate_factor = self.get_proration_factor(self.current_invoice_end, self.current_invoice_start) prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
item_names = frappe.db.sql( item_names = frappe.db.sql(
'select item as item_code, cost * %s as rate from `tabSubscription Plan` where name in %s', 'select item as item_code, cost * %s as rate from `tabSubscription Plan` where name in %s',
@ -311,13 +315,6 @@ class Subscriptions(Document):
return item_names return item_names
def get_proration_factor(self, period_end, period_start):
diff = date_diff(nowdate(), period_start) + 1
plan_days = date_diff(period_end, period_start) + 1
prorate_factor = diff/plan_days
return prorate_factor
def process(self): def process(self):
""" """
To be called by task periodically. It checks the subscription and takes appropriate action To be called by task periodically. It checks the subscription and takes appropriate action
@ -379,7 +376,8 @@ class Subscriptions(Document):
else: else:
self.set_status_grace_period() self.set_status_grace_period()
def is_not_outstanding(self, invoice): @staticmethod
def is_not_outstanding(invoice):
""" """
Return `True` if the given invoice is paid Return `True` if the given invoice is paid
""" """
@ -394,7 +392,6 @@ class Subscriptions(Document):
return False return False
else: else:
return not self.is_not_outstanding(current_invoice) return not self.is_not_outstanding(current_invoice)
return True
def cancel_subscription(self): def cancel_subscription(self):
""" """
@ -457,6 +454,14 @@ def process(data):
frappe.db.commit() frappe.db.commit()
def get_prorata_factor(period_end, period_start):
diff = date_diff(nowdate(), period_start) + 1
plan_days = date_diff(period_end, period_start) + 1
prorate_factor = diff/plan_days
return prorate_factor
@frappe.whitelist() @frappe.whitelist()
def cancel_subscription(name): def cancel_subscription(name):
""" """

View File

@ -3,10 +3,11 @@
# See license.txt # See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe
import unittest import unittest
from frappe.utils.data import nowdate, add_days, get_last_day, cint, getdate, add_to_date, get_datetime_str, add_months, date_diff
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry import frappe
from erpnext.accounts.doctype.subscriptions.subscriptions import get_prorata_factor
from frappe.utils.data import nowdate, add_days, add_to_date, add_months, date_diff
class TestSubscriptions(unittest.TestCase): class TestSubscriptions(unittest.TestCase):
@ -156,7 +157,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.append('plans', {'plan': '_Test Plan Name'}) subscription.append('plans', {'plan': '_Test Plan Name'})
subscription.start = '2018-01-01' subscription.start = '2018-01-01'
subscription.insert() subscription.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice
self.assertEqual(subscription.status, 'Past Due Date') self.assertEqual(subscription.status, 'Past Due Date')
@ -179,7 +180,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.append('plans', {'plan': '_Test Plan Name'}) subscription.append('plans', {'plan': '_Test Plan Name'})
subscription.start = '2018-01-01' subscription.start = '2018-01-01'
subscription.insert() subscription.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice
self.assertEqual(subscription.status, 'Past Due Date') self.assertEqual(subscription.status, 'Past Due Date')
@ -198,13 +199,13 @@ class TestSubscriptions(unittest.TestCase):
subscription.days_until_due = 10 subscription.days_until_due = 10
subscription.start = add_months(nowdate(), -1) subscription.start = add_months(nowdate(), -1)
subscription.insert() subscription.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice
self.assertEqual(len(subscription.invoices), 1) self.assertEqual(len(subscription.invoices), 1)
self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.status, 'Active')
subscription.delete() subscription.delete()
def test_subcription_is_past_due_doesnt_change_within_grace_period(self): def test_subscription_is_past_due_doesnt_change_within_grace_period(self):
settings = frappe.get_single('Subscription Settings') settings = frappe.get_single('Subscription Settings')
grace_period = settings.grace_period grace_period = settings.grace_period
settings.grace_period = 1000 settings.grace_period = 1000
@ -215,7 +216,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.append('plans', {'plan': '_Test Plan Name'}) subscription.append('plans', {'plan': '_Test Plan Name'})
subscription.start = '2018-01-01' subscription.start = '2018-01-01'
subscription.insert() subscription.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice
self.assertEqual(subscription.status, 'Past Due Date') self.assertEqual(subscription.status, 'Past Due Date')
@ -238,20 +239,20 @@ class TestSubscriptions(unittest.TestCase):
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.save()
subscription.process() # no changes expected subscription.process() # no changes expected
self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.status, 'Active')
self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_start, nowdate())
self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
self.assertEqual(len(subscription.invoices), 0) self.assertEqual(len(subscription.invoices), 0)
subscription.process() # no changes expected still subscription.process() # no changes expected still
self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.status, 'Active')
self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_start, nowdate())
self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
self.assertEqual(len(subscription.invoices), 0) self.assertEqual(len(subscription.invoices), 0)
subscription.process() # no changes expected yet still subscription.process() # no changes expected yet still
self.assertEqual(subscription.status, 'Active') self.assertEqual(subscription.status, 'Active')
self.assertEqual(subscription.current_invoice_start, nowdate()) self.assertEqual(subscription.current_invoice_start, nowdate())
self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1)) self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
@ -259,7 +260,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.delete() subscription.delete()
def test_subcription_cancelation(self): def test_subscription_cancelation(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'})
@ -270,7 +271,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.delete() subscription.delete()
def test_subscription_cancelation_invoices(self): def test_subscription_cancellation_invoices(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'})
@ -288,16 +289,15 @@ class TestSubscriptions(unittest.TestCase):
prorate_factor = diff/plan_days prorate_factor = diff/plan_days
self.assertEqual( self.assertEqual(
subscription.get_proration_factor( get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start),
subscription.current_invoice_end, prorate_factor
subscription.current_invoice_start )
), prorate_factor)
self.assertEqual(invoice.grand_total, prorate_factor * 900) self.assertEqual(invoice.grand_total, prorate_factor * 900)
self.assertEqual(subscription.status, 'Canceled') self.assertEqual(subscription.status, 'Canceled')
subscription.delete() subscription.delete()
def test_subscription_cancellation_invoices_with_proration_false(self): def test_subscription_cancellation_invoices_with_prorata_false(self):
settings = frappe.get_single('Subscription Settings') settings = frappe.get_single('Subscription Settings')
to_prorate = settings.prorate to_prorate = settings.prorate
settings.prorate = 0 settings.prorate = 0
@ -317,7 +317,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.delete() subscription.delete()
def test_subscription_cancellation_invoices_with_proration_true(self): def test_subscription_cancellation_invoices_with_prorata_true(self):
settings = frappe.get_single('Subscription Settings') settings = frappe.get_single('Subscription Settings')
to_prorate = settings.prorate to_prorate = settings.prorate
settings.prorate = 1 settings.prorate = 1
@ -341,7 +341,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.delete() subscription.delete()
def test_subcription_cancelation_and_process(self): def test_subcription_cancellation_and_process(self):
settings = frappe.get_single('Subscription Settings') settings = frappe.get_single('Subscription Settings')
default_grace_period_action = settings.cancel_after_grace default_grace_period_action = settings.cancel_after_grace
settings.cancel_after_grace = 1 settings.cancel_after_grace = 1
@ -386,7 +386,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.append('plans', {'plan': '_Test Plan Name'}) subscription.append('plans', {'plan': '_Test Plan Name'})
subscription.start = '2018-01-01' subscription.start = '2018-01-01'
subscription.insert() subscription.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice
self.assertEqual(subscription.status, 'Past Due Date') self.assertEqual(subscription.status, 'Past Due Date')
@ -423,7 +423,7 @@ class TestSubscriptions(unittest.TestCase):
subscription.append('plans', {'plan': '_Test Plan Name'}) subscription.append('plans', {'plan': '_Test Plan Name'})
subscription.start = '2018-01-01' subscription.start = '2018-01-01'
subscription.insert() subscription.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice
self.assertEqual(subscription.status, 'Past Due Date') self.assertEqual(subscription.status, 'Past Due Date')