From 110047ea5b8c4bac7b4d5677fe153d72ac95f0dc Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 10 Feb 2012 10:48:35 +0530 Subject: [PATCH] Debit/Credit note creation on purchase/sales return --- erpnext/accounts/__init__.py | 207 +++++++++++++++ .../sales_and_purchase_return_wizard.js | 249 ++++++++++-------- 2 files changed, 350 insertions(+), 106 deletions(-) diff --git a/erpnext/accounts/__init__.py b/erpnext/accounts/__init__.py index 0f98d2bc6a..c3c50052e4 100644 --- a/erpnext/accounts/__init__.py +++ b/erpnext/accounts/__init__.py @@ -1,4 +1,6 @@ import webnotes +from webnotes.utils import flt +from webnotes.model.code import get_obj def get_default_bank_account(): """ @@ -11,3 +13,208 @@ def get_default_bank_account(): WHERE name=%s AND docstatus<2""", company) if res: return res[0][0] + + +def get_new_jv_details(): + """ + Get details which will help create new jv on sales/purchase return + """ + doclist = webnotes.form_dict.get('doclist') + fiscal_year = webnotes.form_dict.get('fiscal_year') + if not (isinstance(doclist, basestring) and isinstance(fiscal_year, basestring)): return + + import json + doclist = json.loads(doclist) + doc, children = doclist[0], doclist[1:] + + if doc.get('return_type')=='Sales Return': + if doc.get('sales_invoice_no'): + return get_invoice_details(doc, children, fiscal_year) + elif doc.get('delivery_note_no'): + return get_delivery_note_details(doc, children, fiscal_year) + + elif doc.get('purchase_receipt_no'): + return get_purchase_receipt_details(doc, children, fiscal_year) + + +def get_invoice_details(doc, children, fiscal_year): + """ + Gets details from an invoice to make new jv + Returns [{ + 'account': , + 'balance': , + 'debit': , + 'credit': , + 'against_invoice': , + 'against_payable': + }, { ... }, ...] + """ + if doc.get('return_type')=='Sales Return': + obj = get_obj('Receivable Voucher', doc.get('sales_invoice_no'), with_children=1) + else: + obj = get_obj('Payable Voucher', doc.get('purchase_invoice_no'), with_children=1) + if not obj.doc.docstatus==1: return + + # Build invoice account jv detail record + invoice_rec = get_invoice_account_jv_record(doc, children, fiscal_year, obj) + + # Build item accountwise jv detail records + item_accountwise_list = get_item_accountwise_jv_record(doc, children, fiscal_year, obj) + + return [invoice_rec] + item_accountwise_list + + +def get_invoice_account_jv_record(doc, children, fiscal_year, obj): + """ + Build customer/supplier account jv detail record + """ + # Calculate total return amount + total_amt = sum([(flt(ch.get('rate')) * flt(ch.get('returned_qty'))) for ch in children]) + + ret = {} + + if doc.get('return_type')=='Sales Return': + account = obj.doc.debit_to + ret['against_invoice'] = doc.get('sales_invoice_no') + ret['credit'] = total_amt + else: + account = obj.doc.credit_to + ret['against_voucher'] = doc.get('purchase_invoice_no') + ret['debit'] = total_amt + + ret.update({ + 'account': account, + 'balance': get_obj('GL Control').get_bal(account + "~~~" + fiscal_year) + }) + + return ret + + +def get_item_accountwise_jv_record(doc, children, fiscal_year, obj): + """ + Build item accountwise jv detail records + """ + if doc.get('return_type')=='Sales Return': + amt_field = 'debit' + ac_field = 'income_account' + else: + amt_field = 'credit' + ac_field = 'expense_head' + + inv_children = dict([[ic.fields.get('item_code'), ic] for ic in obj.doclist if ic.fields.get('item_code')]) + + accwise_list = [] + + for ch in children: + inv_ch = inv_children.get(ch.get('item_code')) + if not inv_ch: continue + + amount = flt(ch.get('rate')) * flt(ch.get('returned_qty')) + + accounts = [[jvd['account'], jvd['cost_center']] for jvd in accwise_list] + + if [inv_ch.fields.get(ac_field), inv_ch.fields.get('cost_center')] not in accounts: + rec = { + 'account': inv_ch.fields.get(ac_field), + 'cost_center': inv_ch.fields.get('cost_center'), + 'balance': get_obj('GL Control').get_bal(inv_ch.fields.get(ac_field) + "~~~" + fiscal_year) + } + rec[amt_field] = amount + accwise_list.append(rec) + else: + rec = accwise_list[accounts.index([inv_ch.fields.get(ac_field), inv_ch.fields.get('cost_center')])] + rec[amt_field] = rec[amt_field] + amount + + return accwise_list + + +def get_jv_details_from_inv_list(doc, children, fiscal_year, inv_list, jv_details_list): + """ + Get invoice details and make jv detail records + """ + for inv in inv_list: + if not inv[0]: continue + + if doc.get('return_type')=='Sales Return': + doc['sales_invoice_no'] = inv[0] + else: + doc['purchase_invoice_no'] = inv[0] + + jv_details = get_invoice_details(doc, children, fiscal_year) + + if jv_details and len(jv_details)>1: jv_details_list.extend(jv_details) + + return jv_details_list + + +def get_prev_doc_list(obj, prev_doctype): + """ + Returns a list of previous doc's names + """ + prevdoc_list = [] + for ch in obj.doclist: + if ch.fields.get('prevdoc_docname') and ch.fields.get('prevdoc_doctype')==prev_doctype: + prevdoc_list.append(ch.fields.get('prevdoc_docname')) + return prevdoc_list + + +def get_inv_list(table, field, value): + """ + Returns invoice list + """ + if isinstance(value, basestring): + return webnotes.conn.sql("""\ + SELECT DISTINCT parent FROM `%s` + WHERE %s='%s' AND docstatus=1""" % (table, field, value)) + elif isinstance(value, list): + return webnotes.conn.sql("""\ + SELECT DISTINCT parent FROM `%s` + WHERE %s IN ("%s") AND docstatus=1""" % (table, field, '", "'.join(value))) + else: + return [] + + +def get_delivery_note_details(doc, children, fiscal_year): + """ + Gets sales invoice numbers from delivery note details + and returns detail records for jv + """ + jv_details_list = [] + + dn_obj = get_obj('Delivery Note', doc['delivery_note_no'], with_children=1) + + inv_list = get_inv_list('tabRV Detail', 'delivery_note', doc['delivery_note_no']) + + if inv_list: + jv_details_list = get_jv_details_from_inv_list(doc, children, fiscal_year, inv_list, jv_details_list) + + if not (inv_list and jv_details_list): + so_list = get_prev_doc_list(dn_obj, 'Sales Order') + inv_list = get_inv_list('tabRV Detail', 'sales_order', so_list) + if inv_list: + jv_details_list = get_jv_details_from_inv_list(doc, children, fiscal_year, inv_list, jv_details_list) + + return jv_details_list + + +def get_purchase_receipt_details(doc, children, fiscal_year): + """ + Gets purchase invoice numbers from purchase receipt details + and returns detail records for jv + """ + jv_details_list = [] + + pr_obj = get_obj('Purchase Receipt', doc['purchase_receipt_no'], with_children=1) + + inv_list = get_inv_list('tabPV Detail', 'purchase_receipt', doc['purchase_receipt_no']) + + if inv_list: + jv_details_list = get_jv_details_from_inv_list(doc, children, fiscal_year, inv_list, jv_details_list) + + if not (inv_list and jv_details_list): + po_list = get_prev_doc_list(pr_obj, 'Purchase Order') + inv_list = get_inv_list('tabPV Detail', 'purchase_order', po_list) + if inv_list: + jv_details_list = get_jv_details_from_inv_list(doc, children, fiscal_year, inv_list, jv_details_list) + + return jv_details_list diff --git a/erpnext/stock/doctype/sales_and_purchase_return_wizard/sales_and_purchase_return_wizard.js b/erpnext/stock/doctype/sales_and_purchase_return_wizard/sales_and_purchase_return_wizard.js index ed071fa3dd..46a59b94a2 100644 --- a/erpnext/stock/doctype/sales_and_purchase_return_wizard/sales_and_purchase_return_wizard.js +++ b/erpnext/stock/doctype/sales_and_purchase_return_wizard/sales_and_purchase_return_wizard.js @@ -1,176 +1,213 @@ // Onload //------------------------------- cur_frm.cscript.onload = function(doc,dt,dn){ - if(!doc.return_date) set_multiple(dt,dn,{return_date:get_today()}); - doc.delivery_note_no = ''; - doc.purchase_receipt_no = ''; - doc.sales_invoice_no = ''; - doc.return_type =''; - refresh_many(['delivery_note_no', 'sales_invoice_no', 'purchase_receipt_no', 'return_type']); + if(!doc.return_date) set_multiple(dt,dn,{return_date:get_today()}); + doc.delivery_note_no = ''; + doc.purchase_receipt_no = ''; + doc.sales_invoice_no = ''; + doc.return_type =''; + refresh_many(['delivery_note_no', 'sales_invoice_no', 'purchase_receipt_no', 'return_type']); } // Link field query //-------------------------------- cur_frm.fields_dict.delivery_note_no.get_query = function(doc) { - return 'SELECT DISTINCT `tabDelivery Note`.name FROM `tabDelivery Note` WHERE `tabDelivery Note`.docstatus = 1 AND `tabDelivery Note`.%(key)s LIKE "%s" ORDER BY `tabDelivery Note`.name desc LIMIT 50'; + return 'SELECT DISTINCT `tabDelivery Note`.name FROM `tabDelivery Note` WHERE `tabDelivery Note`.docstatus = 1 AND `tabDelivery Note`.%(key)s LIKE "%s" ORDER BY `tabDelivery Note`.name desc LIMIT 50'; } cur_frm.fields_dict.sales_invoice_no.get_query = function(doc) { - return 'SELECT DISTINCT `tabReceivable Voucher`.name FROM `tabReceivable Voucher` WHERE `tabReceivable Voucher`.docstatus = 1 AND `tabReceivable Voucher`.%(key)s LIKE "%s" ORDER BY `tabReceivable Voucher`.name desc LIMIT 50'; + return 'SELECT DISTINCT `tabReceivable Voucher`.name FROM `tabReceivable Voucher` WHERE `tabReceivable Voucher`.docstatus = 1 AND `tabReceivable Voucher`.%(key)s LIKE "%s" ORDER BY `tabReceivable Voucher`.name desc LIMIT 50'; } cur_frm.fields_dict.purchase_receipt_no.get_query = function(doc) { - return 'SELECT DISTINCT `tabPurchase Receipt`.name FROM `tabPurchase Receipt` WHERE `tabPurchase Receipt`.docstatus = 1 AND `tabPurchase Receipt`.%(key)s LIKE "%s" ORDER BY `tabPurchase Receipt`.name desc LIMIT 50'; + return 'SELECT DISTINCT `tabPurchase Receipt`.name FROM `tabPurchase Receipt` WHERE `tabPurchase Receipt`.docstatus = 1 AND `tabPurchase Receipt`.%(key)s LIKE "%s" ORDER BY `tabPurchase Receipt`.name desc LIMIT 50'; } // Hide/unhide based on return type //---------------------------------- cur_frm.cscript.return_type = function(doc, cdt, cdn) { - hide_field(['purchase_receipt_no', 'delivery_note_no', 'sales_invoice_no', 'return_details', 'Get Items', 'Make Excise Invoice', 'Make Stock Entry', 'Make Debit Note', 'Make Credit Note']); - if(doc.return_type == 'Sales Return') - unhide_field(['delivery_note_no', 'sales_invoice_no', 'Get Items', 'return_details', 'Make Credit Note', 'Make Stock Entry', 'Make Excise Invoice']); - else if(doc.return_type == 'Purchase Return') - unhide_field(['purchase_receipt_no', 'Get Items', 'return_details', 'Make Debit Note', 'Make Stock Entry', 'Make Excise Invoice']); + var cp = locals['Control Panel']['Control Panel']; + hide_field(['purchase_receipt_no', 'delivery_note_no', 'sales_invoice_no', + 'return_details', 'Get Items', 'Make Excise Invoice', 'Make Stock Entry', + 'Make Debit Note', 'Make Credit Note']); - cur_frm.cscript.clear_fields(doc); + if(doc.return_type == 'Sales Return') { + unhide_field(['delivery_note_no', 'sales_invoice_no', 'Get Items', + 'return_details', 'Make Credit Note', 'Make Stock Entry']); + + if(cp.country == 'India') { unhide_field(['Make Excise Invoice']); } + + } else if(doc.return_type == 'Purchase Return') { + unhide_field(['purchase_receipt_no', 'Get Items', 'return_details', + 'Make Debit Note', 'Make Stock Entry']); + + if(cp.country == 'India') { unhide_field(['Make Excise Invoice']); } + } + + cur_frm.cscript.clear_fields(doc); } // Create item table //------------------------------- cur_frm.cscript['Get Items'] = function(doc, cdt, cdn) { - flag = 0 - if(doc.return_type == 'Sales Return') { - if (doc.delivery_note_no && doc.sales_invoice_no) { - msgprint("You can not enter both Delivery Note No and Sales Invoice No. Please enter any one."); - flag = 1; - } else if (!doc.delivery_note_no && !doc.sales_invoice_no) { - msgprint("Please enter Delivery Note No or Sales Invoice No to proceed"); - flag = 1; - } - } else if (doc.return_type == 'Purchase Return' && !doc.purchase_receipt_no) { - msgprint("Please enter Purchase Receipt No to proceed"); - flag = 1; - } - if (!flag) - $c_obj(make_doclist(doc.doctype, doc.name),'pull_item_details','', function(r, rt) { - refresh_many(['return_details', 'cust_supp', 'cust_supp_name', 'cust_supp_address']); - }); + flag = 0 + if(doc.return_type == 'Sales Return') { + if (doc.delivery_note_no && doc.sales_invoice_no) { + msgprint("You can not enter both Delivery Note No and Sales Invoice No. Please enter any one."); + flag = 1; + } else if (!doc.delivery_note_no && !doc.sales_invoice_no) { + msgprint("Please enter Delivery Note No or Sales Invoice No to proceed"); + flag = 1; + } + } else if (doc.return_type == 'Purchase Return' && !doc.purchase_receipt_no) { + msgprint("Please enter Purchase Receipt No to proceed"); + flag = 1; + } + if (!flag) + $c_obj(make_doclist(doc.doctype, doc.name),'pull_item_details','', function(r, rt) { + refresh_many(['return_details', 'cust_supp', 'cust_supp_name', 'cust_supp_address']); + }); } // Clear fields //------------------------------- cur_frm.cscript.clear_fields = function(doc) { - doc.purchase_receipt_no, doc.delivery_note_no, doc.sales_invoice_no = '', '', ''; - var cl = getchildren('Return Detail', doc.name, 'return_details') - if(cl.length) $c_obj(make_doclist(doc.doctype, doc.name),'clear_return_table','', function(r, rt) {refresh_field('return_details')}); - refresh_many(['delivery_note_no', 'sales_invoice_no', 'purchase_receipt_no', 'return_details']); + doc.purchase_receipt_no, doc.delivery_note_no, doc.sales_invoice_no = '', '', ''; + var cl = getchildren('Return Detail', doc.name, 'return_details') + if(cl.length) $c_obj(make_doclist(doc.doctype, doc.name),'clear_return_table','', function(r, rt) {refresh_field('return_details')}); + refresh_many(['delivery_note_no', 'sales_invoice_no', 'purchase_receipt_no', 'return_details']); } // Make Stock Entry //------------------------------- cur_frm.cscript['Make Stock Entry'] = function(doc, cdt, cdn) { - var cl = getchildren('Return Detail', doc.name, 'return_details'); - if (!cl.length) - msgprint("Item table can not be blank. Please click on 'Get Items'."); - else if (!cur_frm.cscript.validate_returned_qty(cl)) { - se = cur_frm.cscript.map_parent_fields(doc,cdt,cdn); - cur_frm.cscript.map_child_fields(cl, se); - loaddoc('Stock Entry', se.name); - } + var cl = getchildren('Return Detail', doc.name, 'return_details'); + if (!cl.length) + msgprint("Item table can not be blank. Please click on 'Get Items'."); + else if (!cur_frm.cscript.validate_returned_qty(cl)) { + se = cur_frm.cscript.map_parent_fields(doc,cdt,cdn); + cur_frm.cscript.map_child_fields(cl, se); + loaddoc('Stock Entry', se.name); + } } // Validate returned qty //--------------------------- cur_frm.cscript.validate_returned_qty = function(cl) { - flag = 0 - for(var i = 0; i cl[i].qty) { - msgprint("Returned Qty can not be greater than qty. Please check for item: " + cl[i].item_code); - flag = 1 - } - } - return flag + flag = 0 + for(var i = 0; i cl[i].qty) { + msgprint("Returned Qty can not be greater than qty. Please check for item: " + cl[i].item_code); + flag = 1 + } + } + return flag } // map parent fields of stock entry //---------------------------------- cur_frm.cscript.map_parent_fields = function(doc, cdt, cdn) { - var se = LocalDB.create('Stock Entry'); - se = locals['Stock Entry'][se]; - se.posting_date = dateutil.obj_to_str(new Date()); - se.transfer_date = dateutil.obj_to_str(new Date()); - se.fiscal_year = sys_defaults.fiscal_year; - se.purpose = doc.return_type; - se.remarks = doc.return_type + ' of ' + (doc.delivery_note_no || doc.sales_invoice_no || doc.purchase_receipt_no); - if(doc.return_type == 'Sales Return'){ - se.delivery_note_no = doc.delivery_note_no; - se.sales_invoice_no = doc.sales_invoice_no; - se.customer = doc.cust_supp_name; - se.customer_name = doc.cust_supp_name; - se.customer_address = doc.cust_supp_address; - } - else if(doc.return_type == 'Purchase Return'){ - se.purchase_receipt_no = doc.purchase_receipt_no; - se.supplier = doc.cust_supp_name; - se.supplier_name = doc.cust_supp_name; - se.supplier_address = doc.cust_supp_address; - } - return se + var se = LocalDB.create('Stock Entry'); + se = locals['Stock Entry'][se]; + se.posting_date = dateutil.obj_to_str(new Date()); + se.transfer_date = dateutil.obj_to_str(new Date()); + se.fiscal_year = sys_defaults.fiscal_year; + se.purpose = doc.return_type; + se.remarks = doc.return_type + ' of ' + (doc.delivery_note_no || doc.sales_invoice_no || doc.purchase_receipt_no); + if(doc.return_type == 'Sales Return'){ + se.delivery_note_no = doc.delivery_note_no; + se.sales_invoice_no = doc.sales_invoice_no; + se.customer = doc.cust_supp_name; + se.customer_name = doc.cust_supp_name; + se.customer_address = doc.cust_supp_address; + } + else if(doc.return_type == 'Purchase Return'){ + se.purchase_receipt_no = doc.purchase_receipt_no; + se.supplier = doc.cust_supp_name; + se.supplier_name = doc.cust_supp_name; + se.supplier_address = doc.cust_supp_address; + } + return se } // map child fields of stock entry //--------------------------------- cur_frm.cscript.map_child_fields = function(cl, se) { - for(var i = 0; i