From 059922cef1036278ead50f391267cbc2b14e265c Mon Sep 17 00:00:00 2001 From: webnotes Date: Fri, 11 Jul 2014 11:20:44 +0530 Subject: [PATCH 1/9] Add Payment Reconciliation Feature/Tool --- .../payment_reconciliation/__init__.py | 0 .../payment_reconciliation.js | 25 +++ .../payment_reconciliation.json | 152 ++++++++++++++++++ .../payment_reconciliation.py | 14 ++ .../__init__.py | 0 .../payment_reconciliation_invoice.json | 59 +++++++ .../payment_reconciliation_invoice.py | 9 ++ .../__init__.py | 0 .../payment_reconciliation_payment.json | 71 ++++++++ .../payment_reconciliation_payment.py | 9 ++ 10 files changed, 339 insertions(+) create mode 100644 erpnext/accounts/doctype/payment_reconciliation/__init__.py create mode 100644 erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js create mode 100644 erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json create mode 100644 erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py create mode 100644 erpnext/accounts/doctype/payment_reconciliation_invoice/__init__.py create mode 100644 erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json create mode 100644 erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py create mode 100644 erpnext/accounts/doctype/payment_reconciliation_payment/__init__.py create mode 100644 erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json create mode 100644 erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py diff --git a/erpnext/accounts/doctype/payment_reconciliation/__init__.py b/erpnext/accounts/doctype/payment_reconciliation/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js new file mode 100644 index 00000000000..f0706db896f --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -0,0 +1,25 @@ +// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +// For license information, please see license.txt + +frappe.provide("erpnext.accounts"); + +erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({ + + onload: function() { + var me = this + this.frm.set_query ("party_account", function() { + return{ + filters:[ + ["Account", "company", "=", me.frm.doc.company], + ["Account", "group_or_ledger", "=", "Ledger"], + ["Account", "master_type", "in", ["Customer", "Supplier"]] + ] + }; + }); + } + +}); + +$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm})); + +cur_frm.add_fetch("party_account", "master_type", "party_type") \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json new file mode 100644 index 00000000000..7c53b739e60 --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -0,0 +1,152 @@ +{ + "creation": "2014-07-09 12:04:51.681583", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "permlevel": 0, + "reqd": 1 + }, + { + "depends_on": "", + "fieldname": "party_account", + "fieldtype": "Link", + "in_list_view": 0, + "label": "Party Account", + "options": "Account", + "permlevel": 0, + "reqd": 1, + "search_index": 0 + }, + { + "fieldname": "party_type", + "fieldtype": "Select", + "hidden": 0, + "in_list_view": 1, + "label": "Party Type", + "options": "Customer\nSupplier", + "permlevel": 0, + "reqd": 0 + }, + { + "fieldname": "bank_cash_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Bank / Cash Account", + "options": "Account", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, + { + "fieldname": "col_break1", + "fieldtype": "Column Break", + "label": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "permlevel": 0, + "search_index": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "To Date", + "permlevel": 0, + "search_index": 1 + }, + { + "fieldname": "minimum_amount", + "fieldtype": "Currency", + "label": "Minimum Amount", + "permlevel": 0 + }, + { + "fieldname": "maximum_amount", + "fieldtype": "Currency", + "label": "Maximum Amount", + "permlevel": 0 + }, + { + "fieldname": "get_unreconciled_entries_btn", + "fieldtype": "Button", + "label": "Get Unreconciled Entries", + "permlevel": 0 + }, + { + "fieldname": "sec_break1", + "fieldtype": "Section Break", + "label": "Unreconciled Payment Details", + "permlevel": 0 + }, + { + "fieldname": "payment_reconciliation_payments", + "fieldtype": "Table", + "label": "Payment Reconciliation Payments", + "options": "Payment Reconciliation Payment", + "permlevel": 0 + }, + { + "fieldname": "reconcile_btn", + "fieldtype": "Button", + "label": "Reconcile", + "permlevel": 0 + }, + { + "fieldname": "sec_break2", + "fieldtype": "Section Break", + "label": "Invoice/JV Details", + "permlevel": 0 + }, + { + "fieldname": "payment_reconciliation_invoices", + "fieldtype": "Table", + "label": "Payment Reconciliation Invoices", + "options": "Payment Reconciliation Invoice", + "permlevel": 0, + "read_only": 1 + } + ], + "issingle": 1, + "modified": "2014-07-10 18:04:50.893833", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Reconciliation", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "cancel": 0, + "create": 1, + "delete": 1, + "permlevel": 0, + "read": 1, + "role": "Accounts Manager", + "submit": 0, + "write": 1 + }, + { + "cancel": 0, + "create": 1, + "delete": 1, + "permlevel": 0, + "read": 1, + "role": "Accounts User", + "submit": 0, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py new file mode 100644 index 00000000000..8fb331714b7 --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -0,0 +1,14 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe + +from frappe.utils import flt + +from frappe import msgprint, _ + +from frappe.model.document import Document + +class PaymentReconciliation(Document): + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/__init__.py b/erpnext/accounts/doctype/payment_reconciliation_invoice/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json new file mode 100644 index 00000000000..008b3265d8d --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -0,0 +1,59 @@ +{ + "creation": "2014-07-09 16:14:23.672922", + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "fieldname": "invoice_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Invoice Type", + "options": "Sales Invoice\nPurchase Invoice\nJournal Voucher", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "invoice_number", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Invoice Number", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "invoice_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Invoice Date", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Outstanding Amount", + "permlevel": 0, + "read_only": 1 + } + ], + "istable": 1, + "modified": "2014-07-09 17:15:00.069551", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Reconciliation Invoice", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py new file mode 100644 index 00000000000..e136881514f --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class PaymentReconciliationInvoice(Document): + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/__init__.py b/erpnext/accounts/doctype/payment_reconciliation_payment/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json new file mode 100644 index 00000000000..cbff5b1e202 --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -0,0 +1,71 @@ +{ + "creation": "2014-07-09 16:13:35.452759", + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "fieldname": "journal_voucher", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Journal Voucher", + "options": "Journal Voucher", + "permlevel": 0, + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "permlevel": 0, + "read_only": 1 + }, + { + "default": "Sales Invoice", + "fieldname": "invoice_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Invoice Type", + "options": "Sales Invoice\nPurchase Invoice\nJournal Voucher", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "invoice_number", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Invoice Number", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "remark", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Remark", + "permlevel": 0, + "read_only": 1 + } + ], + "istable": 1, + "modified": "2014-07-09 17:00:18.705385", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Reconciliation Payment", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py new file mode 100644 index 00000000000..9082ef9bcf8 --- /dev/null +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class PaymentReconciliationPayment(Document): + pass \ No newline at end of file From 90b79fcd1fda8166413e9af8e7a0a3f2636dee01 Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Fri, 11 Jul 2014 15:23:26 +0530 Subject: [PATCH 2/9] Add Payment Reconciliation Feature/Tool - minor changes --- .../payment_reconciliation/payment_reconciliation.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 7c53b739e60..12253d00433 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -79,7 +79,7 @@ "permlevel": 0 }, { - "fieldname": "get_unreconciled_entries_btn", + "fieldname": "get_unreconciled_entries", "fieldtype": "Button", "label": "Get Unreconciled Entries", "permlevel": 0 @@ -98,7 +98,7 @@ "permlevel": 0 }, { - "fieldname": "reconcile_btn", + "fieldname": "reconcile", "fieldtype": "Button", "label": "Reconcile", "permlevel": 0 @@ -119,7 +119,7 @@ } ], "issingle": 1, - "modified": "2014-07-10 18:04:50.893833", + "modified": "2014-07-11 15:01:47.133918", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", From 0e57b971ad2c9d3bb7f828ef5ca9fd1ffc1c7c27 Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Fri, 11 Jul 2014 16:08:55 +0530 Subject: [PATCH 3/9] Add Payment Reconciliation Feature/Tool - minor changes --- .../payment_reconciliation.js | 17 ++++++++++++----- .../payment_reconciliation.py | 6 +++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index f0706db896f..b40691d0539 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -7,19 +7,26 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext onload: function() { var me = this - this.frm.set_query ("party_account", function() { + this.frm.set_query ('party_account', function() { return{ filters:[ - ["Account", "company", "=", me.frm.doc.company], - ["Account", "group_or_ledger", "=", "Ledger"], - ["Account", "master_type", "in", ["Customer", "Supplier"]] + ['Account', 'company', '=', me.frm.doc.company], + ['Account', 'group_or_ledger', '=', 'Ledger'], + ['Account', 'master_type', 'in', ['Customer', 'Supplier']] ] }; }); + }, + + get_unreconciled_entries: function() { + return this.frm.call({ + doc: me.frm.doc, + method: 'get_unreconciled_entries' + }); } }); $.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm})); -cur_frm.add_fetch("party_account", "master_type", "party_type") \ No newline at end of file +cur_frm.add_fetch('party_account', 'master_type', 'party_type') \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8fb331714b7..2eb188b0a90 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -11,4 +11,8 @@ from frappe import msgprint, _ from frappe.model.document import Document class PaymentReconciliation(Document): - pass \ No newline at end of file + def get_unreconciled_entries(self): + self.set('payment_reconciliation_payment', []) + jve = self.get_jv_entries() + self.create_payment_reconciliation_payment(jve) + From 345753ed195754aaeb30dbf59d858a296bf8bb27 Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Mon, 14 Jul 2014 16:38:27 +0530 Subject: [PATCH 4/9] Add Payment Reconciliation Feature/Tool - fetch unreconciled entries --- .../payment_reconciliation.js | 17 +++++-- .../payment_reconciliation.py | 45 ++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index b40691d0539..277fcf47050 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -19,10 +19,19 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext }, get_unreconciled_entries: function() { - return this.frm.call({ - doc: me.frm.doc, - method: 'get_unreconciled_entries' - }); + var me = this; + if (!this.frm.doc.company) { + msgprint(__("Please enter the Company")); + } + else if (!this.frm.doc.party_account) { + msgprint(__("Please enter the Party Account")); + } + else { + return this.frm.call({ + doc: me.frm.doc, + method: 'get_unreconciled_entries' + }); + } } }); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 2eb188b0a90..17dd5e70a50 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -14,5 +14,48 @@ class PaymentReconciliation(Document): def get_unreconciled_entries(self): self.set('payment_reconciliation_payment', []) jve = self.get_jv_entries() - self.create_payment_reconciliation_payment(jve) + self.add_payment_entries(jve) + def get_jv_entries(self): + self.validation() + + dr_or_cr = "credit" if self.party_type == "Customer" else "debit" + + #Add conditions for debit/credit, sorting by date and amount + cond = self.from_date and " and t1.posting_date >= '" + self.from_date + "'" or "" + cond += self.to_date and " and t1.posting_date <= '" + self.to_date + "'" or "" + + if self.minimum_amount: + cond += (" and ifnull(t2.%s), 0) >= %s") % (dr_or_cr, self.minimum_amount) + if self.maximum_amount: + cond += " and ifnull(t2.%s, 0) <= %s" % (dr_or_cr, self.maximum_amount) + + jve = frappe.db.sql(""" + select + t1.name as voucher_no, t1.posting_date, t1.remark, t2.account, + t2.name as voucher_detail_no, t2.%s, t2.is_advance + from + `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 + where + t1.name = t2.parent and t1.docstatus = 1 and t2.account = %s + and t2.%s > 0 and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' + and ifnull(t2.against_jv, '')='' %s + group by t1.name, t2.name """ % (dr_or_cr, '%s', dr_or_cr, cond), (self.party_account), + as_dict = True) + + return jve + + def add_payment_entries(self, jve): + self.set('payment_reconciliation_payments', []) + for e in jve: + ent = self.append('payment_reconciliation_payments', {}) + ent.journal_voucher = e.get('voucher_no') + ent.posting_date = e.get('posting_date') + ent.amount = flt(e.get('credit' or 'debit')) + ent.remark = e.get('remark') + + def validation(self): + self.check_mandatory() + + def check_mandatory(self): + pass From ec05d7f8a1eae1b3864eb4dd197527a872d85e35 Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Mon, 14 Jul 2014 18:19:15 +0530 Subject: [PATCH 5/9] Add Payment Reconciliation Feature/Tool - javascript validation --- .../payment_reconciliation.js | 15 +++++++-------- .../payment_reconciliation.py | 1 + .../payment_reconciliation_payment.json | 11 ++++++++++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 277fcf47050..cf62fd30735 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -20,20 +20,19 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext get_unreconciled_entries: function() { var me = this; - if (!this.frm.doc.company) { - msgprint(__("Please enter the Company")); - } - else if (!this.frm.doc.party_account) { - msgprint(__("Please enter the Party Account")); - } - else { + if(!this.frm.doc.company || !this.frm.doc.party_account) { + if(!this.frm.doc.company) { + msgprint(__("Please enter Company")); + } else { + msgprint(__("Please enter Party Account")); + } + } else { return this.frm.call({ doc: me.frm.doc, method: 'get_unreconciled_entries' }); } } - }); $.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm})); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 17dd5e70a50..b0b189e6d00 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -53,6 +53,7 @@ class PaymentReconciliation(Document): ent.posting_date = e.get('posting_date') ent.amount = flt(e.get('credit' or 'debit')) ent.remark = e.get('remark') + ent.voucher_detail_number = e.get('voucher_detail_no') def validation(self): self.check_mandatory() diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index cbff5b1e202..231dfae075b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -56,10 +56,19 @@ "label": "Remark", "permlevel": 0, "read_only": 1 + }, + { + "fieldname": "voucher_detail_number", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 0, + "label": "Voucher Detail Number", + "permlevel": 0, + "read_only": 1 } ], "istable": 1, - "modified": "2014-07-09 17:00:18.705385", + "modified": "2014-07-14 16:48:45.875052", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", From d934cd105b1597785839f821549d1c0c9e6219f8 Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Tue, 15 Jul 2014 18:01:36 +0530 Subject: [PATCH 6/9] Add Payment Reconciliation Feature/Tool - invoice entries table population --- .../payment_reconciliation.py | 95 +++++++++++++++---- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b0b189e6d00..fc1ff24109f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -12,25 +12,20 @@ from frappe.model.document import Document class PaymentReconciliation(Document): def get_unreconciled_entries(self): - self.set('payment_reconciliation_payment', []) - jve = self.get_jv_entries() - self.add_payment_entries(jve) + jv_entries = self.get_jv_entries() + self.add_payment_entries(jv_entries) + invoice_entries = self.get_invoice_entries() + + self.add_invoice_entries(invoice_entries) def get_jv_entries(self): - self.validation() + self.check_mandatory() dr_or_cr = "credit" if self.party_type == "Customer" else "debit" - - #Add conditions for debit/credit, sorting by date and amount - cond = self.from_date and " and t1.posting_date >= '" + self.from_date + "'" or "" - cond += self.to_date and " and t1.posting_date <= '" + self.to_date + "'" or "" - if self.minimum_amount: - cond += (" and ifnull(t2.%s), 0) >= %s") % (dr_or_cr, self.minimum_amount) - if self.maximum_amount: - cond += " and ifnull(t2.%s, 0) <= %s" % (dr_or_cr, self.maximum_amount) + cond = self.check_condition(dr_or_cr) - jve = frappe.db.sql(""" + jv_entries = frappe.db.sql(""" select t1.name as voucher_no, t1.posting_date, t1.remark, t2.account, t2.name as voucher_detail_no, t2.%s, t2.is_advance @@ -42,21 +37,83 @@ class PaymentReconciliation(Document): and ifnull(t2.against_jv, '')='' %s group by t1.name, t2.name """ % (dr_or_cr, '%s', dr_or_cr, cond), (self.party_account), as_dict = True) + return jv_entries - return jve - - def add_payment_entries(self, jve): + def add_payment_entries(self, jv_entries): self.set('payment_reconciliation_payments', []) - for e in jve: + for e in jv_entries: ent = self.append('payment_reconciliation_payments', {}) ent.journal_voucher = e.get('voucher_no') ent.posting_date = e.get('posting_date') - ent.amount = flt(e.get('credit' or 'debit')) + ent.amount = flt(e.get('credit')) or flt(e.get('debit')) ent.remark = e.get('remark') ent.voucher_detail_number = e.get('voucher_detail_no') - def validation(self): + def get_invoice_entries(self): + #Fetch JVs, Sales and Purchase Invoices for 'payment_reconciliation_invoices' to reconcile against + non_reconciled_invoices = [] self.check_mandatory() + dr_or_cr = "debit" if self.party_type == "Customer" else "credit" + + cond = self.check_condition(dr_or_cr) + + invoice_list = frappe.db.sql(""" + select + voucher_no, voucher_type, posting_date, ifnull(sum(ifnull(%s, 0)), 0) as amount + from + `tabGL Entry` + where + account = %s and ifnull(%s, 0) > 0 %s + group by voucher_no, voucher_type""" % (dr_or_cr, "%s", + dr_or_cr, cond), (self.party_account), as_dict=True) + + for d in invoice_list: + payment_amount = frappe.db.sql(""" + select + ifnull(sum(ifnull(%s, 0)), 0) + from + `tabGL Entry` + where + account = %s and against_voucher_type = %s and ifnull(against_voucher, '') = %s""", + (("credit" if self.party_type == "Customer" else "debit"), self.party_account, + d.voucher_type, d.voucher_no)) + + payment_amount = payment_amount[0][0] if payment_amount else 0 + + if d.amount > payment_amount: + non_reconciled_invoices.append({'voucher_no': d.voucher_no, + 'voucher_type': d.voucher_type, + 'posting_date': d.posting_date, + 'amount': flt(d.amount), + 'outstanding_amount': d.amount - payment_amount}) + + return non_reconciled_invoices + + + def add_invoice_entries(self, non_reconciled_invoices): + #Populate 'payment_reconciliation_invoices' with JVs and Invoices to reconcile against + self.set('payment_reconciliation_invoices', []) + if not non_reconciled_invoices: + return + for e in non_reconciled_invoices: + ent = self.append('payment_reconciliation_invoices', {}) + ent.invoice_type = e.get('voucher_type') + ent.invoice_number = e.get('voucher_no') + ent.invoice_date = e.get('posting_date') + ent.amount = flt(e.get('amount')) + ent.outstanding_amount = e.get('outstanding_amount') + def check_mandatory(self): pass + + def check_condition(self, dr_or_cr): + cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or "" + cond += self.to_date and " and posting_date <= '" + self.to_date + "'" or "" + + if self.minimum_amount: + cond += (" and ifnull(%s), 0) >= %s") % (dr_or_cr, self.minimum_amount) + if self.maximum_amount: + cond += " and ifnull(%s, 0) <= %s" % (dr_or_cr, self.maximum_amount) + + return cond \ No newline at end of file From 87e30f68b93603597bd398896d641cddd83807ff Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Thu, 17 Jul 2014 19:10:45 +0530 Subject: [PATCH 7/9] Add Payment Reconciliation Feature/Tool - Complete --- .../payment_reconciliation.js | 41 ++++-- .../payment_reconciliation.json | 7 +- .../payment_reconciliation.py | 121 +++++++++++++----- .../payment_reconciliation_invoice.json | 3 +- .../payment_reconciliation_payment.json | 21 ++- 5 files changed, 142 insertions(+), 51 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index cf62fd30735..c2ba1e62b3f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -20,19 +20,38 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext get_unreconciled_entries: function() { var me = this; - if(!this.frm.doc.company || !this.frm.doc.party_account) { - if(!this.frm.doc.company) { - msgprint(__("Please enter Company")); - } else { - msgprint(__("Please enter Party Account")); + return this.frm.call({ + doc: me.frm.doc, + method: 'get_unreconciled_entries', + callback: function(r, rt) { + var invoices = []; + + $.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) { + if (row.invoice_number && !inList(invoices, row.invoice_number)) + invoices.push(row.invoice_number); + }); + + frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number", + me.frm.doc.name).options = invoices.join("\n"); + + $.each(me.frm.doc.payment_reconciliation_payments || [], function(i, p) { + if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null; + }); + + refresh_field("payment_reconciliation_payments"); } - } else { - return this.frm.call({ - doc: me.frm.doc, - method: 'get_unreconciled_entries' - }); - } + }); + + }, + + reconcile: function() { + var me = this; + return this.frm.call({ + doc: me.frm.doc, + method: 'reconcile' + }); } + }); $.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm})); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 12253d00433..ce0d7317865 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -1,4 +1,5 @@ { + "allow_copy": 1, "creation": "2014-07-09 12:04:51.681583", "custom": 0, "docstatus": 0, @@ -27,11 +28,12 @@ { "fieldname": "party_type", "fieldtype": "Select", - "hidden": 0, + "hidden": 1, "in_list_view": 1, "label": "Party Type", "options": "Customer\nSupplier", "permlevel": 0, + "read_only": 1, "reqd": 0 }, { @@ -118,8 +120,9 @@ "read_only": 1 } ], + "hide_toolbar": 1, "issingle": 1, - "modified": "2014-07-11 15:01:47.133918", + "modified": "2014-07-17 19:07:34.385854", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index fc1ff24109f..be53aca9287 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -12,32 +12,42 @@ from frappe.model.document import Document class PaymentReconciliation(Document): def get_unreconciled_entries(self): - jv_entries = self.get_jv_entries() - self.add_payment_entries(jv_entries) - invoice_entries = self.get_invoice_entries() - - self.add_invoice_entries(invoice_entries) + self.get_jv_entries() + self.get_invoice_entries() def get_jv_entries(self): - self.check_mandatory() - - dr_or_cr = "credit" if self.party_type == "Customer" else "debit" - + self.check_mandatory_to_fetch() + dr_or_cr = "credit" if self.party_type == "Customer" else "debit" cond = self.check_condition(dr_or_cr) + bank_account_condition = "t2.against_account like %(bank_cash_account)s" \ + if self.bank_cash_account else "1=1" + jv_entries = frappe.db.sql(""" select t1.name as voucher_no, t1.posting_date, t1.remark, t2.account, - t2.name as voucher_detail_no, t2.%s, t2.is_advance + t2.name as voucher_detail_no, t2.{dr_or_cr}, t2.is_advance from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 where - t1.name = t2.parent and t1.docstatus = 1 and t2.account = %s - and t2.%s > 0 and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' - and ifnull(t2.against_jv, '')='' %s - group by t1.name, t2.name """ % (dr_or_cr, '%s', dr_or_cr, cond), (self.party_account), - as_dict = True) - return jv_entries + t1.name = t2.parent and t1.docstatus = 1 and t2.account = %(party_account)s + and t2.{dr_or_cr} > 0 and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' + and ifnull(t2.against_jv, '')='' {cond} + and (CASE + WHEN t1.voucher_type in ('Debit Note', 'Credit Note') + THEN 1=1 + ELSE {bank_account_condition} + END) + group by t1.name, t2.name """.format(**{ + "dr_or_cr": dr_or_cr, + "cond": cond, + "bank_account_condition": bank_account_condition + }), { + "party_account": self.party_account, + "bank_cash_account": "%%%s%%" % self.bank_cash_account + }, as_dict=1) + + self.add_payment_entries(jv_entries) def add_payment_entries(self, jv_entries): self.set('payment_reconciliation_payments', []) @@ -48,14 +58,12 @@ class PaymentReconciliation(Document): ent.amount = flt(e.get('credit')) or flt(e.get('debit')) ent.remark = e.get('remark') ent.voucher_detail_number = e.get('voucher_detail_no') + ent.is_advance = e.get('is_advance') def get_invoice_entries(self): #Fetch JVs, Sales and Purchase Invoices for 'payment_reconciliation_invoices' to reconcile against non_reconciled_invoices = [] - self.check_mandatory() - dr_or_cr = "debit" if self.party_type == "Customer" else "credit" - cond = self.check_condition(dr_or_cr) invoice_list = frappe.db.sql(""" @@ -65,7 +73,7 @@ class PaymentReconciliation(Document): `tabGL Entry` where account = %s and ifnull(%s, 0) > 0 %s - group by voucher_no, voucher_type""" % (dr_or_cr, "%s", + group by voucher_no, voucher_type""" % (dr_or_cr, '%s', dr_or_cr, cond), (self.party_account), as_dict=True) for d in invoice_list: @@ -75,27 +83,30 @@ class PaymentReconciliation(Document): from `tabGL Entry` where - account = %s and against_voucher_type = %s and ifnull(against_voucher, '') = %s""", - (("credit" if self.party_type == "Customer" else "debit"), self.party_account, - d.voucher_type, d.voucher_no)) - + account = %s and against_voucher_type = %s and ifnull(against_voucher, '') = %s""" % + (("credit" if self.party_type == "Customer" else "debit"), '%s', '%s', '%s'), + (self.party_account, d.voucher_type, d.voucher_no)) + payment_amount = payment_amount[0][0] if payment_amount else 0 if d.amount > payment_amount: - non_reconciled_invoices.append({'voucher_no': d.voucher_no, + non_reconciled_invoices.append({ + 'voucher_no': d.voucher_no, 'voucher_type': d.voucher_type, 'posting_date': d.posting_date, 'amount': flt(d.amount), 'outstanding_amount': d.amount - payment_amount}) - return non_reconciled_invoices + self.add_invoice_entries(non_reconciled_invoices) def add_invoice_entries(self, non_reconciled_invoices): #Populate 'payment_reconciliation_invoices' with JVs and Invoices to reconcile against self.set('payment_reconciliation_invoices', []) if not non_reconciled_invoices: - return + frappe.throw(_("No invoices found to be reconciled")) + + for e in non_reconciled_invoices: ent = self.append('payment_reconciliation_invoices', {}) ent.invoice_type = e.get('voucher_type') @@ -104,16 +115,64 @@ class PaymentReconciliation(Document): ent.amount = flt(e.get('amount')) ent.outstanding_amount = e.get('outstanding_amount') - def check_mandatory(self): - pass + def reconcile(self, args): + self.get_invoice_entries() + self.validate_invoice() + dr_or_cr = "credit" if self.party_type == "Customer" else "debit" + lst = [] + for e in self.get('payment_reconciliation_payments'): + lst.append({ + 'voucher_no' : e.journal_voucher, + 'voucher_detail_no' : e.voucher_detail_number, + 'against_voucher_type' : e.invoice_type, + 'against_voucher' : e.invoice_number, + 'account' : self.party_account, + 'is_advance' : e.is_advance, + 'dr_or_cr' : dr_or_cr, + 'unadjusted_amt' : flt(e.amount), + 'allocated_amt' : flt(e.amount) + }) + + if lst: + from erpnext.accounts.utils import reconcile_against_document + reconcile_against_document(lst) + self.get_unreconciled_entries() + msgprint(_("Successfully Reconciled")) + + + def check_mandatory_to_fetch(self): + for fieldname in ["company", "party_account"]: + if not self.get(fieldname): + frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname))) + + + def validate_invoice(self): + unreconciled_invoices = frappe._dict() + for d in self.get("payment_reconciliation_invoices"): + unreconciled_invoices.setdefault(d.invoice_type, {}).setdefault(d.invoice_number, d.outstanding_amount) + + invoices_to_reconcile = [] + for p in self.get("payment_reconciliation_payments"): + if p.invoice_type and p.invoice_number: + invoices_to_reconcile.append(p.invoice_number) + + if p.invoice_number not in unreconciled_invoices.get(p.invoice_type): + frappe.throw(_("{0}: {1} not found in Invoice Details table") + .format(p.invoice_type, p.invoice_number)) + + if p.amount > unreconciled_invoices.get(p.invoice_type).get(p.invoice_number): + frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount").format(p.idx)) + + if not invoices_to_reconcile: + frappe.throw(_("Please select Invoice Type and Invoice Number in atleast one row")) def check_condition(self, dr_or_cr): cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or "" cond += self.to_date and " and posting_date <= '" + self.to_date + "'" or "" if self.minimum_amount: - cond += (" and ifnull(%s), 0) >= %s") % (dr_or_cr, self.minimum_amount) + cond += " and ifnull(%s, 0) >= %s" % (dr_or_cr, self.minimum_amount) if self.maximum_amount: cond += " and ifnull(%s, 0) <= %s" % (dr_or_cr, self.maximum_amount) - return cond \ No newline at end of file + return cond \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json index 008b3265d8d..fba5f4e422c 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -18,6 +18,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Invoice Number", + "options": "", "permlevel": 0, "read_only": 1 }, @@ -47,7 +48,7 @@ } ], "istable": 1, - "modified": "2014-07-09 17:15:00.069551", + "modified": "2014-07-17 15:52:28.820965", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Invoice", diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index 231dfae075b..e939b02e1e5 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -43,17 +43,18 @@ }, { "fieldname": "invoice_number", - "fieldtype": "Data", + "fieldtype": "Select", "in_list_view": 1, "label": "Invoice Number", + "options": "", "permlevel": 0, "reqd": 1 }, { - "fieldname": "remark", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Remark", + "fieldname": "is_advance", + "fieldtype": "Data", + "hidden": 1, + "label": "Is Advance", "permlevel": 0, "read_only": 1 }, @@ -65,10 +66,18 @@ "label": "Voucher Detail Number", "permlevel": 0, "read_only": 1 + }, + { + "fieldname": "remark", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Remark", + "permlevel": 0, + "read_only": 1 } ], "istable": 1, - "modified": "2014-07-14 16:48:45.875052", + "modified": "2014-07-17 18:32:25.230146", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", From 5283e5a7d8adfc9829f91336e5e329ace5b98e5d Mon Sep 17 00:00:00 2001 From: ankitjavalkarwork Date: Fri, 18 Jul 2014 15:42:45 +0530 Subject: [PATCH 8/9] Add Payment Reconciliation Feature/Tool - JS changes1 --- .../payment_reconciliation.js | 5 ++ .../payment_reconciliation.json | 11 +++- .../payment_reconciliation_invoice.json | 8 ++- .../payment_reconciliation_invoice.py | 4 +- .../payment_reconciliation_payment.json | 50 +++++++++++++------ .../payment_reconciliation_payment.py | 4 +- 6 files changed, 59 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index c2ba1e62b3f..14520c2a050 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -16,8 +16,13 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext ] }; }); + + var help_content = [' Note:', + '
    If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that your amounts match the invoice you are trying to reconcile.
