mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-03 04:09:11 +00:00
Merge pull request #55502 from frappe/mergify/bp/version-16/pr-55495
fix: opening bal double counting in Process Period Closing Voucher (backport #55495)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
|
"allow_bulk_edit": 1,
|
||||||
"autoname": "format:Process-PCV-{###}",
|
"autoname": "format:Process-PCV-{###}",
|
||||||
"creation": "2025-09-25 15:44:03.534699",
|
"creation": "2025-09-25 15:44:03.534699",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -7,11 +8,13 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"parent_pcv",
|
"parent_pcv",
|
||||||
"status",
|
"status",
|
||||||
|
"amended_from",
|
||||||
|
"section_normal_balances",
|
||||||
"p_l_closing_balance",
|
"p_l_closing_balance",
|
||||||
"normal_balances",
|
|
||||||
"bs_closing_balance",
|
"bs_closing_balance",
|
||||||
"z_opening_balances",
|
"normal_balances",
|
||||||
"amended_from"
|
"section_opening_balances",
|
||||||
|
"z_opening_balances"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -64,17 +67,27 @@
|
|||||||
"fieldname": "bs_closing_balance",
|
"fieldname": "bs_closing_balance",
|
||||||
"fieldtype": "JSON",
|
"fieldtype": "JSON",
|
||||||
"label": "Balance Sheet Closing Balance"
|
"label": "Balance Sheet Closing Balance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_normal_balances",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Normal Balances"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_opening_balances",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Opening Balances"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-11-05 11:40:24.996403",
|
"modified": "2026-06-01 12:16:37.374412",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Process Period Closing Voucher",
|
"name": "Process Period Closing Voucher",
|
||||||
"naming_rule": "Expression",
|
"naming_rule": "Expression (old style)",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ class ProcessPeriodClosingVoucher(Document):
|
|||||||
parent_pcv: DF.Link
|
parent_pcv: DF.Link
|
||||||
status: DF.Literal["Queued", "Running", "Paused", "Completed", "Cancelled"]
|
status: DF.Literal["Queued", "Running", "Paused", "Completed", "Cancelled"]
|
||||||
z_opening_balances: DF.Table[ProcessPeriodClosingVoucherDetail]
|
z_opening_balances: DF.Table[ProcessPeriodClosingVoucherDetail]
|
||||||
|
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
def on_discard(self):
|
def on_discard(self):
|
||||||
self.db_set("status", "Cancelled")
|
self.db_set("status", "Cancelled")
|
||||||
|
|
||||||
@@ -562,6 +562,9 @@ def process_individual_date(docname: str, date, report_type, parentfield):
|
|||||||
|
|
||||||
if parentfield == "z_opening_balances":
|
if parentfield == "z_opening_balances":
|
||||||
query = query.where(gle.is_opening.eq("Yes"))
|
query = query.where(gle.is_opening.eq("Yes"))
|
||||||
|
else:
|
||||||
|
# Keep balances aligned with legacy PCV logic (non-opening transactions only)
|
||||||
|
query = query.where(gle.is_opening.eq("No"))
|
||||||
|
|
||||||
query = query.groupby(gle.account)
|
query = query.groupby(gle.account)
|
||||||
for dim in dimensions:
|
for dim in dimensions:
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
// render
|
||||||
|
frappe.listview_settings["Process Period Closing Voucher"] = {
|
||||||
|
add_fields: ["status"],
|
||||||
|
get_indicator: function (doc) {
|
||||||
|
const status_colors = {
|
||||||
|
Queued: "blue",
|
||||||
|
Running: "orange",
|
||||||
|
Paused: "gray",
|
||||||
|
Completed: "green",
|
||||||
|
Cancelled: "red",
|
||||||
|
};
|
||||||
|
return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,4 +1,173 @@
|
|||||||
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
# import frappe
|
import frappe
|
||||||
|
from frappe.utils import today
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
|
from erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher import (
|
||||||
|
process_individual_date,
|
||||||
|
)
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
|
|
||||||
|
class TestProcessPeriodClosingVoucher(ERPNextTestSuite):
|
||||||
|
def setUp(self):
|
||||||
|
frappe.db.set_single_value("Accounts Settings", "use_legacy_controller_for_pcv", 0)
|
||||||
|
self.company = "_Test Company"
|
||||||
|
|
||||||
|
def make_period_closing_voucher(self, posting_date, submit=True):
|
||||||
|
fy = get_fiscal_year(posting_date, company="_Test Company")
|
||||||
|
pcv = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Period Closing Voucher",
|
||||||
|
"transaction_date": posting_date or today(),
|
||||||
|
"period_start_date": fy[1],
|
||||||
|
"period_end_date": fy[2],
|
||||||
|
"company": self.company,
|
||||||
|
"fiscal_year": fy[0],
|
||||||
|
"closing_account_head": "Retained Earnings - _TC",
|
||||||
|
"remarks": "closing",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
pcv.insert()
|
||||||
|
if submit:
|
||||||
|
pcv.submit()
|
||||||
|
|
||||||
|
return pcv
|
||||||
|
|
||||||
|
def make_process_pcv(self):
|
||||||
|
self.pcv = self.make_period_closing_voucher(posting_date=today(), submit=False)
|
||||||
|
ppcv = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Process Period Closing Voucher",
|
||||||
|
"parent_pcv": self.pcv.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ppcv.save()
|
||||||
|
return ppcv
|
||||||
|
|
||||||
|
def set_processing_date_status(self, date, ppcv, rpt_type, parentfield, status):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Process Period Closing Voucher Detail",
|
||||||
|
{"processing_date": date, "parent": ppcv, "report_type": rpt_type, "parentfield": parentfield},
|
||||||
|
"status",
|
||||||
|
status,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_processing_date_closing_balance(self, date, ppcv, rpt_type, parentfield):
|
||||||
|
return frappe.db.get_value(
|
||||||
|
"Process Period Closing Voucher Detail",
|
||||||
|
{"processing_date": date, "parent": ppcv, "report_type": rpt_type, "parentfield": parentfield},
|
||||||
|
"closing_balance",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_opening_balance_double_counting(self):
|
||||||
|
ppcv = self.make_process_pcv()
|
||||||
|
self.assertEqual(self.pcv.is_first_period_closing_voucher(), True)
|
||||||
|
opening_jv = make_journal_entry(
|
||||||
|
posting_date=today(),
|
||||||
|
amount=10,
|
||||||
|
account1="Cash - _TC",
|
||||||
|
account2="Debtors - _TC",
|
||||||
|
company=self.company,
|
||||||
|
save=False,
|
||||||
|
)
|
||||||
|
opening_jv.accounts[1].party_type = "Customer"
|
||||||
|
opening_jv.accounts[1].party = "_Test Customer"
|
||||||
|
opening_jv.is_opening = "Yes"
|
||||||
|
opening_jv.save()
|
||||||
|
opening_jv.submit()
|
||||||
|
|
||||||
|
jv = make_journal_entry(
|
||||||
|
posting_date=today(),
|
||||||
|
amount=120,
|
||||||
|
account1="Debtors - _TC",
|
||||||
|
account2="Sales - _TC",
|
||||||
|
company=self.company,
|
||||||
|
save=False,
|
||||||
|
)
|
||||||
|
jv.accounts[0].party_type = "Customer"
|
||||||
|
jv.accounts[0].party = "_Test Customer"
|
||||||
|
jv.save()
|
||||||
|
jv.submit()
|
||||||
|
|
||||||
|
# P&L balance
|
||||||
|
parentfield = "normal_balances"
|
||||||
|
rpt_type = "Profit and Loss"
|
||||||
|
# status has to be set to 'Running' for logic to run
|
||||||
|
self.set_processing_date_status(today(), ppcv.name, rpt_type, parentfield, "Running")
|
||||||
|
process_individual_date(ppcv.name, today(), rpt_type, parentfield)
|
||||||
|
bal = frappe.parse_json(
|
||||||
|
self.get_processing_date_closing_balance(today(), ppcv.name, rpt_type, parentfield)
|
||||||
|
)
|
||||||
|
self.assertEqual(len(bal), 1)
|
||||||
|
expected_pl = {
|
||||||
|
"account": "Sales - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"debit": 0.0,
|
||||||
|
"credit": 120.0,
|
||||||
|
"debit_in_account_currency": 0.0,
|
||||||
|
"credit_in_account_currency": 120.0,
|
||||||
|
}
|
||||||
|
for k in expected_pl.keys():
|
||||||
|
with self.subTest(k):
|
||||||
|
self.assertEqual(expected_pl[k], bal[0][k])
|
||||||
|
|
||||||
|
# Balance sheet balance
|
||||||
|
rpt_type = "Balance Sheet"
|
||||||
|
self.set_processing_date_status(today(), ppcv.name, rpt_type, parentfield, "Running")
|
||||||
|
process_individual_date(ppcv.name, today(), rpt_type, parentfield)
|
||||||
|
bal = frappe.parse_json(
|
||||||
|
self.get_processing_date_closing_balance(today(), ppcv.name, rpt_type, parentfield)
|
||||||
|
)
|
||||||
|
self.assertEqual(len(bal), 1)
|
||||||
|
expected_bs = {
|
||||||
|
"account": "Debtors - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"debit": 120.0,
|
||||||
|
"credit": 0.0,
|
||||||
|
"debit_in_account_currency": 120.0,
|
||||||
|
"credit_in_account_currency": 0.0,
|
||||||
|
}
|
||||||
|
for k in expected_bs.keys():
|
||||||
|
with self.subTest(k):
|
||||||
|
self.assertEqual(expected_bs[k], bal[0][k])
|
||||||
|
|
||||||
|
# Opening balance
|
||||||
|
parentfield = "z_opening_balances"
|
||||||
|
rpt_type = "Balance Sheet"
|
||||||
|
self.set_processing_date_status(today(), ppcv.name, rpt_type, parentfield, "Running")
|
||||||
|
process_individual_date(ppcv.name, today(), rpt_type, parentfield)
|
||||||
|
bal = frappe.parse_json(
|
||||||
|
self.get_processing_date_closing_balance(today(), ppcv.name, rpt_type, parentfield)
|
||||||
|
)
|
||||||
|
self.assertEqual(len(bal), 2)
|
||||||
|
opening_cash = next(x for x in bal if x["account"] == "Cash - _TC")
|
||||||
|
expected_opening_cash = {
|
||||||
|
"account": "Cash - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"debit": 10.0,
|
||||||
|
"credit": 0.0,
|
||||||
|
"debit_in_account_currency": 10.0,
|
||||||
|
"credit_in_account_currency": 0.0,
|
||||||
|
"account_currency": "INR",
|
||||||
|
}
|
||||||
|
for k in expected_opening_cash.keys():
|
||||||
|
with self.subTest(k):
|
||||||
|
self.assertEqual(expected_opening_cash[k], opening_cash[k])
|
||||||
|
|
||||||
|
opening_debtors = next(x for x in bal if x["account"] == "Debtors - _TC")
|
||||||
|
expected_opening_debtors = {
|
||||||
|
"account": "Debtors - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"debit": 0.0,
|
||||||
|
"credit": 10.0,
|
||||||
|
"debit_in_account_currency": 0.0,
|
||||||
|
"credit_in_account_currency": 10.0,
|
||||||
|
"account_currency": "INR",
|
||||||
|
}
|
||||||
|
for k in expected_opening_debtors.keys():
|
||||||
|
with self.subTest(k):
|
||||||
|
self.assertEqual(expected_opening_debtors[k], opening_debtors[k])
|
||||||
|
|||||||
Reference in New Issue
Block a user