From 76030540cb3475097e85823fbdf96803c4b98bc8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 11 Sep 2014 19:27:24 +0530 Subject: [PATCH] Accounts Receivable/Payable report based on party --- .../accounts_payable/accounts_payable.js | 17 +-- .../accounts_payable/accounts_payable.py | 125 +++++++--------- .../accounts_receivable.js | 19 +-- .../accounts_receivable.py | 135 ++++++++---------- 4 files changed, 122 insertions(+), 174 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 300c2285e7..281f29fa87 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -11,21 +11,10 @@ frappe.query_reports["Accounts Payable"] = { "default": frappe.defaults.get_user_default("company") }, { - "fieldname":"account", - "label": __("Account"), + "fieldname":"supplier", + "label": __("Supplier"), "fieldtype": "Link", - "options": "Account", - "get_query": function() { - var company = frappe.query_report.filters_by_name.company.get_value(); - return { - "query": "erpnext.controllers.queries.get_account_list", - "filters": { - "report_type": "Balance Sheet", - "company": company, - "master_type": "Supplier" - } - } - } + "options": "Supplier" }, { "fieldname":"report_date", diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.py b/erpnext/accounts/report/accounts_payable/accounts_payable.py index 3ae741e772..2edb18bfaa 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.py @@ -3,8 +3,8 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.utils import getdate, nowdate, flt, cstr -from frappe import msgprint, _ from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data def execute(filters=None): @@ -12,15 +12,11 @@ def execute(filters=None): supplier_naming_by = frappe.db.get_value("Buying Settings", None, "supp_master_name") columns = get_columns(supplier_naming_by) entries = get_gl_entries(filters) - account_map = dict(((r.name, r) for r in frappe.db.sql("""select acc.name, - supp.supplier_name, supp.name as supplier - from `tabAccount` acc, `tabSupplier` supp - where acc.master_type="Supplier" and supp.name=acc.master_name""", as_dict=1))) - entries_after_report_date = [[gle.voucher_type, gle.voucher_no] + entries_after_report_date = [[gle.voucher_type, gle.voucher_no] for gle in get_gl_entries(filters, before_report_date=False)] - account_supplier_type_map = get_account_supplier_type_map() + supplier_details = get_supplier_details() voucher_detail_map = get_voucher_details() # Age of the invoice on this date @@ -32,115 +28,102 @@ def execute(filters=None): if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: voucher_details = voucher_detail_map.get(gle.voucher_type, {}).get(gle.voucher_no, {}) - + invoiced_amount = gle.credit > 0 and gle.credit or 0 - outstanding_amount = get_outstanding_amount(gle, + outstanding_amount = get_outstanding_amount(gle, filters.get("report_date") or nowdate()) if abs(flt(outstanding_amount)) > 0.01: paid_amount = invoiced_amount - outstanding_amount - row = [gle.posting_date, gle.account, gle.voucher_type, gle.voucher_no, - voucher_details.get("due_date", ""), voucher_details.get("bill_no", ""), - voucher_details.get("bill_date", ""), invoiced_amount, - paid_amount, outstanding_amount] - + row = [gle.posting_date, gle.party] + + if supplier_naming_by == "Naming Series": + row += [supplier_details.get(gle.party, {}).supplier_name or ""] + + row += [gle.voucher_type, gle.voucher_no, voucher_details.get("due_date", ""), + voucher_details.get("bill_no", ""), voucher_details.get("bill_date", ""), + invoiced_amount, paid_amount, outstanding_amount] + # Ageing if filters.get("ageing_based_on") == "Due Date": ageing_based_on_date = voucher_details.get("due_date", "") else: ageing_based_on_date = gle.posting_date - + row += get_ageing_data(age_on, ageing_based_on_date, outstanding_amount) + \ - [account_map.get(gle.account, {}).get("supplier") or ""] + [supplier_details.get(gle.party).supplier_type, gle.remarks] - if supplier_naming_by == "Naming Series": - row += [account_map.get(gle.account, {}).get("supplier_name") or ""] - - row += [account_supplier_type_map.get(gle.account), gle.remarks] data.append(row) - for i in range(0, len(data)): - data[i].insert(4, """""" \ - % ("/".join(["#Form", data[i][2], data[i][3]]),)) + # for i in range(0, len(data)): + # data[i].insert(4, """""" \ + # % ("/".join(["#Form", data[i][2], data[i][3]]),)) return columns, data - + def get_columns(supplier_naming_by): - columns = [ - _("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150", _("Voucher Type") + "::110", - _("Voucher No") + "::120", "::30", _("Due Date") + ":Date:80", _("Bill No") + "::80", _("Bill Date") + ":Date:80", - _("Invoiced Amount") + ":Currency:100", _("Paid Amount") + ":Currency:100", - _("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100", - "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100", - _("Supplier") + ":Link/Supplier:150" - ] + columns = [_("Posting Date") + ":Date:80", _("Supplier") + ":Link/Supplier:150"] if supplier_naming_by == "Naming Series": columns += ["Supplier Name::110"] - columns += ["Supplier Type:Link/Supplier Type:120", "Remarks::150"] + columns +=[_("Voucher Type") + "::110", _("Voucher No") + "::120", "::30", + _("Due Date") + ":Date:80", _("Bill No") + "::80", _("Bill Date") + ":Date:80", + _("Invoiced Amount") + ":Currency:100", _("Paid Amount") + ":Currency:100", + _("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100", + "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100", + _("Supplier Type") + ":Link/Supplier Type:150", _("Remarks") + "::150" + ] return columns def get_gl_entries(filters, before_report_date=True): - conditions, supplier_accounts = get_conditions(filters, before_report_date) + conditions = get_conditions(filters, before_report_date) gl_entries = [] - gl_entries = frappe.db.sql("""select * from `tabGL Entry` - where docstatus < 2 %s order by posting_date, account""" % - (conditions), tuple(supplier_accounts), as_dict=1) + gl_entries = frappe.db.sql("""select * from `tabGL Entry` + where docstatus < 2 and party_type='Supplier' %s + order by posting_date, party""" % conditions, as_dict=1) return gl_entries - + def get_conditions(filters, before_report_date=True): conditions = "" if filters.get("company"): conditions += " and company='%s'" % filters["company"].replace("'", "\'") - - supplier_accounts = [] - if filters.get("account"): - supplier_accounts = [filters["account"]] - else: - supplier_accounts = frappe.db.sql_list("""select name from `tabAccount` - where ifnull(master_type, '') = 'Supplier' and docstatus < 2 %s""" % - conditions, filters) - - if supplier_accounts: - conditions += " and account in (%s)" % (", ".join(['%s']*len(supplier_accounts))) - else: - msgprint(_("No Supplier Accounts found. Supplier Accounts are identified based on 'Master Type' value in account record."), raise_exception=1) - + + if filters.get("supplier"): + conditions += " and party='%s'" % filters["supplier"].replace("'", "\'") + if filters.get("report_date"): if before_report_date: conditions += " and posting_date<='%s'" % filters["report_date"] else: conditions += " and posting_date>'%s'" % filters["report_date"] - - return conditions, supplier_accounts - -def get_account_supplier_type_map(): - account_supplier_type_map = {} - for each in frappe.db.sql("""select acc.name, supp.supplier_type from `tabSupplier` supp, - `tabAccount` acc where supp.name = acc.master_name group by acc.name"""): - account_supplier_type_map[each[0]] = each[1] - return account_supplier_type_map - + return conditions + +def get_supplier_details(): + supplier_details = {} + for d in frappe.db.sql("""select name, supplier_type, supplier_name from `tabSupplier`""", as_dict=1): + supplier_details.setdefault(d.name, d) + + return supplier_details + def get_voucher_details(): voucher_details = {} for dt in ["Purchase Invoice", "Journal Voucher"]: voucher_details.setdefault(dt, frappe._dict()) - for t in frappe.db.sql("""select name, due_date, bill_no, bill_date - from `tab%s`""" % dt, as_dict=1): + for t in frappe.db.sql("""select name, due_date, bill_no, bill_date from `tab%s`""" % dt, as_dict=1): voucher_details[dt].setdefault(t.name, t) - + return voucher_details def get_outstanding_amount(gle, report_date): payment_amount = frappe.db.sql(""" - select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) - from `tabGL Entry` - where account = %s and posting_date <= %s and against_voucher_type = %s - and against_voucher = %s and name != %s""", - (gle.account, report_date, gle.voucher_type, gle.voucher_no, gle.name))[0][0] - + select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) + from `tabGL Entry` + where party_type='Supplier' and party = %s and posting_date <= %s and against_voucher_type = %s + and against_voucher = %s and name != %s""", + (gle.party, report_date, gle.voucher_type, gle.voucher_no, gle.name))[0][0] + outstanding_amount = flt(gle.credit) - flt(gle.debit) - flt(payment_amount) return outstanding_amount diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 304c21bebb..708f63b00d 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -11,21 +11,10 @@ frappe.query_reports["Accounts Receivable"] = { "default": frappe.defaults.get_user_default("company") }, { - "fieldname":"account", - "label": __("Account"), + "fieldname":"customer", + "label": __("Customer"), "fieldtype": "Link", - "options": "Account", - "get_query": function() { - var company = frappe.query_report.filters_by_name.company.get_value(); - return { - "query": "erpnext.controllers.queries.get_account_list", - "filters": { - "report_type": "Balance Sheet", - "company": company, - "master_type": "Customer" - } - } - } + "options": "Customer" }, { "fieldname":"report_date", @@ -41,4 +30,4 @@ frappe.query_reports["Accounts Receivable"] = { "default": "Posting Date" } ] -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 3a0fb31dea..edaa25113c 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -13,26 +13,24 @@ class AccountsReceivableReport(object): self.age_as_on = getdate(nowdate()) \ if self.filters.report_date > getdate(nowdate()) \ else self.filters.report_date - + def run(self): customer_naming_by = frappe.db.get_value("Selling Settings", None, "cust_master_name") return self.get_columns(customer_naming_by), self.get_data(customer_naming_by) - + def get_columns(self, customer_naming_by): - columns = [ - _("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150", - _("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120", - _("Due Date") + ":Date:80", - _("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100", - _("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100", - "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100", - _("Customer") + ":Link/Customer:200" - ] + columns = [_("Posting Date") + ":Date:80", _("Customer") + ":Link/Customer:200"] if customer_naming_by == "Naming Series": columns += ["Customer Name::110"] - columns += ["Territory:Link/Territory:80", "Remarks::200"] + columns += [_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120", "::30", + _("Due Date") + ":Date:80", _("Invoiced Amount") + ":Currency:100", + _("Payment Received") + ":Currency:100", _("Outstanding Amount") + ":Currency:100", + _("Age") + ":Int:50", "0-30:Currency:100", "30-60:Currency:100", + "60-90:Currency:100", _("90-Above") + ":Currency:100", + _("Territory") + ":Link/Territory:80", _("Remarks") + "::200" + ] return columns @@ -49,119 +47,108 @@ class AccountsReceivableReport(object): due_date = self.get_due_date(gle) invoiced_amount = gle.debit if (gle.debit > 0) else 0 payment_received = invoiced_amount - outstanding_amount - row = [gle.posting_date, gle.account, - gle.voucher_type, gle.voucher_no, due_date, - invoiced_amount, payment_received, - outstanding_amount] - entry_date = due_date if self.filters.ageing_based_on == "Due Date" \ - else gle.posting_date - row += get_ageing_data(self.age_as_on, entry_date, outstanding_amount) + \ - [self.get_customer(gle.account)] + row = [gle.posting_date, gle.party] if customer_naming_by == "Naming Series": - row += [self.get_customer_name(gle.account)] + row += [self.get_customer_name(gle.party)] + + row += [gle.voucher_type, gle.voucher_no, due_date, invoiced_amount, + payment_received, outstanding_amount] + + entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date + row += get_ageing_data(self.age_as_on, entry_date, outstanding_amount) + \ + [self.get_territory(gle.account), gle.remarks] - row += [self.get_territory(gle.account), gle.remarks] data.append(row) + return data def get_entries_after(self, report_date): # returns a distinct list return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries() if getdate(e.posting_date) > report_date])) - + def get_entries_till(self, report_date): # returns a generator - return (e for e in self.get_gl_entries() + return (e for e in self.get_gl_entries() if getdate(e.posting_date) <= report_date) - + def is_receivable(self, gle, future_vouchers): return ( # advance - (not gle.against_voucher) or - + (not gle.against_voucher) or + # sales invoice - (gle.against_voucher==gle.voucher_no and gle.debit > 0) or - + (gle.against_voucher==gle.voucher_no and gle.debit > 0) or + # entries adjusted with future vouchers ((gle.against_voucher_type, gle.against_voucher) in future_vouchers) ) - + def get_outstanding_amount(self, gle, report_date): payment_received = 0.0 - for e in self.get_gl_entries_for(gle.account, gle.voucher_type, gle.voucher_no): + for e in self.get_gl_entries_for(gle.party, gle.voucher_type, gle.voucher_no): if getdate(e.posting_date) <= report_date and e.name!=gle.name: payment_received += (flt(e.credit) - flt(e.debit)) return flt(gle.debit) - flt(gle.credit) - payment_received - - def get_customer(self, account): - return self.get_account_map().get(account, {}).get("customer") or "" - def get_customer_name(self, account): - return self.get_account_map().get(account, {}).get("customer_name") or "" + def get_customer_name(self, customer): + return self.get_customer_map().get(customer, {}).get("customer_name") or "" + + def get_territory(self, customer): + return self.get_customer_map().get(customer, {}).get("territory") or "" + + def get_customer_map(self): + if not hasattr(self, "customer_map"): + self.customer_map = dict(((r.name, r) for r in frappe.db.sql("""select + name, customer_name, territory from `tabCustomer`""", as_dict=True))) + + return self.customer_map - def get_territory(self, account): - return self.get_account_map().get(account, {}).get("territory") or "" - - def get_account_map(self): - if not hasattr(self, "account_map"): - self.account_map = dict(((r.name, r) for r in frappe.db.sql("""select - acc.name, cust.name as customer, cust.customer_name, cust.territory - from `tabAccount` acc left join `tabCustomer` cust - on cust.name=acc.master_name where acc.master_type="Customer" """, as_dict=True))) - - return self.account_map - def get_due_date(self, gle): if not hasattr(self, "invoice_due_date_map"): # TODO can be restricted to posting date self.invoice_due_date_map = dict(frappe.db.sql("""select name, due_date from `tabSales Invoice` where docstatus=1""")) - + return gle.voucher_type == "Sales Invoice" \ and self.invoice_due_date_map.get(gle.voucher_no) or "" - + def get_gl_entries(self): if not hasattr(self, "gl_entries"): conditions, values = self.prepare_conditions() self.gl_entries = frappe.db.sql("""select * from `tabGL Entry` - where docstatus < 2 {0} order by posting_date, account""".format(conditions), - values, as_dict=True) + where docstatus < 2 and party_type='Customer' {0} + order by posting_date, party""".format(conditions), values, as_dict=True) + return self.gl_entries - + def prepare_conditions(self): conditions = [""] values = {} - + if self.filters.company: conditions.append("company=%(company)s") values["company"] = self.filters.company - - if self.filters.account: - conditions.append("account=%(account)s") - values["account"] = self.filters.account - else: - account_map = self.get_account_map() - if not account_map: - frappe.throw(_("No Customer Accounts found.")) - else: - accounts_list = ['"{0}"'.format(ac) for ac in account_map] - conditions.append("account in ({0})".format(", ".join(accounts_list))) - + + if self.filters.customer: + conditions.append("party=%(customer)s") + values["customer"] = self.filters.customer + return " and ".join(conditions), values - - def get_gl_entries_for(self, account, against_voucher_type, against_voucher): + + def get_gl_entries_for(self, party, against_voucher_type, against_voucher): if not hasattr(self, "gl_entries_map"): self.gl_entries_map = {} for gle in self.get_gl_entries(): if gle.against_voucher_type and gle.against_voucher: - self.gl_entries_map.setdefault(gle.account, {})\ + self.gl_entries_map.setdefault(gle.party, {})\ .setdefault(gle.against_voucher_type, {})\ .setdefault(gle.against_voucher, [])\ .append(gle) - - return self.gl_entries_map.get(account, {})\ + + return self.gl_entries_map.get(party, {})\ .get(against_voucher_type, {})\ .get(against_voucher, []) @@ -173,15 +160,15 @@ def get_ageing_data(age_as_on, entry_date, outstanding_amount): outstanding_range = [0.0, 0.0, 0.0, 0.0] if not (age_as_on and entry_date): return [0] + outstanding_range - + age = (getdate(age_as_on) - getdate(entry_date)).days or 0 index = None for i, days in enumerate([30, 60, 90]): if age <= days: index = i break - + if index is None: index = 3 outstanding_range[index] = outstanding_amount - + return [age] + outstanding_range