From 76bdf7944c4ed9d79ffd133edfab952252ef58c7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 14 Oct 2025 16:31:46 +0530 Subject: [PATCH] refactor: store daily balances based on dimensions key dimensions key is manually converted to string (cherry picked from commit 1846de0d493494311555b956865649ba1dd2de47) --- .../process_period_closing_voucher.py | 98 ++++++++++++++++--- 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/process_period_closing_voucher/process_period_closing_voucher.py b/erpnext/accounts/doctype/process_period_closing_voucher/process_period_closing_voucher.py index e68aaa6b8cb..5786c319e39 100644 --- a/erpnext/accounts/doctype/process_period_closing_voucher/process_period_closing_voucher.py +++ b/erpnext/accounts/doctype/process_period_closing_voucher/process_period_closing_voucher.py @@ -7,10 +7,10 @@ import frappe from frappe import qb from frappe.model.document import Document from frappe.query_builder.functions import Sum -from frappe.utils import add_days, get_datetime +from frappe.utils import add_days, flt, get_datetime from frappe.utils.scheduler import is_scheduler_inactive -BACKGROUND = True +BACKGROUND = False class ProcessPeriodClosingVoucher(Document): @@ -76,13 +76,16 @@ def start_pcv_processing(docname: str): @frappe.whitelist() def pause_pcv_processing(docname: str): + ppcv = qb.DocType("Process Period Closing Voucher") + qb.update(ppcv).set(ppcv.status, "Paused").where(ppcv.name.eq(docname)).run() + queued_dates = frappe.db.get_all( "Process Period Closing Voucher Detail", filters={"parent": docname, "status": "Queued"}, - fields=["name"], + pluck="name", ) - ppcv = qb.DocType("Process Period Closing Voucher") - qb.update(ppcv).set(ppcv.status, "Paused").where(ppcv.name.isin(queued_dates)).run() + ppcvd = qb.DocType("Process Period Closing Voucher Detail") + qb.update(ppcvd).set(ppcvd.status, "Paused").where(ppcvd.name.isin(queued_dates)).run() def call_next_date(docname: str): @@ -121,32 +124,101 @@ def call_next_date(docname: str): limit=1, ) if not running: + # TODO: Generate GL and Account Closing Balance frappe.db.set_value("Process Period Closing Voucher", docname, "status", "Completed") +def get_dimensions(): + from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, + ) + + default_dimensions = ["cost_center", "finance_book", "project"] + dimensions = default_dimensions + get_accounting_dimensions() + return dimensions + + +def get_key(res): + return tuple([res.get(dimension) for dimension in get_dimensions()]) + + def process_individual_date(docname: str, date: str): if frappe.db.get_value("Process Period Closing Voucher", docname, "status") == "Running": pcv_name = frappe.db.get_value("Process Period Closing Voucher", docname, "parent_pcv") pcv = frappe.get_doc("Period Closing Voucher", pcv_name) - gle = qb.DocType("GL Entry") - res = ( - qb.from_(gle) - .select(gle.account, Sum(gle.debit).as_("debit"), Sum(gle.credit).as_("credit")) - .where((gle.company.eq(pcv.company)) & (gle.is_cancelled.eq(False)) & (gle.posting_date.eq(date))) - .groupby(gle.account) - .run(as_dict=True) + + dimensions = get_dimensions() + + p_l_accounts = frappe.db.get_all( + "Account", filters={"company": pcv.company, "report_type": "Profit and Loss"}, pluck="name" ) + + gle = qb.DocType("GL Entry") + query = qb.from_(gle).select(gle.account) + for dim in dimensions: + query = query.select(gle[dim]) + + query = query.select( + Sum(gle.debit).as_("debit"), + Sum(gle.credit).as_("credit"), + Sum(gle.debit_in_account_currency).as_("debit_in_account_currency"), + Sum(gle.credit_in_account_currency).as_("credit_in_account_currency"), + ).where( + (gle.company.eq(pcv.company)) + & (gle.is_cancelled.eq(0)) + & (gle.posting_date.eq(date)) + & (gle.account.isin(p_l_accounts)) + ) + + query = query.groupby(gle.account) + for dim in dimensions: + query = query.groupby(gle[dim]) + + res = query.run(as_dict=True) + + dimension_wise_acc_balances = frappe._dict() + for x in res: + dimension_key = get_key(x) + dimension_wise_acc_balances.setdefault(dimension_key, frappe._dict()).setdefault( + x.account, + frappe._dict( + { + "debit_in_account_currency": 0, + "credit_in_account_currency": 0, + "debit": 0, + "credit": 0, + "account_currency": x.account_currency, + } + ), + ) + dimension_wise_acc_balances[dimension_key][x.account].debit_in_account_currency += flt( + x.debit_in_account_currency + ) + dimension_wise_acc_balances[dimension_key][x.account].credit_in_account_currency += flt( + x.credit_in_account_currency + ) + dimension_wise_acc_balances[dimension_key][x.account].debit += flt(x.debit) + dimension_wise_acc_balances[dimension_key][x.account].credit += flt(x.credit) + frappe.db.set_value( "Process Period Closing Voucher Detail", {"processing_date": date, "parent": docname}, "status", "Completed", ) + + # convert dict keys to json compliant json dictionary keys + json_dict = {} + for k, v in dimension_wise_acc_balances.items(): + str_key = [str(x) for x in k] + str_key = ",".join(str_key) + json_dict[str_key] = v + frappe.db.set_value( "Process Period Closing Voucher Detail", {"processing_date": date, "parent": docname}, "closing_balance", - frappe.json.dumps(res), + frappe.json.dumps(json_dict), ) call_next_date(docname)