From 9739d8b52a3a5c628aa62223b18ff2c18b1444b9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 21 Feb 2023 15:23:29 +0530 Subject: [PATCH 01/50] feat: Introduce opening entry for reporting --- .../doctype/opening_entry/__init__.py | 0 .../doctype/opening_entry/opening_entry.js | 8 + .../doctype/opening_entry/opening_entry.json | 268 ++++++++++++++++++ .../doctype/opening_entry/opening_entry.py | 9 + .../opening_entry/test_opening_entry.py | 9 + 5 files changed, 294 insertions(+) create mode 100644 erpnext/accounts/doctype/opening_entry/__init__.py create mode 100644 erpnext/accounts/doctype/opening_entry/opening_entry.js create mode 100644 erpnext/accounts/doctype/opening_entry/opening_entry.json create mode 100644 erpnext/accounts/doctype/opening_entry/opening_entry.py create mode 100644 erpnext/accounts/doctype/opening_entry/test_opening_entry.py diff --git a/erpnext/accounts/doctype/opening_entry/__init__.py b/erpnext/accounts/doctype/opening_entry/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/accounts/doctype/opening_entry/opening_entry.js b/erpnext/accounts/doctype/opening_entry/opening_entry.js new file mode 100644 index 000000000000..379041d54c62 --- /dev/null +++ b/erpnext/accounts/doctype/opening_entry/opening_entry.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Opening Entry", { +// refresh(frm) { + +// }, +// }); diff --git a/erpnext/accounts/doctype/opening_entry/opening_entry.json b/erpnext/accounts/doctype/opening_entry/opening_entry.json new file mode 100644 index 000000000000..c39970b878ac --- /dev/null +++ b/erpnext/accounts/doctype/opening_entry/opening_entry.json @@ -0,0 +1,268 @@ +{ + "actions": [], + "creation": "2023-02-21 15:20:59.586811", + "default_view": "List", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "closing_date", + "account", + "party_type", + "party", + "cost_center", + "debit", + "credit", + "account_currency", + "debit_in_account_currency", + "credit_in_account_currency", + "against", + "against_voucher_type", + "against_voucher", + "voucher_type", + "voucher_no", + "voucher_detail_no", + "project", + "is_opening", + "is_advance", + "fiscal_year", + "company", + "finance_book", + "to_rename", + "due_date" + ], + "fields": [ + { + "fieldname": "closing_date", + "fieldtype": "Date", + "in_filter": 1, + "in_list_view": 1, + "label": "Closing Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "search_index": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Account", + "oldfieldname": "account", + "oldfieldtype": "Link", + "options": "Account", + "search_index": 1 + }, + { + "fieldname": "party_type", + "fieldtype": "Link", + "label": "Party Type", + "options": "DocType", + "search_index": 1 + }, + { + "fieldname": "party", + "fieldtype": "Dynamic Link", + "in_standard_filter": 1, + "label": "Party", + "options": "party_type", + "search_index": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Cost Center", + "oldfieldname": "cost_center", + "oldfieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname": "debit", + "fieldtype": "Currency", + "label": "Debit Amount", + "oldfieldname": "debit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency" + }, + { + "fieldname": "credit", + "fieldtype": "Currency", + "label": "Credit Amount", + "oldfieldname": "credit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency" + }, + { + "fieldname": "account_currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency" + }, + { + "fieldname": "debit_in_account_currency", + "fieldtype": "Currency", + "label": "Debit Amount in Account Currency", + "options": "account_currency" + }, + { + "fieldname": "credit_in_account_currency", + "fieldtype": "Currency", + "label": "Credit Amount in Account Currency", + "options": "account_currency" + }, + { + "fieldname": "against", + "fieldtype": "Text", + "in_filter": 1, + "label": "Against", + "oldfieldname": "against", + "oldfieldtype": "Text" + }, + { + "fieldname": "against_voucher_type", + "fieldtype": "Link", + "label": "Against Voucher Type", + "oldfieldname": "against_voucher_type", + "oldfieldtype": "Data", + "options": "DocType", + "search_index": 1 + }, + { + "fieldname": "against_voucher", + "fieldtype": "Dynamic Link", + "in_filter": 1, + "label": "Against Voucher", + "oldfieldname": "against_voucher", + "oldfieldtype": "Data", + "options": "against_voucher_type", + "search_index": 1 + }, + { + "fieldname": "voucher_type", + "fieldtype": "Link", + "in_filter": 1, + "label": "Voucher Type", + "oldfieldname": "voucher_type", + "oldfieldtype": "Select", + "options": "DocType", + "search_index": 1 + }, + { + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "in_filter": 1, + "in_standard_filter": 1, + "label": "Voucher No", + "oldfieldname": "voucher_no", + "oldfieldtype": "Data", + "options": "voucher_type", + "search_index": 1 + }, + { + "fieldname": "voucher_detail_no", + "fieldtype": "Data", + "label": "Voucher Detail No", + "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "is_opening", + "fieldtype": "Select", + "in_filter": 1, + "label": "Is Opening", + "oldfieldname": "is_opening", + "oldfieldtype": "Select", + "options": "No\nYes" + }, + { + "fieldname": "is_advance", + "fieldtype": "Select", + "label": "Is Advance", + "oldfieldname": "is_advance", + "oldfieldtype": "Select", + "options": "No\nYes" + }, + { + "fieldname": "fiscal_year", + "fieldtype": "Link", + "in_filter": 1, + "label": "Fiscal Year", + "oldfieldname": "fiscal_year", + "oldfieldtype": "Select", + "options": "Fiscal Year" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "search_index": 1 + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" + }, + { + "default": "1", + "fieldname": "to_rename", + "fieldtype": "Check", + "hidden": 1, + "label": "To Rename", + "search_index": 1 + }, + { + "fieldname": "due_date", + "fieldtype": "Date", + "label": "Due Date" + } + ], + "icon": "fa fa-list", + "in_create": 1, + "links": [], + "modified": "2023-02-21 15:20:59.586811", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Opening Entry", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User" + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager" + }, + { + "export": 1, + "read": 1, + "report": 1, + "role": "Auditor" + } + ], + "search_fields": "voucher_no,account,against_voucher", + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/opening_entry/opening_entry.py b/erpnext/accounts/doctype/opening_entry/opening_entry.py new file mode 100644 index 000000000000..a839b0188cec --- /dev/null +++ b/erpnext/accounts/doctype/opening_entry/opening_entry.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class OpeningEntry(Document): + pass diff --git a/erpnext/accounts/doctype/opening_entry/test_opening_entry.py b/erpnext/accounts/doctype/opening_entry/test_opening_entry.py new file mode 100644 index 000000000000..c21be5563c94 --- /dev/null +++ b/erpnext/accounts/doctype/opening_entry/test_opening_entry.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestOpeningEntry(FrappeTestCase): + pass From b44a19bd1ab58843de154533114ae7dcb05e2744 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 21 Feb 2023 23:20:28 +0530 Subject: [PATCH 02/50] feat: Introduce opening entry for reporting --- .../doctype/{opening_entry => closing_balance}/__init__.py | 0 .../opening_entry.js => closing_balance/closing_balance.js} | 2 +- .../opening_entry.json => closing_balance/closing_balance.json} | 2 +- .../opening_entry.py => closing_balance/closing_balance.py} | 2 +- .../test_closing_balance.py} | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename erpnext/accounts/doctype/{opening_entry => closing_balance}/__init__.py (100%) rename erpnext/accounts/doctype/{opening_entry/opening_entry.js => closing_balance/closing_balance.js} (78%) rename erpnext/accounts/doctype/{opening_entry/opening_entry.json => closing_balance/closing_balance.json} (99%) rename erpnext/accounts/doctype/{opening_entry/opening_entry.py => closing_balance/closing_balance.py} (85%) rename erpnext/accounts/doctype/{opening_entry/test_opening_entry.py => closing_balance/test_closing_balance.py} (79%) diff --git a/erpnext/accounts/doctype/opening_entry/__init__.py b/erpnext/accounts/doctype/closing_balance/__init__.py similarity index 100% rename from erpnext/accounts/doctype/opening_entry/__init__.py rename to erpnext/accounts/doctype/closing_balance/__init__.py diff --git a/erpnext/accounts/doctype/opening_entry/opening_entry.js b/erpnext/accounts/doctype/closing_balance/closing_balance.js similarity index 78% rename from erpnext/accounts/doctype/opening_entry/opening_entry.js rename to erpnext/accounts/doctype/closing_balance/closing_balance.js index 379041d54c62..1a43e0664da7 100644 --- a/erpnext/accounts/doctype/opening_entry/opening_entry.js +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.js @@ -1,7 +1,7 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -// frappe.ui.form.on("Opening Entry", { +// frappe.ui.form.on("Closing Balance", { // refresh(frm) { // }, diff --git a/erpnext/accounts/doctype/opening_entry/opening_entry.json b/erpnext/accounts/doctype/closing_balance/closing_balance.json similarity index 99% rename from erpnext/accounts/doctype/opening_entry/opening_entry.json rename to erpnext/accounts/doctype/closing_balance/closing_balance.json index c39970b878ac..5a47f72585eb 100644 --- a/erpnext/accounts/doctype/opening_entry/opening_entry.json +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.json @@ -235,7 +235,7 @@ "modified": "2023-02-21 15:20:59.586811", "modified_by": "Administrator", "module": "Accounts", - "name": "Opening Entry", + "name": "Closing Balance", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/accounts/doctype/opening_entry/opening_entry.py b/erpnext/accounts/doctype/closing_balance/closing_balance.py similarity index 85% rename from erpnext/accounts/doctype/opening_entry/opening_entry.py rename to erpnext/accounts/doctype/closing_balance/closing_balance.py index a839b0188cec..899749f30419 100644 --- a/erpnext/accounts/doctype/opening_entry/opening_entry.py +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.py @@ -5,5 +5,5 @@ from frappe.model.document import Document -class OpeningEntry(Document): +class ClosingBalance(Document): pass diff --git a/erpnext/accounts/doctype/opening_entry/test_opening_entry.py b/erpnext/accounts/doctype/closing_balance/test_closing_balance.py similarity index 79% rename from erpnext/accounts/doctype/opening_entry/test_opening_entry.py rename to erpnext/accounts/doctype/closing_balance/test_closing_balance.py index c21be5563c94..4e81316210e4 100644 --- a/erpnext/accounts/doctype/opening_entry/test_opening_entry.py +++ b/erpnext/accounts/doctype/closing_balance/test_closing_balance.py @@ -5,5 +5,5 @@ from frappe.tests.utils import FrappeTestCase -class TestOpeningEntry(FrappeTestCase): +class TestClosingBalance(FrappeTestCase): pass From 36c08d08357d0384a1238ac37f7a9bf1585fc554 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 23 Feb 2023 16:46:37 +0530 Subject: [PATCH 03/50] fix: Add patches to create accounting dimension in Closing Balance --- ...counting_dimensions_for_closing_balance.py | 31 +++++++++++++++++++ .../patches/v14_0/update_closing_balances.py | 9 ++++++ 2 files changed, 40 insertions(+) create mode 100644 erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py create mode 100644 erpnext/patches/v14_0/update_closing_balances.py diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py b/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py new file mode 100644 index 000000000000..375782171ca5 --- /dev/null +++ b/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py @@ -0,0 +1,31 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + accounting_dimensions = frappe.db.get_all( + "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"] + ) + + if not accounting_dimensions: + return + + doctype = "Closing Balance" + + for d in accounting_dimensions: + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) + + if field: + continue + + df = { + "fieldname": d.fieldname, + "label": d.label, + "fieldtype": "Link", + "options": d.document_type, + "insert_after": "accounting_dimensions_section", + } + + create_custom_field(doctype, df, ignore_validate=True) + + frappe.clear_cache(doctype=doctype) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py new file mode 100644 index 000000000000..cd120b553cce --- /dev/null +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +# import frappe + + +def execute(): + pass From c3f39c3f32ed90f934e264b25ee530852e0ed6c1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 23 Feb 2023 16:48:54 +0530 Subject: [PATCH 04/50] feat: Cascade closing balances on PCV submit --- .../closing_balance/closing_balance.json | 107 +----------------- .../closing_balance/closing_balance.py | 37 +++++- .../period_closing_voucher.py | 82 ++++++++++++-- erpnext/patches.txt | 2 + 4 files changed, 118 insertions(+), 110 deletions(-) diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.json b/erpnext/accounts/doctype/closing_balance/closing_balance.json index 5a47f72585eb..f99e7921b52c 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.json +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.json @@ -8,28 +8,18 @@ "field_order": [ "closing_date", "account", - "party_type", - "party", "cost_center", "debit", "credit", "account_currency", "debit_in_account_currency", "credit_in_account_currency", - "against", - "against_voucher_type", - "against_voucher", - "voucher_type", - "voucher_no", - "voucher_detail_no", "project", "is_opening", - "is_advance", "fiscal_year", "company", "finance_book", - "to_rename", - "due_date" + "period_closing_voucher" ], "fields": [ { @@ -54,21 +44,6 @@ "options": "Account", "search_index": 1 }, - { - "fieldname": "party_type", - "fieldtype": "Link", - "label": "Party Type", - "options": "DocType", - "search_index": 1 - }, - { - "fieldname": "party", - "fieldtype": "Dynamic Link", - "in_standard_filter": 1, - "label": "Party", - "options": "party_type", - "search_index": 1 - }, { "fieldname": "cost_center", "fieldtype": "Link", @@ -113,60 +88,6 @@ "label": "Credit Amount in Account Currency", "options": "account_currency" }, - { - "fieldname": "against", - "fieldtype": "Text", - "in_filter": 1, - "label": "Against", - "oldfieldname": "against", - "oldfieldtype": "Text" - }, - { - "fieldname": "against_voucher_type", - "fieldtype": "Link", - "label": "Against Voucher Type", - "oldfieldname": "against_voucher_type", - "oldfieldtype": "Data", - "options": "DocType", - "search_index": 1 - }, - { - "fieldname": "against_voucher", - "fieldtype": "Dynamic Link", - "in_filter": 1, - "label": "Against Voucher", - "oldfieldname": "against_voucher", - "oldfieldtype": "Data", - "options": "against_voucher_type", - "search_index": 1 - }, - { - "fieldname": "voucher_type", - "fieldtype": "Link", - "in_filter": 1, - "label": "Voucher Type", - "oldfieldname": "voucher_type", - "oldfieldtype": "Select", - "options": "DocType", - "search_index": 1 - }, - { - "fieldname": "voucher_no", - "fieldtype": "Dynamic Link", - "in_filter": 1, - "in_standard_filter": 1, - "label": "Voucher No", - "oldfieldname": "voucher_no", - "oldfieldtype": "Data", - "options": "voucher_type", - "search_index": 1 - }, - { - "fieldname": "voucher_detail_no", - "fieldtype": "Data", - "label": "Voucher Detail No", - "read_only": 1 - }, { "fieldname": "project", "fieldtype": "Link", @@ -182,14 +103,6 @@ "oldfieldtype": "Select", "options": "No\nYes" }, - { - "fieldname": "is_advance", - "fieldtype": "Select", - "label": "Is Advance", - "oldfieldname": "is_advance", - "oldfieldtype": "Select", - "options": "No\nYes" - }, { "fieldname": "fiscal_year", "fieldtype": "Link", @@ -216,23 +129,16 @@ "options": "Finance Book" }, { - "default": "1", - "fieldname": "to_rename", - "fieldtype": "Check", - "hidden": 1, - "label": "To Rename", - "search_index": 1 - }, - { - "fieldname": "due_date", - "fieldtype": "Date", - "label": "Due Date" + "fieldname": "period_closing_voucher", + "fieldtype": "Link", + "label": "Period Closing Voucher", + "options": "Period Closing Voucher" } ], "icon": "fa fa-list", "in_create": 1, "links": [], - "modified": "2023-02-21 15:20:59.586811", + "modified": "2023-02-22 19:28:14.490403", "modified_by": "Administrator", "module": "Accounts", "name": "Closing Balance", @@ -261,7 +167,6 @@ "role": "Auditor" } ], - "search_fields": "voucher_no,account,against_voucher", "sort_field": "modified", "sort_order": "DESC", "states": [] diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.py b/erpnext/accounts/doctype/closing_balance/closing_balance.py index 899749f30419..572553d8080f 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.py +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.py @@ -1,9 +1,42 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) + class ClosingBalance(Document): - pass + def aggregate_with_last_closing_balance(self, accounting_dimensions): + closing_balance = frappe.qb.DocType("Closing Balance") + + query = ( + frappe.qb.from_(closing_balance) + .select(closing_balance.debit, closing_balance.credit) + .where( + closing_balance.closing_date < self.closing_date, + ) + ) + + for dimension in accounting_dimensions: + query = query.where(closing_balance[dimension] == self.get(dimension)) + + query.orderby(closing_balance.closing_date, order=frappe.qb.desc).limit(1) + + last_closing_balance = query.run(as_dict=1) + + if last_closing_balance: + self.debit += last_closing_balance[0].debit + self.credit += last_closing_balance[0].credit + + +def make_closing_entries(closing_entries): + accounting_dimensions = get_accounting_dimensions() + for entry in closing_entries: + cle = frappe.new_doc("Closing Balance") + cle.update(entry) + cle.aggregate_with_last_closing_balance(accounting_dimensions) + cle.submit() diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index ca98bee5c1cc..af90d97218cc 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -21,6 +21,7 @@ def validate(self): def on_submit(self): self.db_set("gle_processing_status", "In Progress") self.make_gl_entries() + self.make_closing_entries() def on_cancel(self): self.db_set("gle_processing_status", "In Progress") @@ -90,16 +91,37 @@ def make_gl_entries(self): else: process_gl_entries(gl_entries) + def make_closing_entries(self): + closing_entries = self.get_grouped_gl_entries() + if closing_entries: + if len(closing_entries) > 5000: + frappe.enqueue(process_closing_entries, gl_entries=closing_entries, queue="long") + frappe.msgprint( + _("The Opening Entries will be processed in the background, it can take a few minutes."), + alert=True, + ) + else: + process_closing_entries(closing_entries) + + def get_grouped_gl_entries(self): + closing_entries = [] + for acc in self.get_balances_based_on_dimensions( + group_by_account=True, report_type=["Profit and Loss", "Balance Sheet"], for_aggregation=True + ): + closing_entries.append(self.get_closing_entries(acc)) + + return closing_entries + def get_gl_entries(self): gl_entries = [] # pl account - for acc in self.get_pl_balances_based_on_dimensions(group_by_account=True): + for acc in self.get_balances_based_on_dimensions(group_by_account=True): if flt(acc.bal_in_company_currency): gl_entries.append(self.get_gle_for_pl_account(acc)) # closing liability account - for acc in self.get_pl_balances_based_on_dimensions(group_by_account=False): + for acc in self.get_balances_based_on_dimensions(group_by_account=False): if flt(acc.bal_in_company_currency): gl_entries.append(self.get_gle_for_closing_account(acc)) @@ -147,6 +169,25 @@ def get_gle_for_closing_account(self, acc): self.update_default_dimensions(gl_entry, acc) return gl_entry + def get_closing_entries(self, acc): + closing_entry = self.get_gl_dict( + { + "closing_date": self.posting_date, + "period_closing_voucher": self.name, + "account": acc.account, + "cost_center": acc.cost_center, + "finance_book": acc.finance_book, + "account_currency": acc.account_currency, + "debit_in_account_currency": flt(acc.debit_in_account_currency), + "debit": flt(acc.debit), + "credit_in_account_currency": flt(acc.credit_in_account_currency), + "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, + }, + item=acc, + ) + + return closing_entry + def update_default_dimensions(self, gl_entry, acc): if not self.accounting_dimensions: self.accounting_dimensions = get_accounting_dimensions() @@ -154,9 +195,24 @@ def update_default_dimensions(self, gl_entry, acc): for dimension in self.accounting_dimensions: gl_entry.update({dimension: acc.get(dimension)}) - def get_pl_balances_based_on_dimensions(self, group_by_account=False): + def get_balances_based_on_dimensions( + self, group_by_account=False, report_type=["Profit and Loss"], for_aggregation=False + ): """Get balance for dimension-wise pl accounts""" + if for_aggregation: + balance_fields = [ + "sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency", + "sum(t1.debit) - sum(t1.credit) as bal_in_company_currency", + ] + else: + balance_fields = [ + "sum(t1.debit_in_account_currency) as debit_in_account_currency", + "sum(t1.credit_in_account_currency) as credit_in_account_currency", + "sum(t1.debit) as debit", + "sum(t1.credit) as credit", + ] + dimension_fields = ["t1.cost_center", "t1.finance_book"] self.accounting_dimensions = get_accounting_dimensions() @@ -169,27 +225,39 @@ def get_pl_balances_based_on_dimensions(self, group_by_account=False): return frappe.db.sql( """ select + t1.account, t2.account_currency, {dimension_fields}, - sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency, - sum(t1.debit) - sum(t1.credit) as bal_in_company_currency + {balance_fields} from `tabGL Entry` t1, `tabAccount` t2 where t1.is_cancelled = 0 and t1.account = t2.name - and t2.report_type = 'Profit and Loss' + and t2.report_type in ("{report_type}") and t2.docstatus < 2 and t2.company = %s and t1.posting_date between %s and %s group by {dimension_fields} """.format( - dimension_fields=", ".join(dimension_fields) + dimension_fields=", ".join(dimension_fields), + balance_fields=", ".join(balance_fields), + report_type='", "'.join(report_type), ), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1, ) +def process_closing_entries(closing_entries): + from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries + + try: + make_closing_entries(closing_entries) + except Exception as e: + frappe.db.rollback() + frappe.log_error(e) + + def process_gl_entries(gl_entries): from erpnext.accounts.general_ledger import make_gl_entries diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 211f07445a1c..d0873e8be1f3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -327,3 +327,5 @@ erpnext.patches.v14_0.update_entry_type_for_journal_entry erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers erpnext.patches.v14_0.set_pick_list_status erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries +erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance +#erpnext.patches.v14_0.update_closing_balances From e18336ebe74a7b3dd14dc1ae65941ee594862996 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 26 Feb 2023 15:47:29 +0530 Subject: [PATCH 05/50] feat: Add views in standard filter --- .../accounts/doctype/closing_balance/closing_balance.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.json b/erpnext/accounts/doctype/closing_balance/closing_balance.json index f99e7921b52c..377121f5eea9 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.json +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.json @@ -116,6 +116,8 @@ "fieldname": "company", "fieldtype": "Link", "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, "label": "Company", "oldfieldname": "company", "oldfieldtype": "Link", @@ -131,6 +133,7 @@ { "fieldname": "period_closing_voucher", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Period Closing Voucher", "options": "Period Closing Voucher" } @@ -138,7 +141,7 @@ "icon": "fa fa-list", "in_create": 1, "links": [], - "modified": "2023-02-22 19:28:14.490403", + "modified": "2023-02-24 18:11:11.612395", "modified_by": "Administrator", "module": "Accounts", "name": "Closing Balance", From 7fa7d6b5e46dd0e1f44bedd80d6a0c1fbc514f8f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 26 Feb 2023 15:52:30 +0530 Subject: [PATCH 06/50] chore: Rewrite query using query builder --- .../period_closing_voucher.py | 121 +++++++++++------- 1 file changed, 74 insertions(+), 47 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index af90d97218cc..5d118bc8fbae 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Sum from frappe.utils import flt from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -43,6 +44,14 @@ def on_cancel(self): else: make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name) + self.delete_closing_entries() + + def delete_closing_entries(self): + closing_balance = frappe.qb.DocType("Closing Balance") + frappe.qb.from_(closing_balance).delete().where( + closing_balance.period_closing_voucher == self.name + ).run() + def validate_account_head(self): closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") @@ -93,6 +102,7 @@ def make_gl_entries(self): def make_closing_entries(self): closing_entries = self.get_grouped_gl_entries() + if closing_entries: if len(closing_entries) > 5000: frappe.enqueue(process_closing_entries, gl_entries=closing_entries, queue="long") @@ -105,9 +115,7 @@ def make_closing_entries(self): def get_grouped_gl_entries(self): closing_entries = [] - for acc in self.get_balances_based_on_dimensions( - group_by_account=True, report_type=["Profit and Loss", "Balance Sheet"], for_aggregation=True - ): + for acc in self.get_balances_based_on_dimensions(group_by_account=True, for_aggregation=True): closing_entries.append(self.get_closing_entries(acc)) return closing_entries @@ -116,12 +124,16 @@ def get_gl_entries(self): gl_entries = [] # pl account - for acc in self.get_balances_based_on_dimensions(group_by_account=True): + for acc in self.get_balances_based_on_dimensions( + group_by_account=True, report_type="Profit and Loss" + ): if flt(acc.bal_in_company_currency): gl_entries.append(self.get_gle_for_pl_account(acc)) # closing liability account - for acc in self.get_balances_based_on_dimensions(group_by_account=False): + for acc in self.get_balances_based_on_dimensions( + group_by_account=False, report_type="Profit and Loss" + ): if flt(acc.bal_in_company_currency): gl_entries.append(self.get_gle_for_closing_account(acc)) @@ -181,11 +193,14 @@ def get_closing_entries(self, acc): "debit_in_account_currency": flt(acc.debit_in_account_currency), "debit": flt(acc.debit), "credit_in_account_currency": flt(acc.credit_in_account_currency), - "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, + "credit": flt(acc.credit), }, item=acc, ) + for dimension in self.accounting_dimensions: + closing_entry.update({dimension: acc.get(dimension)}) + return closing_entry def update_default_dimensions(self, gl_entry, acc): @@ -196,57 +211,69 @@ def update_default_dimensions(self, gl_entry, acc): gl_entry.update({dimension: acc.get(dimension)}) def get_balances_based_on_dimensions( - self, group_by_account=False, report_type=["Profit and Loss"], for_aggregation=False + self, group_by_account=False, report_type=None, for_aggregation=False ): """Get balance for dimension-wise pl accounts""" - if for_aggregation: - balance_fields = [ - "sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency", - "sum(t1.debit) - sum(t1.credit) as bal_in_company_currency", - ] - else: - balance_fields = [ - "sum(t1.debit_in_account_currency) as debit_in_account_currency", - "sum(t1.credit_in_account_currency) as credit_in_account_currency", - "sum(t1.debit) as debit", - "sum(t1.credit) as credit", - ] - - dimension_fields = ["t1.cost_center", "t1.finance_book"] + qb_dimension_fields = ["cost_center", "finance_book"] self.accounting_dimensions = get_accounting_dimensions() for dimension in self.accounting_dimensions: - dimension_fields.append("t1.{0}".format(dimension)) + qb_dimension_fields.append(dimension) if group_by_account: - dimension_fields.append("t1.account") - - return frappe.db.sql( - """ - select - t1.account, - t2.account_currency, - {dimension_fields}, - {balance_fields} - from `tabGL Entry` t1, `tabAccount` t2 - where - t1.is_cancelled = 0 - and t1.account = t2.name - and t2.report_type in ("{report_type}") - and t2.docstatus < 2 - and t2.company = %s - and t1.posting_date between %s and %s - group by {dimension_fields} - """.format( - dimension_fields=", ".join(dimension_fields), - balance_fields=", ".join(balance_fields), - report_type='", "'.join(report_type), - ), - (self.company, self.get("year_start_date"), self.posting_date), - as_dict=1, + qb_dimension_fields.append("account") + + account = frappe.qb.DocType("Account") + accounts_query = ( + frappe.qb.from_(account) + .select(account.name) + .where((account.company == self.company) & (account.is_group == 0) & (account.docstatus < 2)) ) + if report_type: + accounts_query = accounts_query.where(account.report_type == report_type) + + accounts = accounts_query.run(as_dict=True) + + accounts = [d.name for d in accounts] + + gl_entry = frappe.qb.DocType("GL Entry") + query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency) + + if not for_aggregation: + query = query.select( + (Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_( + "bal_in_account_currency" + ), + (Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"), + ) + else: + query = query.select( + (Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"), + (Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"), + (Sum(gl_entry.debit)).as_("debit"), + (Sum(gl_entry.credit)).as_("credit"), + ) + + for dimension in qb_dimension_fields: + query = query.select(gl_entry[dimension]) + + query = query.where( + (gl_entry.company == self.company) + & (gl_entry.is_cancelled == 0) + & (gl_entry.account.isin(accounts)) + & (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)) + ) + + if for_aggregation: + query = query.where(gl_entry.voucher_type != "Period Closing Voucher") + + for dimension in qb_dimension_fields: + query = query.groupby(gl_entry[dimension]) + + return query.run(as_dict=1) + def process_closing_entries(closing_entries): from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries From f92c63fb10c464722f264ecf96129ea7bb2aeac4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 26 Feb 2023 15:53:33 +0530 Subject: [PATCH 07/50] feat: Add validations against period closing voucher --- erpnext/accounts/general_ledger.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 41fdb6a97f8c..9fff6f4b5377 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -300,6 +300,9 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): if gl_map: check_freezing_date(gl_map[0]["posting_date"], adv_adj) + is_opening = any(d.get("is_opening") == "Yes" for d in gl_map) + if gl_map[0]["voucher_type"] != "Period Closing Voucher": + validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"]) for entry in gl_map: make_entry(entry, adv_adj, update_outstanding, from_repost) @@ -519,6 +522,9 @@ def make_reverse_gl_entries( ) validate_accounting_period(gl_entries) check_freezing_date(gl_entries[0]["posting_date"], adv_adj) + + is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries) + validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"]) set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]) for entry in gl_entries: @@ -566,6 +572,28 @@ def check_freezing_date(posting_date, adv_adj=False): ) +def validate_against_pcv(is_opening, posting_date, company): + if is_opening and frappe.db.exists( + "Period Closing Voucher", {"docstatus": 1, "company": company} + ): + frappe.throw( + _("Opening Entry can not be created after Period Closing Voucher is created."), + title=_("Invalid Opening Entry"), + ) + + last_pcv_date = frappe.db.get_value( + "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)" + ) + + if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date): + message = _("Books have been closed till the period ending on {0}").format( + formatdate(last_pcv_date) + ) + message += "
" + message += _("You cannot create any new accounting entries till this date.") + frappe.throw(message, title=_("Period Closed")) + + def set_as_cancel(voucher_type, voucher_no): """ Set is_cancelled=1 in all original gl entries for the voucher From 6607c8bd82a70965d9c2f63bcc08218bf6dd3573 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 26 Feb 2023 16:05:26 +0530 Subject: [PATCH 08/50] fix: Order by issue in aggregation query --- erpnext/accounts/doctype/closing_balance/closing_balance.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.py b/erpnext/accounts/doctype/closing_balance/closing_balance.py index 572553d8080f..d49459d004c8 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.py +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.py @@ -1,6 +1,8 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +from typing import List + import frappe from frappe.model.document import Document @@ -10,7 +12,7 @@ class ClosingBalance(Document): - def aggregate_with_last_closing_balance(self, accounting_dimensions): + def aggregate_with_last_closing_balance(self, accounting_dimensions: List[str]): closing_balance = frappe.qb.DocType("Closing Balance") query = ( @@ -24,7 +26,7 @@ def aggregate_with_last_closing_balance(self, accounting_dimensions): for dimension in accounting_dimensions: query = query.where(closing_balance[dimension] == self.get(dimension)) - query.orderby(closing_balance.closing_date, order=frappe.qb.desc).limit(1) + query = query.orderby(closing_balance.closing_date, order=frappe.qb.desc).limit(1) last_closing_balance = query.run(as_dict=1) From a663df376cfb37a902a2b6005daf9af849a2eeb1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 26 Feb 2023 16:06:04 +0530 Subject: [PATCH 09/50] fix: Add patch to update closing balances --- .../patches/v14_0/update_closing_balances.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index cd120b553cce..47d26257bed2 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -2,8 +2,25 @@ # License: MIT. See LICENSE -# import frappe +import frappe + +from erpnext.accounts.utils import get_fiscal_year def execute(): - pass + company_wise_order = {} + for pcv in frappe.db.get_all( + "Period Closing Voucher", + fields=["company", "posting_date", "name"], + filters={"docstatus": 1}, + order_by="posting_date", + ): + + company_wise_order.setdefault(pcv.company, []) + if pcv.posting_date not in company_wise_order[pcv.company]: + pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) + pcv_doc.year_start_date = get_fiscal_year( + pcv.posting_date, pcv.fiscal_year, company=pcv.company + )[1] + pcv_doc.make_closing_entries() + company_wise_order[pcv.company].append(pcv.posting_date) From 436fc03edae6675eba126806b08d9667168be8e1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 1 Mar 2023 14:43:49 +0530 Subject: [PATCH 10/50] fix: Closing balance entries for period closing voucher --- .../closing_balance/closing_balance.json | 31 ++++++------------- .../closing_balance/closing_balance.py | 14 ++++++++- .../period_closing_voucher.py | 8 +++-- .../test_period_closing_voucher.py | 3 ++ 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.json b/erpnext/accounts/doctype/closing_balance/closing_balance.json index 377121f5eea9..37d26b857889 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.json +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.json @@ -15,11 +15,10 @@ "debit_in_account_currency", "credit_in_account_currency", "project", - "is_opening", - "fiscal_year", "company", "finance_book", - "period_closing_voucher" + "period_closing_voucher", + "is_period_closing_voucher_entry" ], "fields": [ { @@ -94,24 +93,6 @@ "label": "Project", "options": "Project" }, - { - "fieldname": "is_opening", - "fieldtype": "Select", - "in_filter": 1, - "label": "Is Opening", - "oldfieldname": "is_opening", - "oldfieldtype": "Select", - "options": "No\nYes" - }, - { - "fieldname": "fiscal_year", - "fieldtype": "Link", - "in_filter": 1, - "label": "Fiscal Year", - "oldfieldname": "fiscal_year", - "oldfieldtype": "Select", - "options": "Fiscal Year" - }, { "fieldname": "company", "fieldtype": "Link", @@ -136,12 +117,18 @@ "in_standard_filter": 1, "label": "Period Closing Voucher", "options": "Period Closing Voucher" + }, + { + "default": "0", + "fieldname": "is_period_closing_voucher_entry", + "fieldtype": "Check", + "label": "Is Period Closing Voucher Entry" } ], "icon": "fa fa-list", "in_create": 1, "links": [], - "modified": "2023-02-24 18:11:11.612395", + "modified": "2023-02-27 19:47:36.658224", "modified_by": "Administrator", "module": "Accounts", "name": "Closing Balance", diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.py b/erpnext/accounts/doctype/closing_balance/closing_balance.py index d49459d004c8..7dccacaa3259 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.py +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.py @@ -35,10 +35,22 @@ def aggregate_with_last_closing_balance(self, accounting_dimensions: List[str]): self.credit += last_closing_balance[0].credit -def make_closing_entries(closing_entries): +def make_closing_entries( + closing_entries, is_period_closing_voucher_entry=False, voucher_name=None +): accounting_dimensions = get_accounting_dimensions() for entry in closing_entries: cle = frappe.new_doc("Closing Balance") cle.update(entry) + + if is_period_closing_voucher_entry: + cle.update( + { + "closing_date": entry.get("posting_date"), + "is_period_closing_voucher_entry": 1, + "period_closing_voucher": voucher_name, + } + ) + cle.aggregate_with_last_closing_balance(accounting_dimensions) cle.submit() diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 5d118bc8fbae..57e22a1f9acb 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -92,13 +92,13 @@ def make_gl_entries(self): gl_entries = self.get_gl_entries() if gl_entries: if len(gl_entries) > 5000: - frappe.enqueue(process_gl_entries, gl_entries=gl_entries, queue="long") + frappe.enqueue(process_gl_entries, gl_entries=gl_entries, voucher_name=self.name, queue="long") frappe.msgprint( _("The GL Entries will be processed in the background, it can take a few minutes."), alert=True, ) else: - process_gl_entries(gl_entries) + process_gl_entries(gl_entries, voucher_name=self.name) def make_closing_entries(self): closing_entries = self.get_grouped_gl_entries() @@ -285,11 +285,13 @@ def process_closing_entries(closing_entries): frappe.log_error(e) -def process_gl_entries(gl_entries): +def process_gl_entries(gl_entries, voucher_name=None): + from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries from erpnext.accounts.general_ledger import make_gl_entries try: make_gl_entries(gl_entries, merge_entries=False) + make_closing_entries(gl_entries, is_period_closing_voucher_entry=True, voucher_name=voucher_name) frappe.db.set_value( "Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed" ) diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index e9ed2e469408..cc12a55d0485 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -16,6 +16,7 @@ class TestPeriodClosingVoucher(unittest.TestCase): def test_closing_entry(self): frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'") company = create_company() cost_center = create_cost_center("Test Cost Center 1") @@ -65,6 +66,7 @@ def test_closing_entry(self): def test_cost_center_wise_posting(self): frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'") company = create_company() surplus_account = create_account() @@ -128,6 +130,7 @@ def test_cost_center_wise_posting(self): def test_period_closing_with_finance_book_entries(self): frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'") company = create_company() surplus_account = create_account() From 95c9aafda9ebbfa809257989d717638b44723589 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 1 Mar 2023 14:45:22 +0530 Subject: [PATCH 11/50] fix: Update patch to generate closing balance entries --- erpnext/patches.txt | 2 +- erpnext/patches/v14_0/update_closing_balances.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d0873e8be1f3..52b1b05f029a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -328,4 +328,4 @@ erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers erpnext.patches.v14_0.set_pick_list_status erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -#erpnext.patches.v14_0.update_closing_balances +erpnext.patches.v14_0.update_closing_balances diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 47d26257bed2..6a1844f79602 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -4,6 +4,7 @@ import frappe +from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries from erpnext.accounts.utils import get_fiscal_year @@ -23,4 +24,6 @@ def execute(): pcv.posting_date, pcv.fiscal_year, company=pcv.company )[1] pcv_doc.make_closing_entries() + gl_entries = pcv_doc.get_gl_entries() + make_closing_entries(gl_entries, is_period_closing_voucher_entry=True) company_wise_order[pcv.company].append(pcv.posting_date) From e5f603c9d9d4cb2668cccb3f3c7e287be79c232e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 1 Mar 2023 14:48:06 +0530 Subject: [PATCH 12/50] perf: Apply closing balance in Trial Balance report --- .../report/trial_balance/trial_balance.py | 132 +++++++++++------- 1 file changed, 79 insertions(+), 53 deletions(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 3af01fde7d03..96cb38ba2ab6 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -4,7 +4,8 @@ import frappe from frappe import _ -from frappe.utils import cstr, flt, formatdate, getdate +from frappe.query_builder.functions import Sum +from frappe.utils import add_days, cstr, flt, formatdate, getdate import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -140,45 +141,88 @@ def get_opening_balances(filters): def get_rootwise_opening_balances(filters, report_type): - additional_conditions = "" - if not filters.show_unclosed_fy_pl_balances: - additional_conditions = ( - " and posting_date >= %(year_start_date)s" if report_type == "Profit and Loss" else "" + gle = [] + accounting_dimensions = get_accounting_dimensions(as_list=False) + gle = get_opening_balance("Closing Balance", filters, report_type, accounting_dimensions) + + last_period_closing_voucher_date = frappe.db.get_value( + "Period Closing Voucher", {"docstatus": 1, "company": filters.company}, "max(posting_date)" + ) + + # Check If need to get opening balance from GL Entry + if getdate(last_period_closing_voucher_date) < getdate(add_days(filters.from_date, -1)): + filters.from_date = add_days(last_period_closing_voucher_date, 1) + gle = get_opening_balance("GL Entry", filters, report_type, accounting_dimensions) + + opening = frappe._dict() + for d in gle: + opening.setdefault(d.account, d) + + return opening + + +def get_opening_balance(doctype, filters, report_type, accounting_dimensions): + closing_balance = frappe.qb.DocType(doctype) + account = frappe.qb.DocType("Account") + + opening_balance = ( + frappe.qb.from_(closing_balance) + .select( + closing_balance.account, + Sum(closing_balance.debit).as_("opening_debit"), + Sum(closing_balance.credit).as_("opening_credit"), ) + .where( + (closing_balance.company == filters.company) + & ( + closing_balance.account.isin( + frappe.qb.from_("account").select("name").where(account.report_type == report_type) + ) + ) + ) + .groupby(closing_balance.account) + ) + + if doctype == "Closing Balance": + opening_balance = opening_balance.where(closing_balance.closing_date < filters.from_date) + else: + opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date) + + if not filters.show_unclosed_fy_pl_balances and report_type == "Profit and Loss": + opening_balance = opening_balance.where(closing_balance.closing_date >= filters.year_start_date) if not flt(filters.with_period_closing_entry): - additional_conditions += " and ifnull(voucher_type, '')!='Period Closing Voucher'" + if doctype == "Closing Balance": + opening_balance = opening_balance.where(closing_balance.is_period_closing_voucher_entry == 0) + else: + opening_balance = opening_balance.where( + closing_balance.voucher_type != "Period Closing Voucher" + ) if filters.cost_center: lft, rgt = frappe.db.get_value("Cost Center", filters.cost_center, ["lft", "rgt"]) - additional_conditions += """ and cost_center in (select name from `tabCost Center` - where lft >= %s and rgt <= %s)""" % ( - lft, - rgt, + cost_center = frappe.qb.DocType("Cost Center") + opening_balance = opening_balance.where( + closing_balance.cost_center.in_( + frappe.qb.from_(cost_center) + .select("name") + .where((cost_center.lft >= lft) & (cost_center.rgt <= rgt)) + ) ) if filters.project: - additional_conditions += " and project = %(project)s" + opening_balance = opening_balance.where(closing_balance.project == filters.project) if filters.get("include_default_book_entries"): - additional_conditions += ( - " AND (finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)" + opening_balance = opening_balance.where( + (closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(filters.company_fb), ""])) + | (closing_balance.finance_book.isnull()) ) else: - additional_conditions += " AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)" - - accounting_dimensions = get_accounting_dimensions(as_list=False) - - query_filters = { - "company": filters.company, - "from_date": filters.from_date, - "to_date": filters.to_date, - "report_type": report_type, - "year_start_date": filters.year_start_date, - "project": filters.project, - "finance_book": filters.finance_book, - "company_fb": frappe.get_cached_value("Company", filters.company, "default_finance_book"), - } + opening_balance = opening_balance.where( + (closing_balance.finance_book.isin([cstr(filters.finance_book), ""])) + | (closing_balance.finance_book.isnull()) + ) if accounting_dimensions: for dimension in accounting_dimensions: @@ -187,35 +231,17 @@ def get_rootwise_opening_balances(filters, report_type): filters[dimension.fieldname] = get_dimension_with_children( dimension.document_type, filters.get(dimension.fieldname) ) - additional_conditions += " and {0} in %({0})s".format(dimension.fieldname) + opening_balance = opening_balance.where( + closing_balance[dimension.fieldname].isin(filters[dimension.fieldname]) + ) else: - additional_conditions += " and {0} in %({0})s".format(dimension.fieldname) - - query_filters.update({dimension.fieldname: filters.get(dimension.fieldname)}) - - gle = frappe.db.sql( - """ - select - account, sum(debit) as opening_debit, sum(credit) as opening_credit - from `tabGL Entry` - where - company=%(company)s - {additional_conditions} - and (posting_date < %(from_date)s or (ifnull(is_opening, 'No') = 'Yes' and posting_date <= %(to_date)s)) - and account in (select name from `tabAccount` where report_type=%(report_type)s) - and is_cancelled = 0 - group by account""".format( - additional_conditions=additional_conditions - ), - query_filters, - as_dict=True, - ) + opening_balance = opening_balance.where( + closing_balance[dimension.fieldname].isin(filters[dimension.fieldname]) + ) - opening = frappe._dict() - for d in gle: - opening.setdefault(d.account, d) + gle = opening_balance.run(as_dict=1) - return opening + return gle def calculate_values(accounts, gl_entries_by_account, opening_balances, filters, company_currency): From c089c4156c9f8613c9acf1e1a30b8fdeea9379c8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Mar 2023 16:46:56 +0530 Subject: [PATCH 13/50] chore: Minor fixes --- .../report/trial_balance/trial_balance.py | 37 +++++++++++++++---- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 2 +- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 96cb38ba2ab6..9d6f68d19248 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -142,16 +142,31 @@ def get_opening_balances(filters): def get_rootwise_opening_balances(filters, report_type): gle = [] - accounting_dimensions = get_accounting_dimensions(as_list=False) - gle = get_opening_balance("Closing Balance", filters, report_type, accounting_dimensions) - last_period_closing_voucher_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1, "company": filters.company}, "max(posting_date)" + last_period_closing_voucher = frappe.db.get_all( + "Period Closing Voucher", + filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", filters.from_date)}, + fields=["posting_date", "name"], + order_by="posting_date desc", + limit=1, ) - # Check If need to get opening balance from GL Entry - if getdate(last_period_closing_voucher_date) < getdate(add_days(filters.from_date, -1)): - filters.from_date = add_days(last_period_closing_voucher_date, 1) + accounting_dimensions = get_accounting_dimensions(as_list=False) + + if last_period_closing_voucher: + gle = get_opening_balance( + "Closing Balance", + filters, + report_type, + accounting_dimensions, + period_closing_voucher=last_period_closing_voucher[0].name, + ) + if getdate(last_period_closing_voucher[0].posting_date) < getdate( + add_days(filters.from_date, -1) + ): + filters.from_date = add_days(last_period_closing_voucher, 1) + gle = get_opening_balance("GL Entry", filters, report_type, accounting_dimensions) + else: gle = get_opening_balance("GL Entry", filters, report_type, accounting_dimensions) opening = frappe._dict() @@ -161,7 +176,9 @@ def get_rootwise_opening_balances(filters, report_type): return opening -def get_opening_balance(doctype, filters, report_type, accounting_dimensions): +def get_opening_balance( + doctype, filters, report_type, accounting_dimensions, period_closing_voucher=None +): closing_balance = frappe.qb.DocType(doctype) account = frappe.qb.DocType("Account") @@ -185,6 +202,10 @@ def get_opening_balance(doctype, filters, report_type, accounting_dimensions): if doctype == "Closing Balance": opening_balance = opening_balance.where(closing_balance.closing_date < filters.from_date) + if period_closing_voucher: + opening_balance = opening_balance.where( + closing_balance.period_closing_voucher == period_closing_voucher + ) else: opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 52b1b05f029a..ca1ec08bd173 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -328,4 +328,4 @@ erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers erpnext.patches.v14_0.set_pick_list_status erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances +erpnext.patches.v14_0.update_closing_balances #13 diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 6a1844f79602..6a9334b15038 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -25,5 +25,5 @@ def execute(): )[1] pcv_doc.make_closing_entries() gl_entries = pcv_doc.get_gl_entries() - make_closing_entries(gl_entries, is_period_closing_voucher_entry=True) + make_closing_entries(gl_entries, is_period_closing_voucher_entry=True, voucher_name=pcv.name) company_wise_order[pcv.company].append(pcv.posting_date) From 4a2046dfb67d407f5c6b66b2ad28f8c1117add63 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 5 Mar 2023 18:18:33 +0530 Subject: [PATCH 14/50] fix: Aggregation with previous closing balance --- .../closing_balance/closing_balance.py | 138 +++++++++++++----- .../period_closing_voucher.py | 68 +++++---- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 6 +- 4 files changed, 150 insertions(+), 64 deletions(-) diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.py b/erpnext/accounts/doctype/closing_balance/closing_balance.py index 7dccacaa3259..f4cbab1d9278 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.py +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.py @@ -1,8 +1,6 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from typing import List - import frappe from frappe.model.document import Document @@ -12,45 +10,113 @@ class ClosingBalance(Document): - def aggregate_with_last_closing_balance(self, accounting_dimensions: List[str]): - closing_balance = frappe.qb.DocType("Closing Balance") + pass + + +def make_closing_entries(closing_entries, voucher_name): + accounting_dimensions = get_accounting_dimensions() + company = closing_entries[0].get("company") + closing_date = closing_entries[0].get("closing_date") + + previous_closing_entries = get_previous_closing_entries( + company, closing_date, accounting_dimensions + ) + combined_entries = closing_entries + previous_closing_entries + + merged_entries = aggregate_with_last_closing_balance(combined_entries, accounting_dimensions) - query = ( - frappe.qb.from_(closing_balance) - .select(closing_balance.debit, closing_balance.credit) - .where( - closing_balance.closing_date < self.closing_date, - ) + for key, value in merged_entries.items(): + cle = frappe.new_doc("Closing Balance") + cle.update(value) + cle.update(value["dimensions"]) + cle.update( + { + "period_closing_voucher": voucher_name, + "closing_date": closing_date, + } ) + cle.submit() - for dimension in accounting_dimensions: - query = query.where(closing_balance[dimension] == self.get(dimension)) - query = query.orderby(closing_balance.closing_date, order=frappe.qb.desc).limit(1) +def aggregate_with_last_closing_balance(entries, accounting_dimensions): + merged_entries = {} + for entry in entries: + key, key_values = generate_key(entry, accounting_dimensions) + merged_entries.setdefault( + key, + { + "debit": 0, + "credit": 0, + "debit_in_account_currency": 0, + "credit_in_account_currency": 0, + }, + ) - last_closing_balance = query.run(as_dict=1) + merged_entries[key]["dimensions"] = key_values + merged_entries[key]["debit"] += entry.get("debit") + merged_entries[key]["credit"] += entry.get("credit") + merged_entries[key]["debit_in_account_currency"] += entry.get("debit_in_account_currency") + merged_entries[key]["credit_in_account_currency"] += entry.get("credit_in_account_currency") - if last_closing_balance: - self.debit += last_closing_balance[0].debit - self.credit += last_closing_balance[0].credit + return merged_entries -def make_closing_entries( - closing_entries, is_period_closing_voucher_entry=False, voucher_name=None -): - accounting_dimensions = get_accounting_dimensions() - for entry in closing_entries: - cle = frappe.new_doc("Closing Balance") - cle.update(entry) - - if is_period_closing_voucher_entry: - cle.update( - { - "closing_date": entry.get("posting_date"), - "is_period_closing_voucher_entry": 1, - "period_closing_voucher": voucher_name, - } - ) - - cle.aggregate_with_last_closing_balance(accounting_dimensions) - cle.submit() +def generate_key(entry, accounting_dimensions): + key = [ + entry.get("account"), + entry.get("account_currency"), + entry.get("cost_center"), + entry.get("project"), + entry.get("finance_book"), + entry.get("is_period_closing_voucher_entry"), + ] + + key_values = { + "account": entry.get("account"), + "account_currency": entry.get("account_currency"), + "cost_center": entry.get("cost_center"), + "project": entry.get("project"), + "finance_book": entry.get("finance_book"), + "is_period_closing_voucher_entry": entry.get("is_period_closing_voucher_entry"), + } + for dimension in accounting_dimensions: + key.append(entry.get(dimension)) + key_values[dimension] = entry.get(dimension) + + return tuple(key), key_values + + +def get_previous_closing_entries(company, closing_date, accounting_dimensions): + entries = [] + last_period_closing_voucher = frappe.db.get_all( + "Period Closing Voucher", + filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)}, + fields=["name"], + order_by="posting_date desc", + limit=1, + ) + + if last_period_closing_voucher: + closing_balance = frappe.qb.DocType("Closing Balance") + query = frappe.qb.from_(closing_balance).select( + closing_balance.account, + closing_balance.account_currency, + closing_balance.debit, + closing_balance.credit, + closing_balance.debit_in_account_currency, + closing_balance.credit_in_account_currency, + closing_balance.cost_center, + closing_balance.project, + closing_balance.finance_book, + closing_balance.is_period_closing_voucher_entry, + ) + + for dimension in accounting_dimensions: + query = query.select(closing_balance[dimension]) + + query = query.where( + closing_balance.period_closing_voucher == last_period_closing_voucher[0].name + ) + entries = query.run(as_dict=1) + + return entries diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 57e22a1f9acb..9b1378f15631 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -21,8 +21,14 @@ def validate(self): def on_submit(self): self.db_set("gle_processing_status", "In Progress") - self.make_gl_entries() - self.make_closing_entries() + get_opening_entries = False + + if not frappe.db.exists( + "Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)} + ): + get_opening_entries = True + + self.make_gl_entries(get_opening_entries=get_opening_entries) def on_cancel(self): self.db_set("gle_processing_status", "In Progress") @@ -88,34 +94,30 @@ def validate_posting_date(self): ) ) - def make_gl_entries(self): + def make_gl_entries(self, get_opening_entries=False): gl_entries = self.get_gl_entries() + closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries) if gl_entries: if len(gl_entries) > 5000: - frappe.enqueue(process_gl_entries, gl_entries=gl_entries, voucher_name=self.name, queue="long") - frappe.msgprint( - _("The GL Entries will be processed in the background, it can take a few minutes."), - alert=True, + frappe.enqueue( + process_gl_entries, + gl_entries=gl_entries, + closing_entries=closing_entries, + voucher_name=self.name, + queue="long", ) - else: - process_gl_entries(gl_entries, voucher_name=self.name) - - def make_closing_entries(self): - closing_entries = self.get_grouped_gl_entries() - - if closing_entries: - if len(closing_entries) > 5000: - frappe.enqueue(process_closing_entries, gl_entries=closing_entries, queue="long") frappe.msgprint( - _("The Opening Entries will be processed in the background, it can take a few minutes."), + _("The GL Entries will be processed in the background, it can take a few minutes."), alert=True, ) else: - process_closing_entries(closing_entries) + process_gl_entries(gl_entries, closing_entries, voucher_name=self.name) - def get_grouped_gl_entries(self): + def get_grouped_gl_entries(self, get_opening_entries=False): closing_entries = [] - for acc in self.get_balances_based_on_dimensions(group_by_account=True, for_aggregation=True): + for acc in self.get_balances_based_on_dimensions( + group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries + ): closing_entries.append(self.get_closing_entries(acc)) return closing_entries @@ -142,6 +144,7 @@ def get_gl_entries(self): def get_gle_for_pl_account(self, acc): gl_entry = self.get_gl_dict( { + "closing_date": self.posting_date, "account": acc.account, "cost_center": acc.cost_center, "finance_book": acc.finance_book, @@ -154,6 +157,7 @@ def get_gle_for_pl_account(self, acc): if flt(acc.bal_in_account_currency) > 0 else 0, "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, + "is_period_closing_voucher_entry": 1, }, item=acc, ) @@ -163,6 +167,7 @@ def get_gle_for_pl_account(self, acc): def get_gle_for_closing_account(self, acc): gl_entry = self.get_gl_dict( { + "closing_date": self.posting_date, "account": self.closing_account_head, "cost_center": acc.cost_center, "finance_book": acc.finance_book, @@ -175,6 +180,7 @@ def get_gle_for_closing_account(self, acc): if flt(acc.bal_in_account_currency) < 0 else 0, "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, + "is_period_closing_voucher_entry": 1, }, item=acc, ) @@ -211,11 +217,11 @@ def update_default_dimensions(self, gl_entry, acc): gl_entry.update({dimension: acc.get(dimension)}) def get_balances_based_on_dimensions( - self, group_by_account=False, report_type=None, for_aggregation=False + self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False ): """Get balance for dimension-wise pl accounts""" - qb_dimension_fields = ["cost_center", "finance_book"] + qb_dimension_fields = ["cost_center", "finance_book", "project"] self.accounting_dimensions = get_accounting_dimensions() for dimension in self.accounting_dimensions: @@ -263,9 +269,21 @@ def get_balances_based_on_dimensions( (gl_entry.company == self.company) & (gl_entry.is_cancelled == 0) & (gl_entry.account.isin(accounts)) - & (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)) ) + if get_opening_entries: + query = query.where( + gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date) + | gl_entry.is_opening + == "Yes" + ) + else: + query = query.where( + gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date) + & gl_entry.is_opening + == "No" + ) + if for_aggregation: query = query.where(gl_entry.voucher_type != "Period Closing Voucher") @@ -285,13 +303,13 @@ def process_closing_entries(closing_entries): frappe.log_error(e) -def process_gl_entries(gl_entries, voucher_name=None): +def process_gl_entries(gl_entries, closing_entries, voucher_name=None): from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries from erpnext.accounts.general_ledger import make_gl_entries try: make_gl_entries(gl_entries, merge_entries=False) - make_closing_entries(gl_entries, is_period_closing_voucher_entry=True, voucher_name=voucher_name) + make_closing_entries(gl_entries + closing_entries, voucher_name=voucher_name) frappe.db.set_value( "Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed" ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ca1ec08bd173..52b1b05f029a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -328,4 +328,4 @@ erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers erpnext.patches.v14_0.set_pick_list_status erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances #13 +erpnext.patches.v14_0.update_closing_balances diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 6a9334b15038..0442b362ee5e 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -10,6 +10,7 @@ def execute(): company_wise_order = {} + get_opening_entries = True for pcv in frappe.db.get_all( "Period Closing Voucher", fields=["company", "posting_date", "name"], @@ -23,7 +24,8 @@ def execute(): pcv_doc.year_start_date = get_fiscal_year( pcv.posting_date, pcv.fiscal_year, company=pcv.company )[1] - pcv_doc.make_closing_entries() gl_entries = pcv_doc.get_gl_entries() - make_closing_entries(gl_entries, is_period_closing_voucher_entry=True, voucher_name=pcv.name) + closing_entries = pcv_doc.get_grouped_gl_entries(get_opening_entries=get_opening_entries) + make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name) company_wise_order[pcv.company].append(pcv.posting_date) + get_opening_entries = False From 310f71c31325cc8295ed5a434dac2a73545aea39 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 5 Mar 2023 18:43:26 +0530 Subject: [PATCH 15/50] test: Add static posting dates to tests --- .../test_period_closing_voucher.py | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index cc12a55d0485..cfe7fc1f51b5 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -22,11 +22,11 @@ def test_closing_entry(self): cost_center = create_cost_center("Test Cost Center 1") jv1 = make_journal_entry( + posting_date="2021-03-15", amount=400, account1="Cash - TPC", account2="Sales - TPC", cost_center=cost_center, - posting_date=now(), save=False, ) jv1.company = company @@ -34,18 +34,18 @@ def test_closing_entry(self): jv1.submit() jv2 = make_journal_entry( + posting_date="2021-03-15", amount=600, account1="Cost of Goods Sold - TPC", account2="Cash - TPC", cost_center=cost_center, - posting_date=now(), save=False, ) jv2.company = company jv2.save() jv2.submit() - pcv = self.make_period_closing_voucher() + pcv = self.make_period_closing_voucher(posting_date="2021-03-31") surplus_account = pcv.closing_account_head expected_gle = ( @@ -83,6 +83,7 @@ def test_cost_center_wise_posting(self): debit_to="Debtors - TPC", currency="USD", customer="_Test Customer USD", + posting_date="2021-03-15", ) create_sales_invoice( company=company, @@ -93,9 +94,10 @@ def test_cost_center_wise_posting(self): debit_to="Debtors - TPC", currency="USD", customer="_Test Customer USD", + posting_date="2021-03-15", ) - pcv = self.make_period_closing_voucher(submit=False) + pcv = self.make_period_closing_voucher(posting_date="2021-03-31", submit=False) pcv.save() pcv.submit() surplus_account = pcv.closing_account_head @@ -136,7 +138,7 @@ def test_period_closing_with_finance_book_entries(self): surplus_account = create_account() cost_center = create_cost_center("Test Cost Center 1") - si = create_sales_invoice( + create_sales_invoice( company=company, income_account="Sales - TPC", expense_account="Cost of Goods Sold - TPC", @@ -145,6 +147,7 @@ def test_period_closing_with_finance_book_entries(self): debit_to="Debtors - TPC", currency="USD", customer="_Test Customer USD", + posting_date="2021-03-15", ) jv = make_journal_entry( @@ -152,14 +155,14 @@ def test_period_closing_with_finance_book_entries(self): account2="Sales - TPC", amount=400, cost_center=cost_center, - posting_date=now(), + posting_date="2021-03-15", ) jv.company = company jv.finance_book = create_finance_book().name jv.save() jv.submit() - pcv = self.make_period_closing_voucher() + pcv = self.make_period_closing_voucher(posting_date="2021-03-31") surplus_account = pcv.closing_account_head expected_gle = ( @@ -180,14 +183,38 @@ def test_period_closing_with_finance_book_entries(self): self.assertSequenceEqual(pcv_gle, expected_gle) - def make_period_closing_voucher(self, submit=True): + def test_gl_entries_restrictions(self): + frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'") + + company = create_company() + cost_center = create_cost_center("Test Cost Center 1") + + self.make_period_closing_voucher(posting_date="2021-03-31") + + jv1 = make_journal_entry( + posting_date="2021-03-15", + amount=400, + account1="Cash - TPC", + account2="Sales - TPC", + cost_center=cost_center, + save=False, + ) + jv1.company = company + jv1.save() + + self.assertRaises(frappe.ValidationError, jv1.submit) + + # def test_closing_balance_with_dimensions(self): + + def make_period_closing_voucher(self, posting_date=None, submit=True): surplus_account = create_account() cost_center = create_cost_center("Test Cost Center 1") pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", - "transaction_date": today(), - "posting_date": today(), + "transaction_date": posting_date or today(), + "posting_date": posting_date or today(), "company": "Test PCV Company", "fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0], "cost_center": cost_center, From 5dabc98ba532c9b0d3b6f27ad890d005638d9142 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 6 Mar 2023 16:56:33 +0530 Subject: [PATCH 16/50] chore: Add index to period closing voucher column --- .../accounts/doctype/closing_balance/closing_balance.json | 5 +++-- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.json b/erpnext/accounts/doctype/closing_balance/closing_balance.json index 37d26b857889..ea6da145b7e1 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.json +++ b/erpnext/accounts/doctype/closing_balance/closing_balance.json @@ -116,7 +116,8 @@ "fieldtype": "Link", "in_standard_filter": 1, "label": "Period Closing Voucher", - "options": "Period Closing Voucher" + "options": "Period Closing Voucher", + "search_index": 1 }, { "default": "0", @@ -128,7 +129,7 @@ "icon": "fa fa-list", "in_create": 1, "links": [], - "modified": "2023-02-27 19:47:36.658224", + "modified": "2023-03-06 08:56:36.393237", "modified_by": "Administrator", "module": "Accounts", "name": "Closing Balance", diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 9d6f68d19248..db6b9dc96407 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -201,11 +201,12 @@ def get_opening_balance( ) if doctype == "Closing Balance": - opening_balance = opening_balance.where(closing_balance.closing_date < filters.from_date) if period_closing_voucher: opening_balance = opening_balance.where( closing_balance.period_closing_voucher == period_closing_voucher ) + else: + opening_balance = opening_balance.where(closing_balance.closing_date < filters.from_date) else: opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date) From 3249a79f0784ed4b92da9713cbdb1cefa5597a49 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 8 Mar 2023 15:15:33 +0530 Subject: [PATCH 17/50] chore: rename Closing Balance to Account Closing Balance --- .../__init__.py | 0 .../account_closing_balance.js} | 2 +- .../account_closing_balance.json} | 2 +- .../account_closing_balance.py} | 38 ++++++++++--------- .../test_account_closing_balance.py} | 2 +- .../period_closing_voucher.py | 2 +- .../report/trial_balance/trial_balance.py | 6 +-- ...counting_dimensions_for_closing_balance.py | 2 +- 8 files changed, 28 insertions(+), 26 deletions(-) rename erpnext/accounts/doctype/{closing_balance => account_closing_balance}/__init__.py (100%) rename erpnext/accounts/doctype/{closing_balance/closing_balance.js => account_closing_balance/account_closing_balance.js} (75%) rename erpnext/accounts/doctype/{closing_balance/closing_balance.json => account_closing_balance/account_closing_balance.json} (98%) rename erpnext/accounts/doctype/{closing_balance/closing_balance.py => account_closing_balance/account_closing_balance.py} (73%) rename erpnext/accounts/doctype/{closing_balance/test_closing_balance.py => account_closing_balance/test_account_closing_balance.py} (76%) diff --git a/erpnext/accounts/doctype/closing_balance/__init__.py b/erpnext/accounts/doctype/account_closing_balance/__init__.py similarity index 100% rename from erpnext/accounts/doctype/closing_balance/__init__.py rename to erpnext/accounts/doctype/account_closing_balance/__init__.py diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.js b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.js similarity index 75% rename from erpnext/accounts/doctype/closing_balance/closing_balance.js rename to erpnext/accounts/doctype/account_closing_balance/account_closing_balance.js index 1a43e0664da7..e35591474b18 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.js +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.js @@ -1,7 +1,7 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -// frappe.ui.form.on("Closing Balance", { +// frappe.ui.form.on("Account Closing Balance", { // refresh(frm) { // }, diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.json b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.json similarity index 98% rename from erpnext/accounts/doctype/closing_balance/closing_balance.json rename to erpnext/accounts/doctype/account_closing_balance/account_closing_balance.json index ea6da145b7e1..8dacb96197a6 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.json +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.json @@ -132,7 +132,7 @@ "modified": "2023-03-06 08:56:36.393237", "modified_by": "Administrator", "module": "Accounts", - "name": "Closing Balance", + "name": "Account Closing Balance", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/accounts/doctype/closing_balance/closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py similarity index 73% rename from erpnext/accounts/doctype/closing_balance/closing_balance.py rename to erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py index f4cbab1d9278..9c1567eaf162 100644 --- a/erpnext/accounts/doctype/closing_balance/closing_balance.py +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py @@ -9,7 +9,7 @@ ) -class ClosingBalance(Document): +class AccountClosingBalance(Document): pass @@ -23,10 +23,12 @@ def make_closing_entries(closing_entries, voucher_name): ) combined_entries = closing_entries + previous_closing_entries - merged_entries = aggregate_with_last_closing_balance(combined_entries, accounting_dimensions) + merged_entries = aggregate_with_last_account_closing_balance( + combined_entries, accounting_dimensions + ) for key, value in merged_entries.items(): - cle = frappe.new_doc("Closing Balance") + cle = frappe.new_doc("Account Closing Balance") cle.update(value) cle.update(value["dimensions"]) cle.update( @@ -38,7 +40,7 @@ def make_closing_entries(closing_entries, voucher_name): cle.submit() -def aggregate_with_last_closing_balance(entries, accounting_dimensions): +def aggregate_with_last_account_closing_balance(entries, accounting_dimensions): merged_entries = {} for entry in entries: key, key_values = generate_key(entry, accounting_dimensions) @@ -97,25 +99,25 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions): ) if last_period_closing_voucher: - closing_balance = frappe.qb.DocType("Closing Balance") - query = frappe.qb.from_(closing_balance).select( - closing_balance.account, - closing_balance.account_currency, - closing_balance.debit, - closing_balance.credit, - closing_balance.debit_in_account_currency, - closing_balance.credit_in_account_currency, - closing_balance.cost_center, - closing_balance.project, - closing_balance.finance_book, - closing_balance.is_period_closing_voucher_entry, + account_closing_balance = frappe.qb.DocType("Account Closing Balance") + query = frappe.qb.from_(account_closing_balance).select( + account_closing_balance.account, + account_closing_balance.account_currency, + account_closing_balance.debit, + account_closing_balance.credit, + account_closing_balance.debit_in_account_currency, + account_closing_balance.credit_in_account_currency, + account_closing_balance.cost_center, + account_closing_balance.project, + account_closing_balance.finance_book, + account_closing_balance.is_period_closing_voucher_entry, ) for dimension in accounting_dimensions: - query = query.select(closing_balance[dimension]) + query = query.select(account_closing_balance[dimension]) query = query.where( - closing_balance.period_closing_voucher == last_period_closing_voucher[0].name + account_closing_balance.period_closing_voucher == last_period_closing_voucher[0].name ) entries = query.run(as_dict=1) diff --git a/erpnext/accounts/doctype/closing_balance/test_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/test_account_closing_balance.py similarity index 76% rename from erpnext/accounts/doctype/closing_balance/test_closing_balance.py rename to erpnext/accounts/doctype/account_closing_balance/test_account_closing_balance.py index 4e81316210e4..fc42677062e3 100644 --- a/erpnext/accounts/doctype/closing_balance/test_closing_balance.py +++ b/erpnext/accounts/doctype/account_closing_balance/test_account_closing_balance.py @@ -5,5 +5,5 @@ from frappe.tests.utils import FrappeTestCase -class TestClosingBalance(FrappeTestCase): +class TestAccountClosingBalance(FrappeTestCase): pass diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 9b1378f15631..f11513d36702 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -53,7 +53,7 @@ def on_cancel(self): self.delete_closing_entries() def delete_closing_entries(self): - closing_balance = frappe.qb.DocType("Closing Balance") + closing_balance = frappe.qb.DocType("Account Closing Balance") frappe.qb.from_(closing_balance).delete().where( closing_balance.period_closing_voucher == self.name ).run() diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index db6b9dc96407..0a9dadb2feb7 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -155,7 +155,7 @@ def get_rootwise_opening_balances(filters, report_type): if last_period_closing_voucher: gle = get_opening_balance( - "Closing Balance", + "Account Closing Balance", filters, report_type, accounting_dimensions, @@ -200,7 +200,7 @@ def get_opening_balance( .groupby(closing_balance.account) ) - if doctype == "Closing Balance": + if doctype == "Account Closing Balance": if period_closing_voucher: opening_balance = opening_balance.where( closing_balance.period_closing_voucher == period_closing_voucher @@ -214,7 +214,7 @@ def get_opening_balance( opening_balance = opening_balance.where(closing_balance.closing_date >= filters.year_start_date) if not flt(filters.with_period_closing_entry): - if doctype == "Closing Balance": + if doctype == "Account Closing Balance": opening_balance = opening_balance.where(closing_balance.is_period_closing_voucher_entry == 0) else: opening_balance = opening_balance.where( diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py b/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py index 375782171ca5..43ad0d7cfab2 100644 --- a/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py +++ b/erpnext/patches/v14_0/create_accounting_dimensions_for_closing_balance.py @@ -10,7 +10,7 @@ def execute(): if not accounting_dimensions: return - doctype = "Closing Balance" + doctype = "Account Closing Balance" for d in accounting_dimensions: field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) From f0267feca8383c5f5075ed265a761a0d2c769fc9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 8 Mar 2023 18:34:07 +0530 Subject: [PATCH 18/50] test: Add test case for closing balance --- .../account_closing_balance.py | 31 +++--- .../period_closing_voucher.py | 17 ++-- .../test_period_closing_voucher.py | 94 ++++++++++++++++++- 3 files changed, 116 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py index 9c1567eaf162..7c842372de81 100644 --- a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py @@ -3,6 +3,7 @@ import frappe from frappe.model.document import Document +from frappe.utils import cint, cstr from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, @@ -65,25 +66,26 @@ def aggregate_with_last_account_closing_balance(entries, accounting_dimensions): def generate_key(entry, accounting_dimensions): key = [ - entry.get("account"), - entry.get("account_currency"), - entry.get("cost_center"), - entry.get("project"), - entry.get("finance_book"), - entry.get("is_period_closing_voucher_entry"), + cstr(entry.get("account")), + cstr(entry.get("account_currency")), + cstr(entry.get("cost_center")), + cstr(entry.get("project")), + cstr(entry.get("finance_book")), + cint(entry.get("is_period_closing_voucher_entry")), ] key_values = { - "account": entry.get("account"), - "account_currency": entry.get("account_currency"), - "cost_center": entry.get("cost_center"), - "project": entry.get("project"), - "finance_book": entry.get("finance_book"), - "is_period_closing_voucher_entry": entry.get("is_period_closing_voucher_entry"), + "company": cstr(entry.get("company")), + "account": cstr(entry.get("account")), + "account_currency": cstr(entry.get("account_currency")), + "cost_center": cstr(entry.get("cost_center")), + "project": cstr(entry.get("project")), + "finance_book": cstr(entry.get("finance_book")), + "is_period_closing_voucher_entry": cint(entry.get("is_period_closing_voucher_entry")), } for dimension in accounting_dimensions: - key.append(entry.get(dimension)) - key_values[dimension] = entry.get(dimension) + key.append(cstr(entry.get(dimension))) + key_values[dimension] = cstr(entry.get(dimension)) return tuple(key), key_values @@ -101,6 +103,7 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions): if last_period_closing_voucher: account_closing_balance = frappe.qb.DocType("Account Closing Balance") query = frappe.qb.from_(account_closing_balance).select( + account_closing_balance.company, account_closing_balance.account, account_closing_balance.account_currency, account_closing_balance.debit, diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index f11513d36702..f6289e7e4791 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -144,6 +144,7 @@ def get_gl_entries(self): def get_gle_for_pl_account(self, acc): gl_entry = self.get_gl_dict( { + "company": self.company, "closing_date": self.posting_date, "account": acc.account, "cost_center": acc.cost_center, @@ -167,6 +168,7 @@ def get_gle_for_pl_account(self, acc): def get_gle_for_closing_account(self, acc): gl_entry = self.get_gl_dict( { + "company": self.company, "closing_date": self.posting_date, "account": self.closing_account_head, "cost_center": acc.cost_center, @@ -190,6 +192,7 @@ def get_gle_for_closing_account(self, acc): def get_closing_entries(self, acc): closing_entry = self.get_gl_dict( { + "company": self.company, "closing_date": self.posting_date, "period_closing_voucher": self.name, "account": acc.account, @@ -293,18 +296,10 @@ def get_balances_based_on_dimensions( return query.run(as_dict=1) -def process_closing_entries(closing_entries): - from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries - - try: - make_closing_entries(closing_entries) - except Exception as e: - frappe.db.rollback() - frappe.log_error(e) - - def process_gl_entries(gl_entries, closing_entries, voucher_name=None): - from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries + from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( + make_closing_entries, + ) from erpnext.accounts.general_ledger import make_gl_entries try: diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index cfe7fc1f51b5..62ae8572e4e7 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -205,7 +205,99 @@ def test_gl_entries_restrictions(self): self.assertRaises(frappe.ValidationError, jv1.submit) - # def test_closing_balance_with_dimensions(self): + def test_closing_balance_with_dimensions(self): + frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'") + frappe.db.sql("delete from `tabAccount Closing Balance` where company='Test PCV Company'") + + company = create_company() + cost_center1 = create_cost_center("Test Cost Center 1") + cost_center2 = create_cost_center("Test Cost Center 2") + + jv1 = make_journal_entry( + posting_date="2021-03-15", + amount=400, + account1="Cash - TPC", + account2="Sales - TPC", + cost_center=cost_center1, + save=False, + ) + jv1.company = company + jv1.save() + jv1.submit() + + jv2 = make_journal_entry( + posting_date="2021-03-15", + amount=200, + account1="Cash - TPC", + account2="Sales - TPC", + cost_center=cost_center2, + save=False, + ) + jv2.company = company + jv2.save() + jv2.submit() + + pcv1 = self.make_period_closing_voucher(posting_date="2021-03-31") + + closing_balance = frappe.db.get_value( + "Account Closing Balance", + { + "account": "Sales - TPC", + "cost_center": cost_center1, + "period_closing_voucher": pcv1.name, + "is_period_closing_voucher_entry": 0, + }, + ["credit", "credit_in_account_currency"], + as_dict=1, + ) + + self.assertEqual(closing_balance.credit, 400) + self.assertEqual(closing_balance.credit_in_account_currency, 400) + + jv3 = make_journal_entry( + posting_date="2022-03-15", + amount=300, + account1="Cash - TPC", + account2="Sales - TPC", + cost_center=cost_center2, + save=False, + ) + + jv3.company = company + jv3.save() + jv3.submit() + + pcv2 = self.make_period_closing_voucher(posting_date="2022-03-31") + + cc1_closing_balance = frappe.db.get_value( + "Account Closing Balance", + { + "account": "Sales - TPC", + "cost_center": cost_center1, + "period_closing_voucher": pcv2.name, + "is_period_closing_voucher_entry": 0, + }, + ["credit", "credit_in_account_currency"], + as_dict=1, + ) + + cc2_closing_balance = frappe.db.get_value( + "Account Closing Balance", + { + "account": "Sales - TPC", + "cost_center": cost_center2, + "period_closing_voucher": pcv2.name, + "is_period_closing_voucher_entry": 0, + }, + ["credit", "credit_in_account_currency"], + as_dict=1, + ) + + self.assertEqual(cc1_closing_balance.credit, 400) + self.assertEqual(cc1_closing_balance.credit_in_account_currency, 400) + self.assertEqual(cc2_closing_balance.credit, 500) + self.assertEqual(cc2_closing_balance.credit_in_account_currency, 500) def make_period_closing_voucher(self, posting_date=None, submit=True): surplus_account = create_account() From 0157fa15eb3dfbd32098cdae7ab6b0c947304182 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 10 Mar 2023 13:02:01 +0530 Subject: [PATCH 19/50] chore: Use account closing balance in set gl entries --- .../report/balance_sheet/balance_sheet.py | 2 + .../accounts/report/financial_statements.py | 145 ++++++++++++------ erpnext/hooks.py | 1 + 3 files changed, 99 insertions(+), 49 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 07552e311c0b..c831b7876c25 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -25,6 +25,8 @@ def execute(filters=None): company=filters.company, ) + filters.period_start_date = period_list[0]["year_start_date"] + currency = filters.presentation_currency or frappe.get_cached_value( "Company", filters.company, "default_currency" ) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 8c6fe43a93d2..d9c398004b26 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -419,45 +419,46 @@ def set_gl_entries_by_account( ): """Returns a dict like { "account": [gl entries], ... }""" - additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters) - - accounts = frappe.db.sql_list( - """select name from `tabAccount` - where lft >= %s and rgt <= %s and company = %s""", - (root_lft, root_rgt, company), + gl_entries = [] + account = frappe.qb.DocType("Account") + + accounts = ( + frappe.qb.from_(account) + .select(account.name) + .where(account.lft >= root_lft) + .where(account.rgt <= root_rgt) + .where(account.company == company) + .run(as_dict=True) ) - if accounts: - additional_conditions += " and account in ({})".format( - ", ".join(frappe.db.escape(d) for d in accounts) - ) + accounts_list = [account.name for account in accounts] - gl_filters = { - "company": company, - "from_date": from_date, - "to_date": to_date, - "finance_book": cstr(filters.get("finance_book")), - } + if accounts_list: - if filters.get("include_default_book_entries"): - gl_filters["company_fb"] = frappe.get_cached_value("Company", company, "default_finance_book") - - for key, value in filters.items(): - if value: - gl_filters.update({key: value}) - - gl_entries = frappe.db.sql( - """ - select posting_date, account, debit, credit, is_opening, fiscal_year, - debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` - where company=%(company)s - {additional_conditions} - and posting_date <= %(to_date)s - and is_cancelled = 0""".format( - additional_conditions=additional_conditions - ), - gl_filters, - as_dict=True, + # For balance sheet + if not from_date: + from_date = filters["period_start_date"] + last_period_closing_voucher = frappe.db.get_all( + "Period Closing Voucher", + filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", from_date)}, + fields=["posting_date", "name"], + order_by="posting_date desc", + limit=1, + ) + if last_period_closing_voucher: + gl_entries += get_accounting_entries( + "Account Closing Balance", + from_date, + to_date, + accounts_list, + filters, + ignore_closing_entries, + last_period_closing_voucher[0].name, + ) + from_date = add_days(last_period_closing_voucher[0].posting_date, 1) + + gl_entries += get_accounting_entries( + "GL Entry", from_date, to_date, accounts_list, filters, ignore_closing_entries ) if filters and filters.get("presentation_currency"): @@ -469,34 +470,81 @@ def set_gl_entries_by_account( return gl_entries_by_account -def get_additional_conditions(from_date, ignore_closing_entries, filters): - additional_conditions = [] +def get_accounting_entries( + doctype, + from_date, + to_date, + accounts, + filters, + ignore_closing_entries, + period_closing_voucher=None, +): + gl_entry = frappe.qb.DocType(doctype) + query = ( + frappe.qb.from_(gl_entry) + .select( + gl_entry.account, + gl_entry.debit, + gl_entry.credit, + gl_entry.is_opening, + gl_entry.fiscal_year, + gl_entry.debit_in_account_currency, + gl_entry.credit_in_account_currency, + gl_entry.account_currency, + ) + .where(gl_entry.company == filters.company) + ) + + query = query.where(gl_entry.account.isin(accounts)) + + if doctype == "GL Entry": + query = query.select(gl_entry.posting_date) + query = query.where(gl_entry.is_cancelled == 0) + query = query.where(gl_entry.posting_date <= to_date) + else: + query = query.select(gl_entry.closing_date.as_("posting_date")) + query = query.where(gl_entry.period_closing_voucher == period_closing_voucher) + + query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters) + + entries = query.run(as_dict=True) + return entries + + +def apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters): + gl_entry = frappe.qb.DocType(doctype) accounting_dimensions = get_accounting_dimensions(as_list=False) if ignore_closing_entries: - additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'") + if doctype == "GL Entry": + query = query.where(gl_entry.voucher_type != "Period Closing Voucher") + else: + query = query.where(gl_entry.is_period_closing_voucher_entry == 0) - if from_date: - additional_conditions.append("posting_date >= %(from_date)s") + if from_date and doctype == "GL Entry": + query = query.where(gl_entry.posting_date >= from_date) if filters: if filters.get("project"): if not isinstance(filters.get("project"), list): filters.project = frappe.parse_json(filters.get("project")) - additional_conditions.append("project in %(project)s") + query = query.where(gl_entry.project.isin(filters.project)) if filters.get("cost_center"): filters.cost_center = get_cost_centers_with_children(filters.cost_center) - additional_conditions.append("cost_center in %(cost_center)s") + query = query.where(gl_entry.cost_center.isin(filters.cost_center)) if filters.get("include_default_book_entries"): - additional_conditions.append( - "(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)" + query = query.where( + (gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(filters.company_fb), ""])) + | (gl_entry.finance_book.isnull()) ) else: - additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") + query = query.where( + (gl_entry.finance_book.isin([cstr(filters.company_fb), ""])) | (gl_entry.finance_book.isnull()) + ) if accounting_dimensions: for dimension in accounting_dimensions: @@ -505,11 +553,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): filters[dimension.fieldname] = get_dimension_with_children( dimension.document_type, filters.get(dimension.fieldname) ) - additional_conditions.append("{0} in %({0})s".format(dimension.fieldname)) - else: - additional_conditions.append("{0} in %({0})s".format(dimension.fieldname)) - return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" + query = query.where(gl_entry[dimension.fieldname].isin(filters[dimension.fieldname])) + + return query def get_cost_centers_with_children(cost_centers): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index dbfbcc9b3852..e8822a7dcfa4 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -510,6 +510,7 @@ "Subcontracting Order Item", "Subcontracting Receipt", "Subcontracting Receipt Item", + "Accounts Closing Balance", ] # get matching queries for Bank Reconciliation From 76775a3e49623cbca0e6ac1ae54e7284a849b834 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 13 Mar 2023 20:51:35 +0530 Subject: [PATCH 20/50] fix: Update patch --- erpnext/patches/v14_0/update_closing_balances.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 0442b362ee5e..40a18516cb7f 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -4,7 +4,9 @@ import frappe -from erpnext.accounts.doctype.closing_balance.closing_balance import make_closing_entries +from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( + make_closing_entries, +) from erpnext.accounts.utils import get_fiscal_year From 7f11373b58431d0ab65bb742f6cdeebdade655b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 17 Mar 2023 16:59:23 +0530 Subject: [PATCH 21/50] fix: Account sub query --- erpnext/accounts/report/trial_balance/trial_balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 82bb576a2156..acbf7a8b84ef 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -190,7 +190,7 @@ def get_opening_balance( (closing_balance.company == filters.company) & ( closing_balance.account.isin( - frappe.qb.from_("account").select("name").where(account.report_type == report_type) + frappe.qb.from_(account).select("name").where(account.report_type == report_type) ) ) ) From 00fe3042b2969b72b46395b965cfb01a356ca9cd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 18 Mar 2023 20:05:43 +0530 Subject: [PATCH 22/50] chore: Simplify query --- .../period_closing_voucher.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index f6289e7e4791..e93fa8c1402b 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -233,19 +233,15 @@ def get_balances_based_on_dimensions( if group_by_account: qb_dimension_fields.append("account") - account = frappe.qb.DocType("Account") - accounts_query = ( - frappe.qb.from_(account) - .select(account.name) - .where((account.company == self.company) & (account.is_group == 0) & (account.docstatus < 2)) - ) + account_filters = { + "company": self.company, + "is_group": 0, + } if report_type: - accounts_query = accounts_query.where(account.report_type == report_type) - - accounts = accounts_query.run(as_dict=True) + account_filters.update({"report_type": report_type}) - accounts = [d.name for d in accounts] + accounts = frappe.get_all("Account", filters=account_filters, pluck="name") gl_entry = frappe.qb.DocType("GL Entry") query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency) From 0aadb680eb32ef2d594e5dcf49247754a2fe0523 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Mar 2023 12:46:42 +0530 Subject: [PATCH 23/50] chore: Add missing validations --- .../period_closing_voucher.py | 19 ++++++++++++---- .../accounts/report/financial_statements.py | 4 +--- erpnext/accounts/utils.py | 22 ++++++++++++++++--- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index e93fa8c1402b..831c34307123 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -5,12 +5,12 @@ import frappe from frappe import _ from frappe.query_builder.functions import Sum -from frappe.utils import flt +from frappe.utils import add_days, flt from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) -from erpnext.accounts.utils import get_account_currency +from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year from erpnext.controllers.accounts_controller import AccountsController @@ -72,8 +72,6 @@ def validate_account_head(self): frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency)) def validate_posting_date(self): - from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year - validate_fiscal_year( self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self ) @@ -82,6 +80,8 @@ def validate_posting_date(self): self.posting_date, self.fiscal_year, company=self.company )[1] + self.check_if_previous_year_closed() + pce = frappe.db.sql( """select name from `tabPeriod Closing Voucher` where posting_date > %s and fiscal_year = %s and docstatus = 1 and company = %s""", @@ -94,6 +94,17 @@ def validate_posting_date(self): ) ) + def check_if_previous_year_closed(self): + last_year_closing = add_days(self.year_start_date, -1) + + previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True) + + if previous_fiscal_year and not frappe.db.exists( + "Period Closing Voucher", + {"posting_date": ("<=", last_year_closing), "docstatus": 1, "company": self.company}, + ): + frappe.throw(_("Previous Year is not closed, please close it first")) + def make_gl_entries(self, get_opening_entries=False): gl_entries = self.get_gl_entries() closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index d9c398004b26..9bb05fda969b 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -486,8 +486,6 @@ def get_accounting_entries( gl_entry.account, gl_entry.debit, gl_entry.credit, - gl_entry.is_opening, - gl_entry.fiscal_year, gl_entry.debit_in_account_currency, gl_entry.credit_in_account_currency, gl_entry.account_currency, @@ -498,7 +496,7 @@ def get_accounting_entries( query = query.where(gl_entry.account.isin(accounts)) if doctype == "GL Entry": - query = query.select(gl_entry.posting_date) + query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year) query = query.where(gl_entry.is_cancelled == 0) query = query.where(gl_entry.posting_date <= to_date) else: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 005a2f176c41..92906c189a3a 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -51,13 +51,25 @@ class PaymentEntryUnlinkError(frappe.ValidationError): @frappe.whitelist() def get_fiscal_year( - date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False + date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False ): - return get_fiscal_years(date, fiscal_year, label, verbose, company, as_dict=as_dict)[0] + fiscal_years = get_fiscal_years( + date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean + ) + if boolean: + return fiscal_years + else: + return fiscal_years[0] def get_fiscal_years( - transaction_date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False + transaction_date=None, + fiscal_year=None, + label="Date", + verbose=1, + company=None, + as_dict=False, + boolean=False, ): fiscal_years = frappe.cache().hget("fiscal_years", company) or [] @@ -121,8 +133,12 @@ def get_fiscal_years( if company: error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company)) + if boolean: + return False + if verbose == 1: frappe.msgprint(error_msg) + raise FiscalYearError(error_msg) From 2d14d92b32021f1a5af76eae8a7f83bfb06e58db Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 6 Mar 2023 20:59:34 +0530 Subject: [PATCH 24/50] fix: incorrect currency symbol in Bank Reconciliation tool --- .../bank_reconciliation_tool.js | 7 ++++--- .../bank_reconciliation_tool.json | 14 +++++++++++--- .../js/bank_reconciliation_tool/dialog_manager.js | 10 +++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index ae84154f2dff..22d3335ba1ab 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -72,6 +72,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, }) }); + }, after_save: function (frm) { @@ -89,7 +90,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { r.account, "account_currency", (r) => { - frm.currency = r.account_currency; + frm.doc.account_currency = r.account_currency; frm.trigger("render_chart"); } ); @@ -162,9 +163,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", { "reconciliation_tool_cards" ).$wrapper, bank_statement_closing_balance: - frm.doc.bank_statement_closing_balance, + frm.doc.bank_statement_closing_balance, cleared_balance: frm.cleared_balance, - currency: frm.currency, + currency: frm.doc.account_currency, } ); }, diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json index 80993d6608de..93fc4439d351 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json @@ -14,6 +14,7 @@ "to_reference_date", "filter_by_reference_date", "column_break_2", + "account_currency", "account_opening_balance", "bank_statement_closing_balance", "section_break_1", @@ -59,7 +60,7 @@ "fieldname": "account_opening_balance", "fieldtype": "Currency", "label": "Account Opening Balance", - "options": "Currency", + "options": "account_currency", "read_only": 1 }, { @@ -67,7 +68,7 @@ "fieldname": "bank_statement_closing_balance", "fieldtype": "Currency", "label": "Closing Balance", - "options": "Currency" + "options": "account_currency" }, { "fieldname": "section_break_1", @@ -104,13 +105,20 @@ "fieldname": "filter_by_reference_date", "fieldtype": "Check", "label": "Filter by Reference Date" + }, + { + "fieldname": "account_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Account Currency", + "options": "Currency" } ], "hide_toolbar": 1, "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-01-13 13:00:02.022919", + "modified": "2023-03-07 11:02:24.535714", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Reconciliation Tool", diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 321b812de21a..1271e38049a0 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -391,14 +391,14 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { fieldname: "deposit", fieldtype: "Currency", label: "Deposit", - options: "currency", + options: "account_currency", read_only: 1, }, { fieldname: "withdrawal", fieldtype: "Currency", label: "Withdrawal", - options: "currency", + options: "account_currency", read_only: 1, }, { @@ -416,18 +416,18 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { fieldname: "allocated_amount", fieldtype: "Currency", label: "Allocated Amount", - options: "Currency", + options: "account_currency", read_only: 1, }, { fieldname: "unallocated_amount", fieldtype: "Currency", label: "Unallocated Amount", - options: "Currency", + options: "account_currency", read_only: 1, }, { - fieldname: "currency", + fieldname: "account_currency", fieldtype: "Link", label: "Currency", options: "Currency", From 1eea585d29586eeee1e17853f1267231a7fcc26a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 19 Mar 2023 14:02:53 +0530 Subject: [PATCH 25/50] refactor: allow for concurrent use of reconciliation tool 1. set default filter dates a period of one month from current date --- .../bank_reconciliation_tool.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 22d3335ba1ab..d97726144113 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -18,6 +18,10 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, onload: function (frm) { + // Set default filter dates + today = frappe.datetime.get_today() + frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1); + frm.doc.bank_statement_to_date = today; frm.trigger('bank_account'); }, @@ -32,6 +36,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, refresh: function (frm) { + frm.disable_save(); frappe.require("bank-reconciliation-tool.bundle.js", () => frm.trigger("make_reconciliation_tool") ); @@ -73,10 +78,11 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }) }); - }, + frm.add_custom_button(__('Get Unreconciled Entries'), function() { + frm.trigger("make_reconciliation_tool"); + }); + frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary'); - after_save: function (frm) { - frm.trigger("make_reconciliation_tool"); }, bank_account: function (frm) { From 44053db0104ab69986bebd7d3e29478ff3759f8d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Mar 2023 19:46:01 +0530 Subject: [PATCH 26/50] chore: Remove unnecessary list comprehension --- .../report/balance_sheet/balance_sheet.py | 3 +-- .../accounts/report/financial_statements.py | 21 ++++++------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index c831b7876c25..b225aac7b560 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -98,7 +98,7 @@ def execute(filters=None): chart = get_chart_data(filters, columns, asset, liability, equity) report_summary = get_report_summary( - period_list, asset, liability, equity, provisional_profit_loss, total_credit, currency, filters + period_list, asset, liability, equity, provisional_profit_loss, currency, filters ) return columns, data, message, chart, report_summary @@ -176,7 +176,6 @@ def get_report_summary( liability, equity, provisional_profit_loss, - total_credit, currency, filters, consolidated=False, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 9bb05fda969b..cd01a35c1ab0 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -418,23 +418,15 @@ def set_gl_entries_by_account( ignore_closing_entries=False, ): """Returns a dict like { "account": [gl entries], ... }""" - gl_entries = [] - account = frappe.qb.DocType("Account") - - accounts = ( - frappe.qb.from_(account) - .select(account.name) - .where(account.lft >= root_lft) - .where(account.rgt <= root_rgt) - .where(account.company == company) - .run(as_dict=True) - ) - accounts_list = [account.name for account in accounts] + accounts_list = frappe.db.get_all( + "Account", + filters={"company": company, "is_group": 0, "lft": (">=", root_lft), "rgt": ("<=", root_rgt)}, + pluck="name", + ) if accounts_list: - # For balance sheet if not from_date: from_date = filters["period_start_date"] @@ -493,8 +485,6 @@ def get_accounting_entries( .where(gl_entry.company == filters.company) ) - query = query.where(gl_entry.account.isin(accounts)) - if doctype == "GL Entry": query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year) query = query.where(gl_entry.is_cancelled == 0) @@ -504,6 +494,7 @@ def get_accounting_entries( query = query.where(gl_entry.period_closing_voucher == period_closing_voucher) query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters) + query = query.where(gl_entry.account.isin(accounts)) entries = query.run(as_dict=True) From aaa4d1eb5582028fcf1e46de0fa1a176311e5562 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 21 Mar 2023 14:15:31 +0530 Subject: [PATCH 27/50] fix: E-commerce issue with Item Variants --- erpnext/e_commerce/variant_selector/utils.py | 24 ++++++++++++++++++- .../generators/item/item_configure.js | 22 ++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/erpnext/e_commerce/variant_selector/utils.py b/erpnext/e_commerce/variant_selector/utils.py index df62c23aa486..1a3e73792816 100644 --- a/erpnext/e_commerce/variant_selector/utils.py +++ b/erpnext/e_commerce/variant_selector/utils.py @@ -1,5 +1,5 @@ import frappe -from frappe.utils import cint +from frappe.utils import cint, flt from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ( get_shopping_cart_settings, @@ -166,6 +166,27 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None + product_id = "" + website_warehouse = "" + if exact_match or filtered_items: + if exact_match and len(exact_match) == 1: + product_id = exact_match[0] + elif filtered_items_count == 1: + product_id = list(filtered_items)[0] + + if product_id: + website_warehouse = frappe.get_cached_value( + "Website Item", {"item_code": product_id}, "website_warehouse" + ) + + available_qty = 0.0 + if website_warehouse: + available_qty = flt( + frappe.db.get_value( + "Bin", {"item_code": product_id, "warehouse": website_warehouse}, "actual_qty" + ) + ) + return { "next_attribute": next_attribute, "valid_options_for_attributes": valid_options_for_attributes, @@ -173,6 +194,7 @@ def get_next_attribute_and_values(item_code, selected_attributes): "filtered_items": filtered_items if filtered_items_count < 10 else [], "exact_match": exact_match, "product_info": product_info, + "available_qty": available_qty, } diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 231ae0587ed4..613c967e3d60 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -186,14 +186,14 @@ class ItemConfigure { this.dialog.$status_area.empty(); } - get_html_for_item_found({ filtered_items_count, filtered_items, exact_match, product_info }) { + get_html_for_item_found({ filtered_items_count, filtered_items, exact_match, product_info, available_qty, settings }) { const one_item = exact_match.length === 1 ? exact_match[0] : filtered_items_count === 1 ? filtered_items[0] : ''; - const item_add_to_cart = one_item ? ` + let item_add_to_cart = one_item ? `