From d51f805b783f02804375e20f2b905dfa4ce478ca Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 27 Dec 2013 17:33:55 +0530 Subject: [PATCH 1/9] Rewritten General Ledger report with grouping functions --- .../report/general_ledger/general_ledger.js | 54 +++-- .../report/general_ledger/general_ledger.py | 199 ++++++++++++------ 2 files changed, 164 insertions(+), 89 deletions(-) diff --git a/accounts/report/general_ledger/general_ledger.js b/accounts/report/general_ledger/general_ledger.js index 7985277115..f2e60b6447 100644 --- a/accounts/report/general_ledger/general_ledger.js +++ b/accounts/report/general_ledger/general_ledger.js @@ -11,26 +11,6 @@ wn.query_reports["General Ledger"] = { "default": wn.defaults.get_user_default("company"), "reqd": 1 }, - { - "fieldname":"account", - "label": wn._("Account"), - "fieldtype": "Link", - "options": "Account" - }, - { - "fieldname":"voucher_no", - "label": wn._("Voucher No"), - "fieldtype": "Data", - }, - { - "fieldname":"group_by", - "label": wn._("Group by"), - "fieldtype": "Select", - "options": "\nGroup by Account\nGroup by Voucher" - }, - { - "fieldtype": "Break", - }, { "fieldname":"from_date", "label": wn._("From Date"), @@ -46,6 +26,40 @@ wn.query_reports["General Ledger"] = { "default": wn.datetime.get_today(), "reqd": 1, "width": "60px" + }, + { + "fieldtype": "Break", + }, + { + "fieldname":"account", + "label": wn._("Account"), + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = wn.query_report.filters_by_name.company.get_value(); + return { + "query": "accounts.utils.get_account_list", + "filters": { + "company": company, + } + } + } + }, + { + "fieldname":"voucher_no", + "label": wn._("Voucher No"), + "fieldtype": "Data", + }, + { + "fieldname":"group_by_voucher", + "label": wn._("Group by Voucher"), + "fieldtype": "Check", + "default": 1 + }, + { + "fieldname":"group_by_account", + "label": wn._("Group by Account"), + "fieldtype": "Check", } ] } \ No newline at end of file diff --git a/accounts/report/general_ledger/general_ledger.py b/accounts/report/general_ledger/general_ledger.py index b88d5bc296..2efc8241c0 100644 --- a/accounts/report/general_ledger/general_ledger.py +++ b/accounts/report/general_ledger/general_ledger.py @@ -3,109 +3,170 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt, add_days +from webnotes.utils import cstr, flt from webnotes import _ -from accounts.utils import get_balance_on def execute(filters=None): - account_details = webnotes.conn.get_value("Account", filters["account"], - ["debit_or_credit", "group_or_ledger"], as_dict=True) if filters.get("account") else None + account_details = {} + for acc in webnotes.conn.sql("""select name, debit_or_credit, group_or_ledger + from tabAccount""", as_dict=1): + account_details.setdefault(acc.name, acc) + validate_filters(filters, account_details) columns = get_columns() - data = [] - if filters.get("group_by"): - data += get_grouped_gle(filters) - else: - data += get_gl_entries(filters) - if data: - data.append(get_total_row(data)) + + res = get_result(filters, account_details) - if account_details: - data = [get_opening_balance_row(filters, account_details.debit_or_credit)] + data + \ - [get_closing_balance_row(filters, account_details.debit_or_credit)] - - return columns, data + return columns, res def validate_filters(filters, account_details): - if account_details and account_details.group_or_ledger == "Ledger" \ - and filters.get("group_by") == "Group by Account": + if filters.get("account") and filters.get("group_by_account") \ + and account_details[filters.account].group_or_ledger == "Ledger": webnotes.throw(_("Can not filter based on Account, if grouped by Account")) - if filters.get("voucher_no") and filters.get("group_by") == "Group by Voucher": + if filters.get("voucher_no") and filters.get("group_by_voucher"): webnotes.throw(_("Can not filter based on Voucher No, if grouped by Voucher")) + + if filters.from_date > filters.to_date: + webnotes.throw(_("From Date must be before To Date")) def get_columns(): return ["Posting Date:Date:100", "Account:Link/Account:200", "Debit:Float:100", "Credit:Float:100", "Voucher Type::120", "Voucher No::160", "Link::20", - "Cost Center:Link/Cost Center:100", "Remarks::200"] + "Against Account::120", "Cost Center:Link/Cost Center:100", "Remarks::200"] -def get_opening_balance_row(filters, debit_or_credit): - opening_balance = get_balance_on(filters["account"], add_days(filters["from_date"], -1)) - return get_balance_row(opening_balance, debit_or_credit, "Opening Balance") - -def get_closing_balance_row(filters, debit_or_credit): - closing_balance = get_balance_on(filters["account"], filters["to_date"]) - return get_balance_row(closing_balance, debit_or_credit, "Closing Balance") - -def get_balance_row(balance, debit_or_credit, balance_label): - if debit_or_credit == "Debit": - return ["", balance_label, balance, 0.0, "", "", ""] - else: - return ["", balance_label, 0.0, balance, "", "", ""] +def get_result(filters, account_details): + gl_entries = get_gl_entries(filters) + + data = get_data_with_opening_closing(filters, account_details, gl_entries) + result = get_result_as_list(data) + + return result + def get_gl_entries(filters): - gl_entries = webnotes.conn.sql("""select - posting_date, account, debit, credit, voucher_type, voucher_no, cost_center, remarks - from `tabGL Entry` - where company=%(company)s - and posting_date between %(from_date)s and %(to_date)s - {conditions} - order by posting_date, account"""\ - .format(conditions=get_conditions(filters)), filters, as_list=1) + group_by_condition = "group by voucher_type, voucher_no, account" \ + if filters.get("group_by_voucher") else "group by name" - for d in gl_entries: - icon = """""" \ - % ("/".join(["#Form", d[4], d[5]]),) - d.insert(6, icon) + gl_entries = webnotes.conn.sql("""select posting_date, account, + sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit, + voucher_type, voucher_no, cost_center, remarks, is_advance, against + from `tabGL Entry` + where company=%(company)s {conditions} + {group_by_condition} + order by posting_date, account"""\ + .format(conditions=get_conditions(filters), group_by_condition=group_by_condition), + filters, as_dict=1) return gl_entries - + def get_conditions(filters): conditions = [] if filters.get("account"): lft, rgt = webnotes.conn.get_value("Account", filters["account"], ["lft", "rgt"]) conditions.append("""account in (select name from tabAccount where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + else: + conditions.append("posting_date between %(from_date)s and %(to_date)s") + if filters.get("voucher_no"): conditions.append("voucher_no=%(voucher_no)s") return "and {}".format(" and ".join(conditions)) if conditions else "" - -def get_grouped_gle(filters): - gle_map = {} - gle = get_gl_entries(filters) - for d in gle: - gle_map.setdefault(d[1 if filters["group_by"]=="Group by Account" else 5], []).append(d) - + +def get_data_with_opening_closing(filters, account_details, gl_entries): data = [] - for entries in gle_map.values(): - subtotal_debit = subtotal_credit = 0.0 - for entry in entries: - data.append(entry) - subtotal_debit += flt(entry[2]) - subtotal_credit += flt(entry[3]) - - data.append(["", "Total", subtotal_debit, subtotal_credit, "", "", ""]) + gle_map = initialize_gle_map(gl_entries) + + opening, total_debit, total_credit, gle_map = get_accountwise_gle(filters, gl_entries, gle_map) + + # Opening for filtered account + if filters.get("account"): + data += [get_balance_row("Opening", account_details[filters.account].debit_or_credit, + opening), {}] + + for acc, acc_dict in gle_map.items(): + if acc_dict.entries: + # Opening for individual ledger, if grouped by account + if filters.get("group_by_account"): + data.append(get_balance_row("Opening", account_details[acc].debit_or_credit, + acc_dict.opening)) + + data += acc_dict.entries + + # Totals and closing for individual ledger, if grouped by account + if filters.get("group_by_account"): + data += [{"account": "Totals", "debit": acc_dict.total_debit, + "credit": acc_dict.total_credit}, + get_balance_row("Closing (Opening + Totals)", + account_details[acc].debit_or_credit, (acc_dict.opening + + acc_dict.total_debit - acc_dict.total_credit)), {}] + + # Total debit and credit between from and to date + if total_debit or total_credit: + data.append({"account": "Totals", "debit": total_debit, "credit": total_credit}) + + # Closing for filtered account + if filters.get("account"): + data.append(get_balance_row("Closing (Opening + Totals)", + account_details[filters.account].debit_or_credit, + (opening + total_debit - total_credit))) - if data: - data.append(get_total_row(gle)) return data + +def initialize_gle_map(gl_entries): + gle_map = webnotes._dict() + for gle in gl_entries: + gle_map.setdefault(gle.account, webnotes._dict({ + "opening": 0, + "entries": [], + "total_debit": 0, + "total_credit": 0, + "closing": 0 + })) + return gle_map + +def get_accountwise_gle(filters, gl_entries, gle_map): + opening, total_debit, total_credit = 0, 0, 0 -def get_total_row(gle): - total_debit = total_credit = 0.0 - for d in gle: - total_debit += flt(d[2]) - total_credit += flt(d[3]) + for gle in gl_entries: + amount = flt(gle.debit) - flt(gle.credit) + if filters.get("account") and (gle.posting_date < filters.from_date + or cstr(gle.is_advance) == "Yes"): + gle_map[gle.account].opening += amount + opening += amount + elif gle.posting_date < filters.to_date: + gle_map[gle.account].entries.append(gle) + gle_map[gle.account].total_debit += flt(gle.debit) + gle_map[gle.account].total_credit += flt(gle.credit) + + total_debit += flt(gle.debit) + total_credit += flt(gle.credit) + + return opening, total_debit, total_credit, gle_map + +def get_balance_row(label, debit_or_credit, balance): + return { + "account": label, + "debit": balance if debit_or_credit=="Debit" else 0, + "credit": -1*balance if debit_or_credit=="Credit" else 0, + } + +def get_result_as_list(data): + result = [] + for d in data: + result.append([d.get("posting_date"), d.get("account"), d.get("debit"), + d.get("credit"), d.get("voucher_type"), d.get("voucher_no"), + get_voucher_link(d.get("voucher_type"), d.get("voucher_no")), + d.get("against"), d.get("cost_center"), d.get("remarks")]) + + return result + +def get_voucher_link(voucher_type, voucher_no): + icon = "" + if voucher_type and voucher_no: + icon = """ + """ % ("/".join(["#Form", voucher_type, voucher_no])) - return ["", "Total Debit/Credit", total_debit, total_credit, "", "", ""] \ No newline at end of file + return icon \ No newline at end of file From e761fe89e22b131fc084f19b0ad6b01c797189c3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 27 Dec 2013 17:35:39 +0530 Subject: [PATCH 2/9] Stock Entry catch exceptions for testcase --- accounts/doctype/sales_invoice/test_sales_invoice.py | 1 - stock/doctype/stock_entry/stock_entry.py | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 9b740d1061..5a573f9441 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -364,7 +364,6 @@ class TestSalesInvoice(unittest.TestCase): from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s order by account asc, debit asc""", si.doc.name, as_dict=1) self.assertTrue(gl_entries) - # print gl_entries stock_in_hand = webnotes.conn.get_value("Account", {"master_name": "_Test Warehouse - _TC"}) diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 2e7e2a4066..7dec8785c4 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -290,11 +290,12 @@ class DocType(StockController): if not returnable_qty: webnotes.throw("{item}: {item_code} {returned}".format( item=_("Item"), item_code=item.item_code, - returned=_("already returned though some other documents"))) + returned=_("already returned though some other documents")), + StockOverReturnError) elif item.transfer_qty > returnable_qty: webnotes.throw("{item}: {item_code}, {returned}: {qty}".format( item=_("Item"), item_code=item.item_code, - returned=_("Max Returnable Qty"), qty=returnable_qty)) + returned=_("Max Returnable Qty"), qty=returnable_qty), StockOverReturnError) def get_already_returned_item_qty(self, ref_fieldname): return dict(webnotes.conn.sql("""select item_code, sum(transfer_qty) as qty From a4f99428d78cc22b616a980f57003d751c816e68 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 27 Dec 2013 17:36:17 +0530 Subject: [PATCH 3/9] Fixes in time log batch --- projects/doctype/time_log_batch/time_log_batch.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/projects/doctype/time_log_batch/time_log_batch.py b/projects/doctype/time_log_batch/time_log_batch.py index ccb0a25624..593dc222f1 100644 --- a/projects/doctype/time_log_batch/time_log_batch.py +++ b/projects/doctype/time_log_batch/time_log_batch.py @@ -28,7 +28,7 @@ class DocType: }) def validate_time_log_is_submitted(self, tl): - if tl.status != "Submitted": + if tl.status != "Submitted" and self.doc.docstatus == 0: webnotes.msgprint(_("Time Log must have status 'Submitted'") + \ " :" + tl.name + " (" + _(tl.status) + ")", raise_exception=True) @@ -57,7 +57,4 @@ class DocType: tl = webnotes.bean("Time Log", d.time_log) tl.doc.time_log_batch = time_log_batch tl.doc.sales_invoice = self.doc.sales_invoice - tl.update_after_submit() - - - \ No newline at end of file + tl.update_after_submit() \ No newline at end of file From ab9d755e4d612fdd3d300f879c20834f3dd21686 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 30 Dec 2013 18:18:01 +0600 Subject: [PATCH 4/9] bumped to version 3.4.0 --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index b57ba61ba1..de048e5b53 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.3.8", + "app_version": "3.4.0", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { @@ -74,5 +74,5 @@ "type": "module" } }, - "requires_framework_version": "==3.3.2" + "requires_framework_version": "==3.4.0" } \ No newline at end of file From ba7221c3325acd48c1f1534921d67d11003ba718 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 30 Dec 2013 19:28:03 +0600 Subject: [PATCH 5/9] bumped to version 3.4.1 --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index de048e5b53..2309618f9d 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.4.0", + "app_version": "3.4.1", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { @@ -74,5 +74,5 @@ "type": "module" } }, - "requires_framework_version": "==3.4.0" + "requires_framework_version": "==3.4.1" } \ No newline at end of file From 9cd9836e1813c6aa2bc5d7f0607472826ffb3a2a Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 30 Dec 2013 20:04:38 +0600 Subject: [PATCH 6/9] bumped to version 3.4.2 --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 2309618f9d..80f1952f1a 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.4.1", + "app_version": "3.4.2", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { @@ -74,5 +74,5 @@ "type": "module" } }, - "requires_framework_version": "==3.4.1" + "requires_framework_version": "==3.4.2" } \ No newline at end of file From 2a3d7e660fbb4d7490e017cd34f12ca707f4f106 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 30 Dec 2013 20:28:14 +0530 Subject: [PATCH 7/9] Fixes in general ledger report --- accounts/report/general_ledger/general_ledger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/report/general_ledger/general_ledger.js b/accounts/report/general_ledger/general_ledger.js index f2e60b6447..96682f9647 100644 --- a/accounts/report/general_ledger/general_ledger.js +++ b/accounts/report/general_ledger/general_ledger.js @@ -38,7 +38,7 @@ wn.query_reports["General Ledger"] = { "get_query": function() { var company = wn.query_report.filters_by_name.company.get_value(); return { - "query": "accounts.utils.get_account_list", + "doctype": "Account", "filters": { "company": company, } From 424b4a4b36d7b650365bc752fc7729ff725ff968 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 31 Dec 2013 10:24:28 +0530 Subject: [PATCH 8/9] Supplier bill info in accounts payable report --- .../accounts_payable/accounts_payable.py | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index e5489d100f..f9266dc9ea 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -19,8 +19,8 @@ def execute(filters=None): for gle in get_gl_entries(filters, before_report_date=False)] account_supplier_type_map = get_account_supplier_type_map() - pi_map = get_pi_map() - + voucher_detail_map = get_voucher_details() + # Age of the invoice on this date age_on = getdate(filters.get("report_date")) > getdate(nowdate()) \ and nowdate() or filters.get("report_date") @@ -29,14 +29,8 @@ def execute(filters=None): for gle in entries: 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: - if gle.voucher_type == "Purchase Invoice": - pi_info = pi_map.get(gle.voucher_no) - due_date = pi_info.get("due_date") - bill_no = pi_info.get("bill_no") - bill_date = pi_info.get("bill_date") - else: - due_date = bill_no = bill_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, filters.get("report_date") or nowdate()) @@ -44,13 +38,15 @@ def execute(filters=None): if abs(flt(outstanding_amount)) > 0.01: paid_amount = invoiced_amount - outstanding_amount row = [gle.posting_date, gle.account, account_supplier.get(gle.account, ""), - gle.voucher_type, gle.voucher_no, - gle.remarks, account_supplier_type_map.get(gle.account), due_date, bill_no, - bill_date, invoiced_amount, paid_amount, outstanding_amount] + gle.voucher_type, gle.voucher_no, gle.remarks, + account_supplier_type_map.get(gle.account), + 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 = due_date + ageing_based_on_date = voucher_details.get("due_date", "") else: ageing_based_on_date = gle.posting_date @@ -112,14 +108,15 @@ def get_account_supplier_type_map(): return account_supplier_type_map -def get_pi_map(): - """ get due_date from sales invoice """ - pi_map = {} - for t in webnotes.conn.sql("""select name, due_date, bill_no, bill_date - from `tabPurchase Invoice`""", as_dict=1): - pi_map[t.name] = t +def get_voucher_details(): + voucher_details = {} + for dt in ["Purchase Invoice", "Journal Voucher"]: + voucher_details.setdefault(dt, webnotes._dict()) + for t in webnotes.conn.sql("""select name, due_date, bill_no, bill_date + from `tab%s`""" % dt, as_dict=1): + voucher_details[dt].setdefault(t.name, t) - return pi_map + return voucher_details def get_outstanding_amount(gle, report_date): payment_amount = webnotes.conn.sql(""" From ffe8af3f2f14c2d16c07c81d41b84131c16c651c Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 31 Dec 2013 14:42:36 +0600 Subject: [PATCH 9/9] bumped to version 3.4.3 --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 80f1952f1a..c09c2afac9 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.4.2", + "app_version": "3.4.3", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": {