mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-01 19:29:10 +00:00
recurring invoice test cases, set period start date and period end date as first and last day of the month if previously so, else just add months
This commit is contained in:
@@ -10,5 +10,15 @@ test_records = [
|
|||||||
"doctype": "Fiscal Year",
|
"doctype": "Fiscal Year",
|
||||||
"year": "_Test Fiscal Year 2014",
|
"year": "_Test Fiscal Year 2014",
|
||||||
"year_start_date": "2014-01-01"
|
"year_start_date": "2014-01-01"
|
||||||
}]
|
}],
|
||||||
|
[{
|
||||||
|
"doctype": "Fiscal Year",
|
||||||
|
"year": "_Test Fiscal Year 2015",
|
||||||
|
"year_start_date": "2015-01-01"
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
"doctype": "Fiscal Year",
|
||||||
|
"year": "_Test Fiscal Year 2016",
|
||||||
|
"year_start_date": "2016-01-01"
|
||||||
|
}],
|
||||||
]
|
]
|
||||||
@@ -17,7 +17,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import webnotes
|
import webnotes
|
||||||
|
|
||||||
from webnotes.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate
|
from webnotes.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
|
||||||
|
get_first_day, get_last_day
|
||||||
|
|
||||||
from webnotes.utils.email_lib import sendmail
|
from webnotes.utils.email_lib import sendmail
|
||||||
from webnotes.utils import comma_and
|
from webnotes.utils import comma_and
|
||||||
from webnotes.model.doc import make_autoname
|
from webnotes.model.doc import make_autoname
|
||||||
@@ -891,25 +893,18 @@ class DocType(SellingController):
|
|||||||
|
|
||||||
next_date = get_next_date(self.doc.posting_date,
|
next_date = get_next_date(self.doc.posting_date,
|
||||||
month_map[self.doc.recurring_type], cint(self.doc.repeat_on_day_of_month))
|
month_map[self.doc.recurring_type], cint(self.doc.repeat_on_day_of_month))
|
||||||
|
|
||||||
webnotes.conn.set(self.doc, 'next_date', next_date)
|
webnotes.conn.set(self.doc, 'next_date', next_date)
|
||||||
|
|
||||||
def get_next_date(dt, mcount, day=None):
|
def get_next_date(dt, mcount, day=None):
|
||||||
import datetime
|
dt = getdate(dt)
|
||||||
month = getdate(dt).month + mcount
|
|
||||||
year = getdate(dt).year
|
from dateutil.relativedelta import relativedelta
|
||||||
if not day:
|
dt += relativedelta(months=mcount, day=day)
|
||||||
day = getdate(dt).day
|
|
||||||
if month > 12:
|
return dt
|
||||||
month, year = month-12, year+1
|
|
||||||
try:
|
def manage_recurring_invoices(next_date=None, commit=True):
|
||||||
next_month_date = datetime.date(year, month, day)
|
|
||||||
except:
|
|
||||||
import calendar
|
|
||||||
last_day = calendar.monthrange(year, month)[1]
|
|
||||||
next_month_date = datetime.date(year, month, last_day)
|
|
||||||
return next_month_date.strftime("%Y-%m-%d")
|
|
||||||
|
|
||||||
def manage_recurring_invoices(next_date=None):
|
|
||||||
"""
|
"""
|
||||||
Create recurring invoices on specific date by copying the original one
|
Create recurring invoices on specific date by copying the original one
|
||||||
and notify the concerned people
|
and notify the concerned people
|
||||||
@@ -929,19 +924,22 @@ def manage_recurring_invoices(next_date=None):
|
|||||||
ref_wrapper = webnotes.bean('Sales Invoice', ref_invoice)
|
ref_wrapper = webnotes.bean('Sales Invoice', ref_invoice)
|
||||||
new_invoice_wrapper = make_new_invoice(ref_wrapper, next_date)
|
new_invoice_wrapper = make_new_invoice(ref_wrapper, next_date)
|
||||||
send_notification(new_invoice_wrapper)
|
send_notification(new_invoice_wrapper)
|
||||||
webnotes.conn.commit()
|
if commit:
|
||||||
|
webnotes.conn.commit()
|
||||||
except:
|
except:
|
||||||
webnotes.conn.rollback()
|
if commit:
|
||||||
|
webnotes.conn.rollback()
|
||||||
|
|
||||||
webnotes.conn.begin()
|
webnotes.conn.begin()
|
||||||
webnotes.conn.sql("update `tabSales Invoice` set \
|
webnotes.conn.sql("update `tabSales Invoice` set \
|
||||||
convert_into_recurring_invoice = 0 where name = %s", ref_invoice)
|
convert_into_recurring_invoice = 0 where name = %s", ref_invoice)
|
||||||
notify_errors(ref_invoice, ref_wrapper.doc.owner)
|
notify_errors(ref_invoice, ref_wrapper.doc.owner)
|
||||||
webnotes.conn.commit()
|
webnotes.conn.commit()
|
||||||
|
|
||||||
exception_list.append(webnotes.getTraceback())
|
exception_list.append(webnotes.getTraceback())
|
||||||
finally:
|
finally:
|
||||||
webnotes.conn.begin()
|
if commit:
|
||||||
|
webnotes.conn.begin()
|
||||||
|
|
||||||
if exception_list:
|
if exception_list:
|
||||||
exception_message = "\n\n".join([cstr(d) for d in exception_list])
|
exception_message = "\n\n".join([cstr(d) for d in exception_list])
|
||||||
@@ -953,19 +951,27 @@ def make_new_invoice(ref_wrapper, posting_date):
|
|||||||
new_invoice = clone(ref_wrapper)
|
new_invoice = clone(ref_wrapper)
|
||||||
|
|
||||||
mcount = month_map[ref_wrapper.doc.recurring_type]
|
mcount = month_map[ref_wrapper.doc.recurring_type]
|
||||||
|
|
||||||
|
invoice_period_from_date = get_next_date(ref_wrapper.doc.invoice_period_from_date, mcount)
|
||||||
|
|
||||||
|
# get last day of the month to maintain period if the from date is first day of its own month
|
||||||
|
# and to date is the last day of its own month
|
||||||
|
if (cstr(get_first_day(ref_wrapper.doc.invoice_period_from_date)) == \
|
||||||
|
cstr(ref_wrapper.doc.invoice_period_from_date)) and \
|
||||||
|
(cstr(get_last_day(ref_wrapper.doc.invoice_period_to_date)) == \
|
||||||
|
cstr(ref_wrapper.doc.invoice_period_to_date)):
|
||||||
|
invoice_period_to_date = get_last_day(get_next_date(ref_wrapper.doc.invoice_period_to_date,
|
||||||
|
mcount))
|
||||||
|
else:
|
||||||
|
invoice_period_to_date = get_next_date(ref_wrapper.doc.invoice_period_to_date, mcount)
|
||||||
|
|
||||||
new_invoice.doc.fields.update({
|
new_invoice.doc.fields.update({
|
||||||
"posting_date": posting_date,
|
"posting_date": posting_date,
|
||||||
"aging_date": posting_date,
|
"aging_date": posting_date,
|
||||||
|
|
||||||
"due_date": add_days(posting_date, cint(date_diff(ref_wrapper.doc.due_date,
|
"due_date": add_days(posting_date, cint(date_diff(ref_wrapper.doc.due_date,
|
||||||
ref_wrapper.doc.posting_date))),
|
ref_wrapper.doc.posting_date))),
|
||||||
|
"invoice_period_from_date": invoice_period_from_date,
|
||||||
"invoice_period_from_date": \
|
"invoice_period_to_date": invoice_period_to_date,
|
||||||
get_next_date(ref_wrapper.doc.invoice_period_from_date, mcount),
|
|
||||||
|
|
||||||
"invoice_period_to_date": \
|
|
||||||
get_next_date(ref_wrapper.doc.invoice_period_to_date, mcount),
|
|
||||||
"fiscal_year": get_fiscal_year(posting_date)[0],
|
"fiscal_year": get_fiscal_year(posting_date)[0],
|
||||||
"owner": ref_wrapper.doc.owner,
|
"owner": ref_wrapper.doc.owner,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"creation": "2013-03-12 11:56:25",
|
"creation": "2013-03-20 17:01:58",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"modified": "2013-03-12 14:31:24",
|
"modified": "2013-03-20 19:17:38",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"owner": "Administrator"
|
"owner": "Administrator"
|
||||||
},
|
},
|
||||||
@@ -1093,7 +1093,7 @@
|
|||||||
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc ",
|
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc ",
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "repeat_on_day_of_month",
|
"fieldname": "repeat_on_day_of_month",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Int",
|
||||||
"label": "Repeat on Day of Month",
|
"label": "Repeat on Day of Month",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
|
|||||||
@@ -332,7 +332,142 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(not webnotes.conn.sql("""select name from `tabJournal Voucher Detail`
|
self.assertTrue(not webnotes.conn.sql("""select name from `tabJournal Voucher Detail`
|
||||||
where against_invoice=%s""", si.doc.name))
|
where against_invoice=%s""", si.doc.name))
|
||||||
|
|
||||||
|
def test_recurring_invoice(self):
|
||||||
|
from webnotes.utils import now_datetime, get_first_day, get_last_day, add_to_date
|
||||||
|
today = now_datetime().date()
|
||||||
|
|
||||||
|
base_si = webnotes.bean(copy=test_records[0])
|
||||||
|
base_si.doc.fields.update({
|
||||||
|
"convert_into_recurring_invoice": 1,
|
||||||
|
"recurring_type": "Monthly",
|
||||||
|
"notification_email_address": "test@example.com, test1@example.com, test2@example.com",
|
||||||
|
"repeat_on_day_of_month": today.day,
|
||||||
|
"posting_date": today,
|
||||||
|
"invoice_period_from_date": get_first_day(today),
|
||||||
|
"invoice_period_to_date": get_last_day(today)
|
||||||
|
})
|
||||||
|
|
||||||
|
# monthly
|
||||||
|
si1 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si1.insert()
|
||||||
|
si1.submit()
|
||||||
|
self._test_recurring_invoice(si1, True)
|
||||||
|
|
||||||
|
# monthly without a first and last day period
|
||||||
|
si2 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si2.doc.fields.update({
|
||||||
|
"invoice_period_from_date": today,
|
||||||
|
"invoice_period_to_date": add_to_date(today, days=30)
|
||||||
|
})
|
||||||
|
si2.insert()
|
||||||
|
si2.submit()
|
||||||
|
self._test_recurring_invoice(si2, False)
|
||||||
|
|
||||||
|
# quarterly
|
||||||
|
si3 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si3.doc.fields.update({
|
||||||
|
"recurring_type": "Quarterly",
|
||||||
|
"invoice_period_from_date": get_first_day(today),
|
||||||
|
"invoice_period_to_date": get_last_day(add_to_date(today, months=3))
|
||||||
|
})
|
||||||
|
si3.insert()
|
||||||
|
si3.submit()
|
||||||
|
self._test_recurring_invoice(si3, True)
|
||||||
|
|
||||||
|
# quarterly without a first and last day period
|
||||||
|
si4 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si4.doc.fields.update({
|
||||||
|
"recurring_type": "Quarterly",
|
||||||
|
"invoice_period_from_date": today,
|
||||||
|
"invoice_period_to_date": add_to_date(today, months=3)
|
||||||
|
})
|
||||||
|
si4.insert()
|
||||||
|
si4.submit()
|
||||||
|
self._test_recurring_invoice(si4, False)
|
||||||
|
|
||||||
|
# yearly
|
||||||
|
si5 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si5.doc.fields.update({
|
||||||
|
"recurring_type": "Yearly",
|
||||||
|
"invoice_period_from_date": get_first_day(today),
|
||||||
|
"invoice_period_to_date": get_last_day(add_to_date(today, years=1))
|
||||||
|
})
|
||||||
|
si5.insert()
|
||||||
|
si5.submit()
|
||||||
|
self._test_recurring_invoice(si5, True)
|
||||||
|
|
||||||
|
# yearly without a first and last day period
|
||||||
|
si6 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si6.doc.fields.update({
|
||||||
|
"recurring_type": "Yearly",
|
||||||
|
"invoice_period_from_date": today,
|
||||||
|
"invoice_period_to_date": add_to_date(today, years=1)
|
||||||
|
})
|
||||||
|
si6.insert()
|
||||||
|
si6.submit()
|
||||||
|
self._test_recurring_invoice(si6, False)
|
||||||
|
|
||||||
|
# change posting date but keep recuring day to be today
|
||||||
|
si7 = webnotes.bean(copy=base_si.doclist)
|
||||||
|
si7.doc.fields.update({
|
||||||
|
"posting_date": add_to_date(today, days=-3)
|
||||||
|
})
|
||||||
|
si7.insert()
|
||||||
|
si7.submit()
|
||||||
|
|
||||||
|
# setting so that _test function works
|
||||||
|
si7.doc.posting_date = today
|
||||||
|
self._test_recurring_invoice(si7, True)
|
||||||
|
|
||||||
|
def _test_recurring_invoice(self, base_si, first_and_last_day):
|
||||||
|
from webnotes.utils import add_months, get_last_day, getdate
|
||||||
|
from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices
|
||||||
|
|
||||||
|
no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_si.doc.recurring_type]
|
||||||
|
|
||||||
|
def _test(i):
|
||||||
|
self.assertEquals(i+1, webnotes.conn.sql("""select count(*) from `tabSales Invoice`
|
||||||
|
where recurring_id=%s and docstatus=1""", base_si.doc.recurring_id)[0][0])
|
||||||
|
|
||||||
|
next_date = add_months(base_si.doc.posting_date, no_of_months)
|
||||||
|
|
||||||
|
manage_recurring_invoices(next_date=next_date, commit=False)
|
||||||
|
|
||||||
|
recurred_invoices = webnotes.conn.sql("""select name from `tabSales Invoice`
|
||||||
|
where recurring_id=%s and docstatus=1 order by name desc""", base_si.doc.recurring_id)
|
||||||
|
|
||||||
|
self.assertEquals(i+2, len(recurred_invoices))
|
||||||
|
|
||||||
|
new_si = webnotes.bean("Sales Invoice", recurred_invoices[0][0])
|
||||||
|
|
||||||
|
for fieldname in ["convert_into_recurring_invoice", "recurring_type",
|
||||||
|
"repeat_on_day_of_month", "notification_email_address"]:
|
||||||
|
self.assertEquals(base_si.doc.fields.get(fieldname),
|
||||||
|
new_si.doc.fields.get(fieldname))
|
||||||
|
|
||||||
|
self.assertEquals(new_si.doc.posting_date, unicode(next_date))
|
||||||
|
|
||||||
|
self.assertEquals(new_si.doc.invoice_period_from_date,
|
||||||
|
unicode(add_months(base_si.doc.invoice_period_from_date, no_of_months)))
|
||||||
|
|
||||||
|
if first_and_last_day:
|
||||||
|
self.assertEquals(new_si.doc.invoice_period_to_date,
|
||||||
|
unicode(get_last_day(add_months(base_si.doc.invoice_period_to_date,
|
||||||
|
no_of_months))))
|
||||||
|
else:
|
||||||
|
self.assertEquals(new_si.doc.invoice_period_to_date,
|
||||||
|
unicode(add_months(base_si.doc.invoice_period_to_date, no_of_months)))
|
||||||
|
|
||||||
|
self.assertEquals(getdate(new_si.doc.posting_date).day,
|
||||||
|
base_si.doc.repeat_on_day_of_month)
|
||||||
|
|
||||||
|
return new_si
|
||||||
|
|
||||||
|
# if yearly, test 3 repetitions, else test 13 repetitions
|
||||||
|
count = no_of_months == 12 and 3 or 13
|
||||||
|
for i in xrange(count):
|
||||||
|
base_si = _test(i)
|
||||||
|
|
||||||
test_dependencies = ["Journal Voucher", "POS Setting"]
|
test_dependencies = ["Journal Voucher", "POS Setting"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user