fixing recurring invoice

This commit is contained in:
Anand Doshi 2012-11-30 12:48:33 +05:30
parent c2fb039697
commit b2293c48e8
4 changed files with 377 additions and 328 deletions

View File

@ -397,194 +397,3 @@ class DocType:
fy_obj = get_obj('Fiscal Year', fy[0]) fy_obj = get_obj('Fiscal Year', fy[0])
for a in set(ac_list): for a in set(ac_list):
fy_obj.repost(a) fy_obj.repost(a)
def manage_recurring_invoices():
"""
Create recurring invoices on specific date by copying the original one
and notify the concerned people
"""
rv = webnotes.conn.sql("""select name, recurring_id from `tabSales Invoice` \
where ifnull(convert_into_recurring_invoice, 0) = 1 and next_date = %s \
and next_date <= ifnull(end_date, '2199-12-31') and docstatus=1""", nowdate())
exception_list = []
for d in rv:
if not webnotes.conn.sql("""select name from `tabSales Invoice` \
where posting_date = %s and recurring_id = %s and docstatus=1""", (nowdate(), d[1])):
try:
prev_rv = get_obj('Sales Invoice', d[0], with_children=1)
new_rv = create_new_invoice(prev_rv)
send_notification(new_rv)
webnotes.conn.commit()
except Exception, e:
webnotes.conn.rollback()
webnotes.conn.begin()
webnotes.conn.sql("update `tabSales Invoice` set \
convert_into_recurring_invoice = 0 where name = %s", d[0])
notify_errors(d[0], prev_rv.doc.owner)
webnotes.conn.commit()
exception_list.append(e)
finally:
webnotes.conn.begin()
if exception_list:
exception_message = "\n\n".join([cstr(d) for d in exception_list])
raise Exception, exception_message
def notify_errors(inv, owner):
import webnotes
import website
exception_msg = """
Dear User,
An error occured while creating recurring invoice from %s (at %s).
May be there are some invalid email ids mentioned in the invoice.
To stop sending repetitive error notifications from the system, we have unchecked
"Convert into Recurring" field in the invoice %s.
Please correct the invoice and make the invoice recurring again.
<b>It is necessary to take this action today itself for the above mentioned recurring invoice \
to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field \
of this invoice for generating the recurring invoice.</b>
Regards,
Administrator
""" % (inv, website.get_site_address(), inv)
subj = "[Urgent] Error while creating recurring invoice from %s" % inv
from webnotes.profile import get_system_managers
recipients = get_system_managers()
owner_email = webnotes.conn.get_value("Profile", owner, "email")
if not owner_email in recipients:
recipients.append(owner_email)
assign_task_to_owner(inv, exception_msg, recipients)
sendmail(recipients, subject=subj, msg = exception_msg)
def assign_task_to_owner(inv, msg, users):
for d in users:
if d.lower() == 'administrator':
d = webnotes.conn.sql("select ifnull(email_id, '') \
from `tabProfile` where name = 'Administrator'")[0][0]
from webnotes.widgets.form import assign_to
args = {
'assign_to' : d,
'doctype' : 'Sales Invoice',
'name' : inv,
'description' : msg,
'priority' : 'Urgent'
}
assign_to.add(args)
def create_new_invoice(prev_rv):
# clone rv
new_rv = clone(prev_rv)
mdict = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
mcount = mdict[prev_rv.doc.recurring_type]
# update new rv
new_rv.doc.posting_date = new_rv.doc.next_date
new_rv.doc.aging_date = new_rv.doc.next_date
new_rv.doc.due_date = add_days(new_rv.doc.next_date, cint(date_diff(prev_rv.doc.due_date, prev_rv.doc.posting_date)))
new_rv.doc.invoice_period_from_date = get_next_date(new_rv.doc.invoice_period_from_date, mcount)
new_rv.doc.invoice_period_to_date = get_next_date(new_rv.doc.invoice_period_to_date, mcount)
new_rv.doc.owner = prev_rv.doc.owner
new_rv.doc.save()
# submit and after submit
new_rv.submit()
new_rv.update_after_submit()
return new_rv
def get_next_date(dt, mcount):
import datetime
m = getdate(dt).month + mcount
y = getdate(dt).year
d = getdate(dt).day
if m > 12:
m, y = m-12, y+1
try:
next_month_date = datetime.date(y, m, d)
except:
import calendar
last_day = calendar.monthrange(y, m)[1]
next_month_date = datetime.date(y, m, last_day)
return next_month_date.strftime("%Y-%m-%d")
def send_notification(new_rv):
"""Notify concerned persons about recurring invoice generation"""
subject = "Invoice : " + new_rv.doc.name
com = new_rv.doc.company # webnotes.conn.get_value('Control Panel', '', 'letter_head')
hd = '''<div><h2>%s</h2></div>
<div><h3>Invoice: %s</h3></div>
<table cellspacing= "5" cellpadding="5" width = "100%%">
<tr>
<td width = "50%%"><b>Customer</b><br>%s<br>%s</td>
<td width = "50%%">Invoice Date : %s<br>Invoice Period : %s to %s <br>Due Date : %s</td>
</tr>
</table>
''' % (com, new_rv.doc.name, new_rv.doc.customer_name, new_rv.doc.address_display, getdate(new_rv.doc.posting_date).strftime("%d-%m-%Y"), \
getdate(new_rv.doc.invoice_period_from_date).strftime("%d-%m-%Y"), getdate(new_rv.doc.invoice_period_to_date).strftime("%d-%m-%Y"),\
getdate(new_rv.doc.due_date).strftime("%d-%m-%Y"))
tbl = '''<table border="1px solid #CCC" width="100%%" cellpadding="0px" cellspacing="0px">
<tr>
<td width = "15%%" bgcolor="#CCC" align="left"><b>Item</b></td>
<td width = "40%%" bgcolor="#CCC" align="left"><b>Description</b></td>
<td width = "15%%" bgcolor="#CCC" align="center"><b>Qty</b></td>
<td width = "15%%" bgcolor="#CCC" align="center"><b>Rate</b></td>
<td width = "15%%" bgcolor="#CCC" align="center"><b>Amount</b></td>
</tr>
'''
for d in getlist(new_rv.doclist, 'entries'):
tbl += '<tr><td>' + d.item_code +'</td><td>' + d.description+'</td><td>' + cstr(d.qty) +'</td><td>' + cstr(d.basic_rate) +'</td><td>' + cstr(d.amount) +'</td></tr>'
tbl += '</table>'
totals =''' <table cellspacing= "5" cellpadding="5" width = "100%%">
<tr>
<td width = "50%%"></td>
<td width = "50%%">
<table width = "100%%">
<tr>
<td width = "50%%">Net Total: </td><td>%s </td>
</tr><tr>
<td width = "50%%">Total Tax: </td><td>%s </td>
</tr><tr>
<td width = "50%%">Grand Total: </td><td>%s</td>
</tr><tr>
<td width = "50%%">In Words: </td><td>%s</td>
</tr>
</table>
</td>
</tr>
<tr><td>Terms and Conditions:</td></tr>
<tr><td>%s</td></tr>
</table>
''' % (new_rv.doc.net_total, new_rv.doc.other_charges_total,new_rv.doc.grand_total, new_rv.doc.in_words,new_rv.doc.terms)
msg = hd + tbl + totals
recipients = new_rv.doc.notification_email_address.replace('\n', '').replace(' ', '').split(",")
sendmail(recipients, subject=subject, msg = msg)