'].join("\n"); + this.frm.set_value("reconcile_help", help_content); }, + get_unreconciled_entries: function() { var me = this; return this.frm.call({ diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index ce0d7317865..40b57062603 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -108,7 +108,7 @@ { "fieldname": "sec_break2", "fieldtype": "Section Break", - "label": "Invoice/JV Details", + "label": "Invoice/Journal Voucher Details", "permlevel": 0 }, { @@ -118,11 +118,18 @@ "options": "Payment Reconciliation Invoice", "permlevel": 0, "read_only": 1 + }, + { + "fieldname": "reconcile_help", + "fieldtype": "Small Text", + "label": "", + "permlevel": 0, + "read_only": 1 } ], "hide_toolbar": 1, "issingle": 1, - "modified": "2014-07-17 19:07:34.385854", + "modified": "2014-07-18 15:53:20.638456", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json index fba5f4e422c..4e4ee1ae95b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -30,6 +30,12 @@ "permlevel": 0, "read_only": 1 }, + { + "fieldname": "col_break1", + "fieldtype": "Column Break", + "label": "Column Break", + "permlevel": 0 + }, { "fieldname": "amount", "fieldtype": "Currency", @@ -48,7 +54,7 @@ } ], "istable": 1, - "modified": "2014-07-17 15:52:28.820965", + "modified": "2014-07-18 12:20:51.269974", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Invoice", diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py index e136881514f..3094a173f04 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors # For license information, please see license.txt from __future__ import unicode_literals @@ -6,4 +6,4 @@ import frappe from frappe.model.document import Document class PaymentReconciliationInvoice(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index e939b02e1e5..3dd36fc4655 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -30,6 +30,29 @@ "permlevel": 0, "read_only": 1 }, + { + "fieldname": "is_advance", + "fieldtype": "Data", + "hidden": 1, + "label": "Is Advance", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "voucher_detail_number", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 0, + "label": "Voucher Detail Number", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "col_break1", + "fieldtype": "Column Break", + "label": "Column Break", + "permlevel": 0 + }, { "default": "Sales Invoice", "fieldname": "invoice_type", @@ -51,21 +74,10 @@ "reqd": 1 }, { - "fieldname": "is_advance", - "fieldtype": "Data", - "hidden": 1, - "label": "Is Advance", - "permlevel": 0, - "read_only": 1 - }, - { - "fieldname": "voucher_detail_number", - "fieldtype": "Data", - "hidden": 1, - "in_list_view": 0, - "label": "Voucher Detail Number", - "permlevel": 0, - "read_only": 1 + "fieldname": "sec_break1", + "fieldtype": "Section Break", + "label": "", + "permlevel": 0 }, { "fieldname": "remark", @@ -74,10 +86,16 @@ "label": "Remark", "permlevel": 0, "read_only": 1 + }, + { + "fieldname": "col_break2", + "fieldtype": "Column Break", + "label": "Column Break", + "permlevel": 0 } ], "istable": 1, - "modified": "2014-07-17 18:32:25.230146", + "modified": "2014-07-18 15:53:15.589501", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py index 9082ef9bcf8..21e19bdd716 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors # For license information, please see license.txt from __future__ import unicode_literals @@ -6,4 +6,4 @@ import frappe from frappe.model.document import Document class PaymentReconciliationPayment(Document): - pass \ No newline at end of file + pass From 97ab9028957026477f856a27aeb20c7de9d09510 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 21 Jul 2014 19:22:02 +0530 Subject: [PATCH 9/9] payment reconciliation fixes --- .../payment_reconciliation.js | 42 +++++-- .../payment_reconciliation.json | 4 +- .../payment_reconciliation.py | 117 +++++++++++------- .../payment_reconciliation_payment.json | 4 +- erpnext/accounts/utils.py | 8 +- 5 files changed, 108 insertions(+), 67 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 14520c2a050..c495a35825f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -7,22 +7,40 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext onload: function() { var me = this - this.frm.set_query ('party_account', function() { - return{ - filters:[ - ['Account', 'company', '=', me.frm.doc.company], - ['Account', 'group_or_ledger', '=', 'Ledger'], - ['Account', 'master_type', 'in', ['Customer', 'Supplier']] - ] - }; + this.frm.set_query('party_account', function() { + if(!me.frm.doc.company) { + msgprint(__("Please select company first")); + } else { + return{ + filters:[ + ['Account', 'company', '=', me.frm.doc.company], + ['Account', 'group_or_ledger', '=', 'Ledger'], + ['Account', 'master_type', 'in', ['Customer', 'Supplier']] + ] + }; + } + + }); + + this.frm.set_query('bank_cash_account', function() { + if(!me.frm.doc.company) { + msgprint(__("Please select company first")); + } else { + return{ + filters:[ + ['Account', 'company', '=', me.frm.doc.company], + ['Account', 'group_or_ledger', '=', 'Ledger'], + ['Account', 'account_type', 'in', ['Bank', 'Cash']] + ] + }; + } }); - var help_content = [' Note:', - '
    If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that your amounts match the invoice you are trying to reconcile.
