mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-02 19:59:12 +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": [],
|
||||
"allow_bulk_edit": 1,
|
||||
"autoname": "format:Process-PCV-{###}",
|
||||
"creation": "2025-09-25 15:44:03.534699",
|
||||
"doctype": "DocType",
|
||||
@@ -7,11 +8,13 @@
|
||||
"field_order": [
|
||||
"parent_pcv",
|
||||
"status",
|
||||
"amended_from",
|
||||
"section_normal_balances",
|
||||
"p_l_closing_balance",
|
||||
"normal_balances",
|
||||
"bs_closing_balance",
|
||||
"z_opening_balances",
|
||||
"amended_from"
|
||||
"normal_balances",
|
||||
"section_opening_balances",
|
||||
"z_opening_balances"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -64,17 +67,27 @@
|
||||
"fieldname": "bs_closing_balance",
|
||||
"fieldtype": "JSON",
|
||||
"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,
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-05 11:40:24.996403",
|
||||
"modified": "2026-06-01 12:16:37.374412",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Period Closing Voucher",
|
||||
"naming_rule": "Expression",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
||||
@@ -36,8 +36,8 @@ class ProcessPeriodClosingVoucher(Document):
|
||||
parent_pcv: DF.Link
|
||||
status: DF.Literal["Queued", "Running", "Paused", "Completed", "Cancelled"]
|
||||
z_opening_balances: DF.Table[ProcessPeriodClosingVoucherDetail]
|
||||
|
||||
# end: auto-generated types
|
||||
|
||||
def on_discard(self):
|
||||
self.db_set("status", "Cancelled")
|
||||
|
||||
@@ -562,6 +562,9 @@ def process_individual_date(docname: str, date, report_type, parentfield):
|
||||
|
||||
if parentfield == "z_opening_balances":
|
||||
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)
|
||||
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
|
||||
# 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