View File

@ -94,8 +94,6 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) {
for(f in item_flds_normal) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_normal[f], true); for(f in item_flds_normal) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_normal[f], true);
for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], false); for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], false);
} }
if (doc.docstatus==1) unhide_field('recurring_invoice');
else hide_field('recurring_invoice');
if(doc.customer) unhide_field('contact_section'); if(doc.customer) unhide_field('contact_section');
else hide_field('contact_section'); else hide_field('contact_section');
@ -499,12 +497,6 @@ cur_frm.cscript.view_ledger_entry = function(){
wn.set_route('Report', 'GL Entry', 'General Ledger', 'Voucher No='+cur_frm.doc.name); wn.set_route('Report', 'GL Entry', 'General Ledger', 'Voucher No='+cur_frm.doc.name);
} }
// Default values for recurring invoices
cur_frm.cscript.convert_into_recurring_invoice = function(doc, dt, dn) {
if (doc.convert_into_recurring_invoice)
get_server_fields('set_default_recurring_values','','',doc, dt, dn, 0);
}
cur_frm.cscript.on_submit = function(doc, cdt, cdn) { cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
var args = { var args = {
type: 'Sales Invoice', type: 'Sales Invoice',
@ -513,11 +505,28 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
cur_frm.cscript.notify(doc, args); cur_frm.cscript.notify(doc, args);
} }
cur_frm.cscript.invoice_period_from_date = function(doc, dt, dn) { cur_frm.cscript.convert_into_recurring_invoice = function(doc, dt, dn) {
if(doc.invoice_period_from_date) { // set default values for recurring invoices
var recurring_type_map = { 'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12 }; if(doc.convert_into_recurring_invoice) {
var owner_email = doc.owner=="Administrator"
? wn.user_info("Administrator").email
: doc.owner;
doc.notification_email_address = $.map([cstr(owner_email),
cstr(doc.contact_email)], function(v) { return v || null; }).join(", ");
doc.repeat_on_day_of_month = wn.datetime.str_to_obj(doc.posting_date).getDate();
}
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
}
var months = $(recurring_type_map).attr(doc.recurring_type); cur_frm.cscript.invoice_period_from_date = function(doc, dt, dn) {
// set invoice_period_to_date
if(doc.invoice_period_from_date) {
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
'Yearly': 12};
var months = recurring_type_map[doc.recurring_type];
if(months) { if(months) {
var to_date = wn.datetime.add_months(doc.invoice_period_from_date, var to_date = wn.datetime.add_months(doc.invoice_period_from_date,
months); months);
@ -525,4 +534,4 @@ cur_frm.cscript.invoice_period_from_date = function(doc, dt, dn) {
refresh_field('invoice_period_to_date'); refresh_field('invoice_period_to_date');
} }
} }
} }

View File

@ -40,9 +40,90 @@ class DocType(TransactionBase):
self.tname = 'Sales Invoice Item' self.tname = 'Sales Invoice Item'
self.fname = 'entries' self.fname = 'entries'
def autoname(self): def autoname(self):
self.doc.name = make_autoname(self.doc.naming_series+ '.#####') self.doc.name = make_autoname(self.doc.naming_series+ '.#####')
def validate(self):
self.so_dn_required()
self.validate_proj_cust()
sales_com_obj = get_obj('Sales Common')
sales_com_obj.check_stop_sales_order(self)
sales_com_obj.check_active_sales_items(self)
sales_com_obj.check_conversion_rate(self)
sales_com_obj.validate_max_discount(self, 'entries') #verify whether rate is not greater than tolerance
sales_com_obj.get_allocated_sum(self) # this is to verify that the allocated % of sales persons is 100%
sales_com_obj.validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date')
self.validate_customer()
self.validate_customer_account()
self.validate_debit_acc()
self.validate_fixed_asset_account()
self.add_remarks()
if cint(self.doc.is_pos):
self.validate_pos()
self.validate_write_off_account()
if cint(self.doc.update_stock):
sl = get_obj('Stock Ledger')
sl.validate_serial_no(self, 'entries')
sl.validate_serial_no(self, 'packing_details')
self.validate_item_code()
self.update_current_stock()
self.validate_delivery_note()
self.set_in_words()
if not self.doc.is_opening:
self.doc.is_opening = 'No'
self.set_aging_date()
self.clear_advances()
self.set_against_income_account()
self.validate_c_form()
def on_submit(self):
if cint(self.doc.is_pos) == 1:
if cint(self.doc.update_stock) == 1:
sl_obj = get_obj("Stock Ledger")
sl_obj.validate_serial_no_warehouse(self, 'entries')
sl_obj.validate_serial_no_warehouse(self, 'packing_details')
sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
self.update_stock_ledger(update_stock=1)
else:
# Check for Approving Authority
if not self.doc.recurring_id:
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total, self)
self.check_prev_docstatus()
get_obj("Sales Common").update_prevdoc_detail(1,self)
# this sequence because outstanding may get -ve
self.make_gl_entries()
if not cint(self.doc.is_pos) == 1:
self.update_against_document_in_jv()
self.update_c_form()
self.convert_to_recurring()
def on_cancel(self):
if cint(self.doc.is_pos) == 1:
if cint(self.doc.update_stock) == 1:
sl = get_obj('Stock Ledger')
sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
self.update_stock_ledger(update_stock = -1)
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
self.check_next_docstatus()
sales_com_obj.update_prevdoc_detail(0, self)
self.make_gl_entries(is_cancel=1)
def on_update_after_submit(self):
self.convert_into_recurring()
def set_pos_fields(self): def set_pos_fields(self):
@ -428,40 +509,6 @@ class DocType(TransactionBase):
d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0 d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
def validate(self):
self.so_dn_required()
self.validate_proj_cust()
sales_com_obj = get_obj('Sales Common')
sales_com_obj.check_stop_sales_order(self)
sales_com_obj.check_active_sales_items(self)
sales_com_obj.check_conversion_rate(self)
sales_com_obj.validate_max_discount(self, 'entries') #verify whether rate is not greater than tolerance
sales_com_obj.get_allocated_sum(self) # this is to verify that the allocated % of sales persons is 100%
sales_com_obj.validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date')
self.validate_customer()
self.validate_customer_account()
self.validate_debit_acc()
self.validate_fixed_asset_account()
self.add_remarks()
if cint(self.doc.is_pos):
self.validate_pos()
self.validate_write_off_account()
if cint(self.doc.update_stock):
sl = get_obj('Stock Ledger')
sl.validate_serial_no(self, 'entries')
sl.validate_serial_no(self, 'packing_details')
self.validate_item_code()
self.update_current_stock()
self.validate_delivery_note()
self.set_in_words()
if not self.doc.is_opening:
self.doc.is_opening = 'No'
self.set_aging_date()
self.clear_advances()
self.set_against_income_account()
self.validate_c_form()
def get_warehouse(self): def get_warehouse(self):
w = webnotes.conn.sql("select warehouse from `tabPOS Setting` where ifnull(user,'') = '%s' and company = '%s'" % (session['user'], self.doc.company)) w = webnotes.conn.sql("select warehouse from `tabPOS Setting` where ifnull(user,'') = '%s' and company = '%s'" % (session['user'], self.doc.company))
w = w and w[0][0] or '' w = w and w[0][0] or ''
@ -598,80 +645,13 @@ class DocType(TransactionBase):
if submit_jv: if submit_jv:
msgprint("Journal Voucher : " + cstr(submit_jv[0][0]) + " has been created against " + cstr(self.doc.doctype) + ". So " + cstr(self.doc.doctype) + " cannot be Cancelled.") msgprint("Journal Voucher : " + cstr(submit_jv[0][0]) + " has been created against " + cstr(self.doc.doctype) + ". So " + cstr(self.doc.doctype) + " cannot be Cancelled.")
raise Exception, "Validation Error." raise Exception, "Validation Error."
@property
def on_submit(self): def meta(self):
if cint(self.doc.is_pos) == 1: if not hasattr(self, "_meta"):
if cint(self.doc.update_stock) == 1: self._meta = webnotes.get_doctype(self.doc.doctype)
sl_obj = get_obj("Stock Ledger") return self._meta
sl_obj.validate_serial_no_warehouse(self, 'entries')
sl_obj.validate_serial_no_warehouse(self, 'packing_details')
sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
self.update_stock_ledger(update_stock=1)
else:
# Check for Approving Authority
if not self.doc.recurring_id:
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total, self)
self.check_prev_docstatus()
get_obj("Sales Common").update_prevdoc_detail(1,self)
# this sequence because outstanding may get -ve
self.make_gl_entries()
if not cint(self.doc.is_pos) == 1:
self.update_against_document_in_jv()
self.update_c_form()
def on_cancel(self):
if cint(self.doc.is_pos) == 1:
if cint(self.doc.update_stock) == 1:
sl = get_obj('Stock Ledger')
sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
self.update_stock_ledger(update_stock = -1)
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
self.check_next_docstatus()
sales_com_obj.update_prevdoc_detail(0, self)
self.make_gl_entries(is_cancel=1)
def set_default_recurring_values(self):
from webnotes.utils import cstr
owner_email = self.doc.owner
if owner_email.lower() == 'administrator':
owner_email = cstr(webnotes.conn.get_value("Profile", "Administrator", "email"))
ret = {
'repeat_on_day_of_month' : getdate(self.doc.posting_date).day,
'notification_email_address' : ', '.join([owner_email, cstr(self.doc.contact_email)]),
}
return ret
def validate_notification_email_id(self):
if self.doc.notification_email_address:
from webnotes.utils import validate_email_add
for add in self.doc.notification_email_address.replace('\n', '').replace(' ', '').split(","):
if add and not validate_email_add(add):
msgprint("%s is not a valid email address" % add, raise_exception=1)
else:
msgprint("Notification Email Addresses not specified for recurring invoice",
raise_exception=1)
def on_update_after_submit(self):
self.convert_into_recurring()
def convert_into_recurring(self): def convert_into_recurring(self):
if self.doc.convert_into_recurring_invoice: if self.doc.convert_into_recurring_invoice:
self.validate_notification_email_id() self.validate_notification_email_id()
@ -685,6 +665,53 @@ class DocType(TransactionBase):
webnotes.conn.set(self.doc, 'recurring_id', make_autoname('RECINV/.#####')) webnotes.conn.set(self.doc, 'recurring_id', make_autoname('RECINV/.#####'))
elif self.doc.recurring_id: elif self.doc.recurring_id:
webnotes.conn.sql("""update `tabSales Invoice` set convert_into_recurring_invoice = 0 where recurring_id = %s""", self.doc.recurring_id) webnotes.conn.sql("""update `tabSales Invoice` set convert_into_recurring_invoice = 0 where recurring_id = %s""", self.doc.recurring_id)
def validate_notification_email_id(self):
if self.doc.notification_email_address:
email_list = filter(None, [cstr(email).strip() for email in
self.doc.notification_email_address.replace("\n", "").split(",")])
from webnotes.utils import validate_email_add
for email in email_list:
if not validate_email_add(email):
msgprint("%s is not a valid email address" % add, raise_exception=1)
else:
msgprint(
_("Notification Email Addresses not specified for recurring invoice",
raise_exception=1)
for add in self.doc.notification_email_address.replace('\n', '').replace(' ', '').split(","):
if add and not validate_email_add(add):
msgprint("%s is not a valid email address" % add, raise_exception=1)
else:
msgprint("Notification Email Addresses not specified for recurring invoice",
raise_exception=1)
# def set_default_recurring_values(self):
# from webnotes.utils import cstr
#
# owner_email = self.doc.owner
# if owner_email.lower() == 'administrator':
# owner_email = cstr(webnotes.conn.get_value("Profile", "Administrator", "email"))
#
# ret = {
# 'repeat_on_day_of_month' : getdate(self.doc.posting_date).day,
# 'notification_email_address' : ', '.join([owner_email, cstr(self.doc.contact_email)]),
# }
# return ret
#
def set_next_date(self): def set_next_date(self):
""" Set next date on which auto invoice will be created""" """ Set next date on which auto invoice will be created"""
@ -709,3 +736,193 @@ class DocType(TransactionBase):
webnotes.conn.set(self.doc, 'next_date', next_date) webnotes.conn.set(self.doc, 'next_date', next_date)
def manage_recurring_invoices():
"""
Create recurring invoices on specific date by copying the original one
and notify the concerned people
"""
rv = webnotes.conn.sql("""select name, recurring_id from `tabSales Invoice` \
where ifnull(convert_into_recurring_invoice, 0) = 1 and next_date = %s \
and next_date <= ifnull(end_date, '2199-12-31') and docstatus=1""", nowdate())
exception_list = []
for d in rv:
if not webnotes.conn.sql("""select name from `tabSales Invoice` \
where posting_date = %s and recurring_id = %s and docstatus=1""", (nowdate(), d[1])):
try:
prev_rv = get_obj('Sales Invoice', d[0], with_children=1)
new_rv = create_new_invoice(prev_rv)
send_notification(new_rv)
webnotes.conn.commit()
except Exception, e:
webnotes.conn.rollback()
webnotes.conn.begin()
webnotes.conn.sql("update `tabSales Invoice` set \
convert_into_recurring_invoice = 0 where name = %s", d[0])
notify_errors(d[0], prev_rv.doc.owner)
webnotes.conn.commit()
exception_list.append(e)
finally:
webnotes.conn.begin()
if exception_list:
exception_message = "\n\n".join([cstr(d) for d in exception_list])
raise Exception, exception_message
def notify_errors(inv, owner):
import webnotes
import website
exception_msg = """
Dear User,
An error occured while creating recurring invoice from %s (at %s).
May be there are some invalid email ids mentioned in the invoice.
To stop sending repetitive error notifications from the system, we have unchecked
"Convert into Recurring" field in the invoice %s.
Please correct the invoice and make the invoice recurring again.
<b>It is necessary to take this action today itself for the above mentioned recurring invoice \
to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field \
of this invoice for generating the recurring invoice.</b>
Regards,
Administrator
""" % (inv, website.get_site_address(), inv)
subj = "[Urgent] Error while creating recurring invoice from %s" % inv
from webnotes.profile import get_system_managers
recipients = get_system_managers()
owner_email = webnotes.conn.get_value("Profile", owner, "email")
if not owner_email in recipients:
recipients.append(owner_email)
assign_task_to_owner(inv, exception_msg, recipients)
sendmail(recipients, subject=subj, msg = exception_msg)
def assign_task_to_owner(inv, msg, users):
for d in users:
if d.lower() == 'administrator':
d = webnotes.conn.sql("select ifnull(email_id, '') \
from `tabProfile` where name = 'Administrator'")[0][0]
from webnotes.widgets.form import assign_to
args = {
'assign_to' : d,
'doctype' : 'Sales Invoice',
'name' : inv,
'description' : msg,
'priority' : 'Urgent'
}
assign_to.add(args)
def create_new_invoice(prev_rv):
# clone rv
new_rv = clone(prev_rv)
mdict = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
mcount = mdict[prev_rv.doc.recurring_type]
# update new rv
new_rv.doc.posting_date = new_rv.doc.next_date
new_rv.doc.aging_date = new_rv.doc.next_date
new_rv.doc.due_date = add_days(new_rv.doc.next_date, cint(date_diff(prev_rv.doc.due_date, prev_rv.doc.posting_date)))
new_rv.doc.invoice_period_from_date = get_next_date(new_rv.doc.invoice_period_from_date, mcount)
new_rv.doc.invoice_period_to_date = get_next_date(new_rv.doc.invoice_period_to_date, mcount)
new_rv.doc.owner = prev_rv.doc.owner
new_rv.doc.save()
# submit and after submit
new_rv.submit()
new_rv.update_after_submit()
return new_rv
def get_next_date(dt, mcount):
import datetime
m = getdate(dt).month + mcount
y = getdate(dt).year
d = getdate(dt).day
if m > 12:
m, y = m-12, y+1
try:
next_month_date = datetime.date(y, m, d)
except:
import calendar
last_day = calendar.monthrange(y, m)[1]
next_month_date = datetime.date(y, m, last_day)
return next_month_date.strftime("%Y-%m-%d")
def send_notification(new_rv):
"""Notify concerned persons about recurring invoice generation"""
subject = "Invoice : " + new_rv.doc.name
com = new_rv.doc.company # webnotes.conn.get_value('Control Panel', '', 'letter_head')
hd = '''<div><h2>%s</h2></div>
<div><h3>Invoice: %s</h3></div>
<table cellspacing= "5" cellpadding="5" width = "100%%">
<tr>
<td width = "50%%"><b>Customer</b><br>%s<br>%s</td>
<td width = "50%%">Invoice Date : %s<br>Invoice Period : %s to %s <br>Due Date : %s</td>
</tr>
</table>
''' % (com, new_rv.doc.name, new_rv.doc.customer_name, new_rv.doc.address_display, getdate(new_rv.doc.posting_date).strftime("%d-%m-%Y"), \
getdate(new_rv.doc.invoice_period_from_date).strftime("%d-%m-%Y"), getdate(new_rv.doc.invoice_period_to_date).strftime("%d-%m-%Y"),\
getdate(new_rv.doc.due_date).strftime("%d-%m-%Y"))
tbl = '''<table border="1px solid #CCC" width="100%%" cellpadding="0px" cellspacing="0px">
<tr>
<td width = "15%%" bgcolor="#CCC" align="left"><b>Item</b></td>
<td width = "40%%" bgcolor="#CCC" align="left"><b>Description</b></td>
<td width = "15%%" bgcolor="#CCC" align="center"><b>Qty</b></td>
<td width = "15%%" bgcolor="#CCC" align="center"><b>Rate</b></td>
<td width = "15%%" bgcolor="#CCC" align="center"><b>Amount</b></td>
</tr>
'''
for d in getlist(new_rv.doclist, 'entries'):
tbl += '<tr><td>' + d.item_code +'</td><td>' + d.description+'</td><td>' + cstr(d.qty) +'</td><td>' + cstr(d.basic_rate) +'</td><td>' + cstr(d.amount) +'</td></tr>'
tbl += '</table>'
totals =''' <table cellspacing= "5" cellpadding="5" width = "100%%">
<tr>
<td width = "50%%"></td>
<td width = "50%%">
<table width = "100%%">
<tr>
<td width = "50%%">Net Total: </td><td>%s </td>
</tr><tr>
<td width = "50%%">Total Tax: </td><td>%s </td>
</tr><tr>
<td width = "50%%">Grand Total: </td><td>%s</td>
</tr><tr>
<td width = "50%%">In Words: </td><td>%s</td>
</tr>
</table>
</td>
</tr>
<tr><td>Terms and Conditions:</td></tr>
<tr><td>%s</td></tr>
</table>
''' % (new_rv.doc.net_total, new_rv.doc.other_charges_total,new_rv.doc.grand_total, new_rv.doc.in_words,new_rv.doc.terms)
msg = hd + tbl + totals
recipients = new_rv.doc.notification_email_address.replace('\n', '').replace(' ', '').split(",")
sendmail(recipients, subject=subject, msg = msg)

View File

@ -2,12 +2,13 @@
{ {
"owner": "Administrator", "owner": "Administrator",
"docstatus": 0, "docstatus": 0,
"creation": "2012-10-10 10:52:22", "creation": "2012-09-10 12:22:26",
"modified_by": "Administrator", "modified_by": "Administrator",
"modified": "2012-09-07 11:56:59" "modified": "2012-11-30 12:29:56"
}, },
{ {
"is_submittable": 1, "is_submittable": 1,
"autoname": "naming_series:",
"allow_attach": 1, "allow_attach": 1,
"default_print_format": "Standard", "default_print_format": "Standard",
"search_fields": "posting_date, due_date, debit_to, fiscal_year, grand_total, outstanding_amount", "search_fields": "posting_date, due_date, debit_to, fiscal_year, grand_total, outstanding_amount",
@ -34,6 +35,15 @@
"name": "Sales Invoice", "name": "Sales Invoice",
"doctype": "DocType" "doctype": "DocType"
}, },
{
"print_hide": 1,
"oldfieldtype": "Section Break",
"doctype": "DocField",
"label": "Basic Info",
"fieldname": "basic_info",
"fieldtype": "Section Break",
"permlevel": 0
},
{ {
"print_hide": 0, "print_hide": 0,
"oldfieldtype": "Column Break", "oldfieldtype": "Column Break",
@ -217,9 +227,9 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"allow_on_submit": 1,
"oldfieldtype": "Table", "oldfieldtype": "Table",
"colour": "White:FFF", "colour": "White:FFF",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Entries", "label": "Entries",
"oldfieldname": "entries", "oldfieldname": "entries",
@ -428,9 +438,9 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"allow_on_submit": 1,
"oldfieldtype": "Table", "oldfieldtype": "Table",
"colour": "White:FFF", "colour": "White:FFF",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Taxes and Charges1", "label": "Taxes and Charges1",
"oldfieldname": "other_charges", "oldfieldname": "other_charges",
@ -1174,6 +1184,7 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"depends_on": "eval:doc.docstatus<2",
"colour": "White:FFF", "colour": "White:FFF",
"doctype": "DocField", "doctype": "DocField",
"label": "Recurring Invoice", "label": "Recurring Invoice",
@ -1193,7 +1204,7 @@
"print_hide": 1, "print_hide": 1,
"description": "Check if recurring invoice, uncheck to stop recurring or put proper End Date", "description": "Check if recurring invoice, uncheck to stop recurring or put proper End Date",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.docstatus==1", "depends_on": "eval:doc.docstatus<2",
"colour": "White:FFF", "colour": "White:FFF",
"allow_on_submit": 1, "allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
@ -1205,23 +1216,24 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"allow_on_submit": 1,
"description": "Select the period when the invoice will be generated automatically", "description": "Select the period when the invoice will be generated automatically",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"colour": "White:FFF",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Recurring Type", "label": "Recurring Type",
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly", "permlevel": 0,
"fieldname": "recurring_type", "fieldname": "recurring_type",
"fieldtype": "Select", "fieldtype": "Select",
"permlevel": 0 "options": "Monthly\nQuarterly\nHalf-yearly\nYearly"
}, },
{ {
"print_hide": 1, "print_hide": 1,
"allow_on_submit": 1,
"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 ",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Repeat on Day of Month", "label": "Repeat on Day of Month",
"fieldname": "repeat_on_day_of_month", "fieldname": "repeat_on_day_of_month",
@ -1230,11 +1242,11 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"allow_on_submit": 1,
"description": "Start date of the invoice period", "description": "Start date of the invoice period",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"colour": "White:FFF", "colour": "White:FFF",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Invoice Period From Date", "label": "Invoice Period From Date",
"fieldname": "invoice_period_from_date", "fieldname": "invoice_period_from_date",
@ -1243,10 +1255,10 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"allow_on_submit": 1,
"description": "End date of the invoice period", "description": "End date of the invoice period",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Invoice Period To Date", "label": "Invoice Period To Date",
"fieldname": "invoice_period_to_date", "fieldname": "invoice_period_to_date",
@ -1264,10 +1276,10 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"allow_on_submit": 1,
"description": "Enter email id separated by commas, invoice will be mailed automatically on particular date", "description": "Enter email id separated by commas, invoice will be mailed automatically on particular date",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "Notification Email Address", "label": "Notification Email Address",
"fieldname": "notification_email_address", "fieldname": "notification_email_address",
@ -1276,9 +1288,10 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"description": "The unique id for tracking all recurring invoices ", "description": "The unique id for tracking all recurring invoices.\u00a0It is generated on submit.",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"colour": "White:FFF",
"doctype": "DocField", "doctype": "DocField",
"label": "Recurring Id", "label": "Recurring Id",
"fieldname": "recurring_id", "fieldname": "recurring_id",
@ -1287,9 +1300,10 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"description": "The date on which next invoice will be generated ", "description": "The date on which next invoice will be generated. It is generated on submit.\n",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"colour": "White:FFF",
"doctype": "DocField", "doctype": "DocField",
"label": "Next Date", "label": "Next Date",
"fieldname": "next_date", "fieldname": "next_date",
@ -1298,10 +1312,10 @@
}, },
{ {
"print_hide": 1, "print_hide": 1,
"allow_on_submit": 1,
"description": "The date on which recurring invoice will be stop", "description": "The date on which recurring invoice will be stop",
"no_copy": 1, "no_copy": 1,
"depends_on": "eval:doc.convert_into_recurring_invoice==1", "depends_on": "eval:doc.convert_into_recurring_invoice==1",
"allow_on_submit": 1,
"doctype": "DocField", "doctype": "DocField",
"label": "End Date", "label": "End Date",
"fieldname": "end_date", "fieldname": "end_date",