'].join("\n"); + var help_content = ' Note:
'+ + '
    If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that payment amount match the invoice amount.
'; this.frm.set_value("reconcile_help", help_content); }, - - + get_unreconciled_entries: function() { var me = this; return this.frm.call({ diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 40b57062603..a83d45363e1 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -31,7 +31,7 @@ "hidden": 1, "in_list_view": 1, "label": "Party Type", - "options": "Customer\nSupplier", + "options": "\nCustomer\nSupplier", "permlevel": 0, "read_only": 1, "reqd": 0 @@ -129,7 +129,7 @@ ], "hide_toolbar": 1, "issingle": 1, - "modified": "2014-07-18 15:53:20.638456", + "modified": "2014-07-21 16:54:31.454679", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index be53aca9287..2d844bb1893 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -18,7 +18,12 @@ class PaymentReconciliation(Document): def get_jv_entries(self): self.check_mandatory_to_fetch() dr_or_cr = "credit" if self.party_type == "Customer" else "debit" - cond = self.check_condition(dr_or_cr) + if self.party_type=="Customer": + amount_query = "ifnull(t2.credit, 0) - ifnull(t2.debit, 0)" + else: + amount_query = "ifnull(t2.debit, 0) - ifnull(t2.credit, 0)" + + cond = self.check_condition(amount_query) bank_account_condition = "t2.against_account like %(bank_cash_account)s" \ if self.bank_cash_account else "1=1" @@ -26,22 +31,26 @@ class PaymentReconciliation(Document): jv_entries = frappe.db.sql(""" select t1.name as voucher_no, t1.posting_date, t1.remark, t2.account, - t2.name as voucher_detail_no, t2.{dr_or_cr}, t2.is_advance + t2.name as voucher_detail_no, {amount_query} as payment_amount, t2.is_advance from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 where - t1.name = t2.parent and t1.docstatus = 1 and t2.account = %(party_account)s - and t2.{dr_or_cr} > 0 and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' + t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1 + and t2.account = %(party_account)s and {amount_query} > 0 + and ifnull((select ifnull(sum(ifnull(credit, 0) - ifnull(debit, 0)), 0) from `tabJournal Voucher Detail` + where parent=t1.name and account=t2.account and docstatus=1 group by account), 0) > 0 + and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_jv, '')='' {cond} and (CASE WHEN t1.voucher_type in ('Debit Note', 'Credit Note') THEN 1=1 ELSE {bank_account_condition} END) - group by t1.name, t2.name """.format(**{ + """.format(**{ "dr_or_cr": dr_or_cr, "cond": cond, - "bank_account_condition": bank_account_condition + "bank_account_condition": bank_account_condition, + "amount_query": amount_query }), { "party_account": self.party_account, "bank_cash_account": "%%%s%%" % self.bank_cash_account @@ -55,7 +64,7 @@ class PaymentReconciliation(Document): ent = self.append('payment_reconciliation_payments', {}) ent.journal_voucher = e.get('voucher_no') ent.posting_date = e.get('posting_date') - ent.amount = flt(e.get('credit')) or flt(e.get('debit')) + ent.amount = flt(e.get('payment_amount')) ent.remark = e.get('remark') ent.voucher_detail_number = e.get('voucher_detail_no') ent.is_advance = e.get('is_advance') @@ -64,55 +73,63 @@ class PaymentReconciliation(Document): #Fetch JVs, Sales and Purchase Invoices for 'payment_reconciliation_invoices' to reconcile against non_reconciled_invoices = [] dr_or_cr = "debit" if self.party_type == "Customer" else "credit" - cond = self.check_condition(dr_or_cr) + if self.party_type=="Customer": + amount_query = "ifnull(debit, 0) - ifnull(credit, 0)" + else: + amount_query = "ifnull(credit, 0) - ifnull(debit, 0)" + + cond = self.check_condition(amount_query) invoice_list = frappe.db.sql(""" select - voucher_no, voucher_type, posting_date, ifnull(sum(ifnull(%s, 0)), 0) as amount + voucher_no, voucher_type, posting_date, + ifnull(sum({amount_query}), 0) as invoice_amount from `tabGL Entry` where - account = %s and ifnull(%s, 0) > 0 %s - group by voucher_no, voucher_type""" % (dr_or_cr, '%s', - dr_or_cr, cond), (self.party_account), as_dict=True) + account = %s and {amount_query} > 0 {cond} + group by voucher_type, voucher_no + """.format(**{ + "cond": cond, + "amount_query": amount_query + }), (self.party_account), as_dict=True) for d in invoice_list: payment_amount = frappe.db.sql(""" select - ifnull(sum(ifnull(%s, 0)), 0) + ifnull(sum(ifnull({amount_query}, 0)), 0) from `tabGL Entry` where - account = %s and against_voucher_type = %s and ifnull(against_voucher, '') = %s""" % - (("credit" if self.party_type == "Customer" else "debit"), '%s', '%s', '%s'), - (self.party_account, d.voucher_type, d.voucher_no)) + account = %s and {amount_query} < 0 + and against_voucher_type = %s and ifnull(against_voucher, '') = %s + """.format(**{ + "cond": cond, + "amount_query": amount_query + }), (self.party_account, d.voucher_type, d.voucher_no)) + + payment_amount = -1*payment_amount[0][0] if payment_amount else 0 - payment_amount = payment_amount[0][0] if payment_amount else 0 - - if d.amount > payment_amount: + if d.invoice_amount > payment_amount: non_reconciled_invoices.append({ 'voucher_no': d.voucher_no, 'voucher_type': d.voucher_type, 'posting_date': d.posting_date, - 'amount': flt(d.amount), - 'outstanding_amount': d.amount - payment_amount}) + 'invoice_amount': flt(d.invoice_amount), + 'outstanding_amount': d.invoice_amount - payment_amount}) self.add_invoice_entries(non_reconciled_invoices) - def add_invoice_entries(self, non_reconciled_invoices): #Populate 'payment_reconciliation_invoices' with JVs and Invoices to reconcile against self.set('payment_reconciliation_invoices', []) - if not non_reconciled_invoices: - frappe.throw(_("No invoices found to be reconciled")) - - + for e in non_reconciled_invoices: ent = self.append('payment_reconciliation_invoices', {}) ent.invoice_type = e.get('voucher_type') ent.invoice_number = e.get('voucher_no') ent.invoice_date = e.get('posting_date') - ent.amount = flt(e.get('amount')) + ent.amount = flt(e.get('invoice_amount')) ent.outstanding_amount = e.get('outstanding_amount') def reconcile(self, args): @@ -121,24 +138,24 @@ class PaymentReconciliation(Document): dr_or_cr = "credit" if self.party_type == "Customer" else "debit" lst = [] for e in self.get('payment_reconciliation_payments'): - lst.append({ - 'voucher_no' : e.journal_voucher, - 'voucher_detail_no' : e.voucher_detail_number, - 'against_voucher_type' : e.invoice_type, - 'against_voucher' : e.invoice_number, - 'account' : self.party_account, - 'is_advance' : e.is_advance, - 'dr_or_cr' : dr_or_cr, - 'unadjusted_amt' : flt(e.amount), - 'allocated_amt' : flt(e.amount) - }) + if e.invoice_type and e.invoice_number: + lst.append({ + 'voucher_no' : e.journal_voucher, + 'voucher_detail_no' : e.voucher_detail_number, + 'against_voucher_type' : e.invoice_type, + 'against_voucher' : e.invoice_number, + 'account' : self.party_account, + 'is_advance' : e.is_advance, + 'dr_or_cr' : dr_or_cr, + 'unadjusted_amt' : flt(e.amount), + 'allocated_amt' : flt(e.amount) + }) if lst: from erpnext.accounts.utils import reconcile_against_document reconcile_against_document(lst) - self.get_unreconciled_entries() msgprint(_("Successfully Reconciled")) - + self.get_unreconciled_entries() def check_mandatory_to_fetch(self): for fieldname in ["company", "party_account"]: @@ -147,32 +164,38 @@ class PaymentReconciliation(Document): def validate_invoice(self): + if not self.get("payment_reconciliation_invoices"): + frappe.throw(_("No records found in the Invoice table")) + + if not self.get("payment_reconciliation_payments"): + frappe.throw(_("No records found in the Payment table")) + unreconciled_invoices = frappe._dict() for d in self.get("payment_reconciliation_invoices"): unreconciled_invoices.setdefault(d.invoice_type, {}).setdefault(d.invoice_number, d.outstanding_amount) - + invoices_to_reconcile = [] for p in self.get("payment_reconciliation_payments"): if p.invoice_type and p.invoice_number: invoices_to_reconcile.append(p.invoice_number) - if p.invoice_number not in unreconciled_invoices.get(p.invoice_type): + if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}): frappe.throw(_("{0}: {1} not found in Invoice Details table") .format(p.invoice_type, p.invoice_number)) - if p.amount > unreconciled_invoices.get(p.invoice_type).get(p.invoice_number): - frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount").format(p.idx)) + if p.amount > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number): + frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount. Please refer Note below.").format(p.idx)) if not invoices_to_reconcile: frappe.throw(_("Please select Invoice Type and Invoice Number in atleast one row")) - def check_condition(self, dr_or_cr): + def check_condition(self, amount_query): cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or "" cond += self.to_date and " and posting_date <= '" + self.to_date + "'" or "" if self.minimum_amount: - cond += " and ifnull(%s, 0) >= %s" % (dr_or_cr, self.minimum_amount) + cond += " and {0} >= %s".format(amount_query) % self.minimum_amount if self.maximum_amount: - cond += " and ifnull(%s, 0) <= %s" % (dr_or_cr, self.maximum_amount) + cond += " and {0} <= %s".format(amount_query) % self.maximum_amount return cond \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index 3dd36fc4655..73fd0f50f33 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -59,7 +59,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Invoice Type", - "options": "Sales Invoice\nPurchase Invoice\nJournal Voucher", + "options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher", "permlevel": 0, "read_only": 0, "reqd": 1 @@ -95,7 +95,7 @@ } ], "istable": 1, - "modified": "2014-07-18 15:53:15.589501", + "modified": "2014-07-21 16:53:56.206169", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index bc005d7c949..25a8adfca1e 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -153,12 +153,12 @@ def check_if_jv_modified(args): check if jv is submitted """ ret = frappe.db.sql(""" - select t2.%(dr_or_cr)s from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 - where t1.name = t2.parent and t2.account = '%(account)s' + select t2.{dr_or_cr} from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 + where t1.name = t2.parent and t2.account = %(account)s and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_jv, '')='' - and t1.name = '%(voucher_no)s' and t2.name = '%(voucher_detail_no)s' - and t1.docstatus=1 """ % args) + and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s + and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args) if not ret: throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))