Skip to content

Commit de37353

Browse files
committed
fix: ensure multiple PCVs in same fiscal year are considered in patch
1 parent 6db1b3f commit de37353

File tree

2 files changed

+85
-59
lines changed

2 files changed

+85
-59
lines changed

erpnext/patches.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
314314
erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
315315
erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
316316
erpnext.patches.v14_0.set_period_start_end_date_in_pcv
317-
erpnext.patches.v14_0.update_closing_balances #08-11-2024
317+
erpnext.patches.v14_0.update_closing_balances #20-12-2024
318318
execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
319319
erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts
320320
erpnext.patches.v14_0.update_subscription_details
@@ -392,4 +392,4 @@ erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_format
392392
erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions
393393
erpnext.patches.v14_0.update_stock_uom_in_work_order_item
394394
erpnext.patches.v15_0.enable_allow_existing_serial_no
395-
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts
395+
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts

erpnext/patches/v14_0/update_closing_balances.py

+83-57
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
22
# License: MIT. See LICENSE
33

4+
import itertools
45

56
import frappe
67

@@ -10,34 +11,91 @@
1011
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
1112
get_accounting_dimensions,
1213
)
13-
from erpnext.accounts.utils import get_fiscal_year
1414

1515

1616
def execute():
17+
# clear balances, they will be recalculated
1718
frappe.db.truncate("Account Closing Balance")
1819

19-
gle_fields = get_gle_fields()
20+
pcv_list = get_period_closing_vouchers()
21+
gl_entries = get_gl_entries(pcv_list)
22+
23+
for _, pcvs in itertools.groupby(pcv_list, key=lambda pcv: (pcv.company, pcv.period_start_date)):
24+
process_grouped_pcvs(list(pcvs), gl_entries)
25+
26+
27+
def process_grouped_pcvs(pcvs, gl_entries):
28+
pl_account_entries = []
29+
closing_account_entries = []
30+
first_pcv = pcvs[0]
31+
32+
for pcv in pcvs:
33+
pcv_entries = gl_entries.get(pcv.name) or []
34+
for entry in pcv_entries:
35+
entry["closing_date"] = first_pcv.period_end_date
36+
entry["period_closing_voucher"] = first_pcv.name
37+
entry["voucher_no"] = first_pcv.name
38+
39+
list_to_update = (
40+
pl_account_entries if entry.account != pcv.closing_account_head else closing_account_entries
41+
)
42+
list_to_update.append(entry)
43+
44+
# hacky!!
45+
if to_cancel := pcvs[1:]:
46+
to_cancel = [pcv.name for pcv in to_cancel]
47+
48+
# update voucher number
49+
gle_to_update = [entry.name for entry in closing_account_entries + pl_account_entries]
50+
frappe.db.set_value(
51+
"GL Entry",
52+
{
53+
"name": ("in", gle_to_update),
54+
"voucher_no": ("in", to_cancel),
55+
},
56+
"voucher_no",
57+
first_pcv.name,
58+
update_modified=False,
59+
)
60+
61+
# mark as cancelled
62+
frappe.db.set_value(
63+
"Period Closing Voucher",
64+
{"name": ("in", to_cancel)},
65+
"docstatus",
66+
2,
67+
update_modified=False,
68+
)
69+
70+
pcv_doc = frappe.get_doc("Period Closing Voucher", first_pcv.name)
71+
pcv_doc.pl_accounts_reverse_gle = pl_account_entries
72+
pcv_doc.closing_account_gle = closing_account_entries
73+
closing_entries = pcv_doc.get_account_closing_balances()
74+
make_closing_entries(closing_entries, pcv_doc.name, pcv_doc.company, pcv_doc.period_end_date)
75+
76+
77+
def get_period_closing_vouchers():
78+
return frappe.db.get_all(
79+
"Period Closing Voucher",
80+
fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"],
81+
filters={"docstatus": 1},
82+
order_by="period_start_date asc, period_end_date desc",
83+
)
84+
2085

21-
for company in frappe.get_all("Company", pluck="name"):
22-
i = 0
23-
company_wise_order = {}
24-
for pcv in get_period_closing_vouchers(company):
25-
company_wise_order.setdefault(pcv.company, [])
26-
if pcv.period_end_date not in company_wise_order[pcv.company]:
27-
pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name)
28-
pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields)
29-
pcv_doc.closing_account_gle = get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields)
30-
closing_entries = pcv_doc.get_account_closing_balances()
31-
make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date)
86+
def get_gl_entries(pcv_list):
87+
gl_entries = frappe.get_all(
88+
"GL Entry",
89+
filters={"voucher_no": ("in", [pcv.name for pcv in pcv_list]), "is_cancelled": 0},
90+
fields=get_gle_fields(),
91+
update={"is_period_closing_voucher_entry": 1},
92+
)
3293

33-
company_wise_order[pcv.company].append(pcv.period_end_date)
34-
i += 1
94+
return {k: list(v) for k, v in itertools.groupby(gl_entries, key=lambda gle: gle.voucher_no)}
3595

3696

3797
def get_gle_fields():
38-
default_diemnsion_fields = ["cost_center", "finance_book", "project"]
39-
accounting_dimension_fields = get_accounting_dimensions()
40-
gle_fields = [
98+
return [
4199
"name",
42100
"company",
43101
"posting_date",
@@ -47,43 +105,11 @@ def get_gle_fields():
47105
"credit",
48106
"debit_in_account_currency",
49107
"credit_in_account_currency",
50-
*default_diemnsion_fields,
51-
*accounting_dimension_fields,
108+
"voucher_no",
109+
# default dimension fields
110+
"cost_center",
111+
"finance_book",
112+
"project",
113+
# accounting dimensions
114+
*get_accounting_dimensions(),
52115
]
53-
54-
return gle_fields
55-
56-
57-
def get_period_closing_vouchers(company):
58-
return frappe.db.get_all(
59-
"Period Closing Voucher",
60-
fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"],
61-
filters={"docstatus": 1, "company": company},
62-
order_by="period_end_date",
63-
)
64-
65-
66-
def get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields):
67-
return get_gl_entries(pcv, gle_fields, {"account": ["!=", pcv.closing_account_head]})
68-
69-
70-
def get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields):
71-
return get_gl_entries(pcv, gle_fields, {"account": pcv.closing_account_head})
72-
73-
74-
def get_gl_entries(pcv, gle_fields, accounts_filter=None):
75-
filters = {"voucher_no": pcv.name, "is_cancelled": 0}
76-
if accounts_filter:
77-
filters.update(accounts_filter)
78-
79-
gl_entries = frappe.db.get_all(
80-
"GL Entry",
81-
filters=filters,
82-
fields=gle_fields,
83-
)
84-
for entry in gl_entries:
85-
entry["is_period_closing_voucher_entry"] = 1
86-
entry["closing_date"] = pcv.period_end_date
87-
entry["period_closing_voucher"] = pcv.name
88-
89-
return gl_entries

0 commit comments

Comments
 (0)