From b634aa9cfb60f78531da4a02efec458a3d294a78 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 14 Sep 2024 15:58:02 +0530 Subject: [PATCH 1/6] fix: map rows on journal entry by validating account, party, debit and credit value --- erpnext/controllers/accounts_controller.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 4aa1d236fa43..778f93f043bc 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3570,6 +3570,13 @@ def check_if_child_table_updated(child_table_before_update, child_table_after_up # Check if any field affecting accounting entry is altered for index, item in enumerate(child_table_before_update): + if item.parenttype == "Journal Entry" and any( + [ + child_table_after_update[index].get(i) != item.get(i) + for i in ["account", "party_type", "party", "debit", "credit"] + ] + ): + continue for field in fields_to_check: if child_table_after_update[index].get(field) != item.get(field): return True From f47ea468066c25db8d6765205733e549c6dce190 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 14 Sep 2024 17:31:53 +0530 Subject: [PATCH 2/6] test: reconcile payment jv from closed fiscal year --- .../test_payment_reconciliation.py | 162 +++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 5aa411158a8f..65d7d42ef3da 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -5,7 +5,7 @@ import frappe from frappe import qb from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowdate +from frappe.utils import add_days, flt, getdate, nowdate, today from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -13,6 +13,7 @@ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.party import get_party_account +from erpnext.accounts.utils import get_fiscal_year from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.stock.doctype.item.test_item import create_item @@ -1845,6 +1846,107 @@ def test_cr_note_payment_limit_filter(self): self.assertEqual(len(pr.invoices), 1) self.assertEqual(len(pr.payments), 1) + def test_reconciliation_on_closed_period_payment(self): + from erpnext.accounts.doctype.account.test_account import create_account + + self.create_company() + self.create_cost_center() + + # create bank account + parent_account = frappe.db.get_value( + "Account", {"company": self.company, "account_name": "Bank Accounts", "is_group": 1}, "name" + ) + bank_account = create_account( + account_name="Bank Account", + account_type="Bank", + is_group=0, + company=self.company, + root_type="Asset", + report_type="Balance Sheet", + account_currency="INR", + parent_account=parent_account, + doctype="Account", + ) + + # create backdated fiscal year + create_fiscal_year(company=self.company, year_start_date="1990-04-01", year_end_date="1991-03-31") + + # make journal entry for previous year + je_1 = frappe.new_doc("Journal Entry") + je_1.posting_date = "1990-06-01" + je_1.company = self.company + je_1.user_remark = "test" + je_1.set( + "accounts", + [ + { + "account": self.debit_to, + "cost_center": self.cost_center, + "party_type": "Customer", + "party": self.customer, + "debit_in_account_currency": 0, + "credit_in_account_currency": 1000, + }, + { + "account": bank_account, + "cost_center": self.sub_cc, + "credit_in_account_currency": 0, + "debit_in_account_currency": 500, + }, + { + "account": "Cash - _PR", + "cost_center": self.sub_cc, + "credit_in_account_currency": 0, + "debit_in_account_currency": 500, + }, + ], + ) + je_1.save() + je_1.submit() + je_1.reload() + # check journal entry is submitted + self.assertTrue(je_1.docstatus == 1) + + # make period closing voucher + pcv = make_period_closing_voucher( + company=self.company, cost_center=self.cost_center, posting_date="1991-03-31" + ) + pcv.reload() + # check if period closing voucher is completed + self.assertEqual(pcv.gle_processing_status, "Completed") + + # make journal entry for active year + je_2 = self.create_journal_entry( + acc1=self.debit_to, acc2=self.income_account, amount=1000, posting_date=today() + ) + je_2.accounts[0].party_type = "Customer" + je_2.accounts[0].party = self.customer + je_2.save() + je_2.submit() + je_2.reload() + # check journal entry is submitted + self.assertTrue(je_2.docstatus == 1) + + # process reconciliation on closed period payment + pr = self.create_payment_reconciliation(party_is_customer=True) + pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = None + pr.get_unreconciled_entries() + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + je_1.reload() + je_2.reload() + + # check whether the payment reconciliation is done on the closed period + self.assertEqual(pr.get("invoices"), []) + self.assertEqual(pr.get("payments"), []) + + # cancel created entires during test + pcv.cancel() + je_1.cancel() + je_2.cancel() + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): @@ -1872,3 +1974,61 @@ def make_supplier(supplier_name, currency=None): return supplier.name else: return supplier_name + + +def create_fiscal_year(company, year_start_date, year_end_date): + fy_docname = frappe.db.exists( + "Fiscal Year", {"year_start_date": year_start_date, "year_end_date": year_end_date} + ) + if not fy_docname: + fy_doc = frappe.get_doc( + { + "doctype": "Fiscal Year", + "year": f"{getdate(year_start_date).year}-{getdate(year_end_date).year}", + "year_start_date": year_start_date, + "year_end_date": year_end_date, + "companies": [{"company": company}], + } + ).save() + return fy_doc + else: + fy_doc = frappe.get_doc("Fiscal Year", fy_docname) + if not frappe.db.exists("Fiscal Year Company", {"parent": fy_docname, "company": company}): + fy_doc.append("companies", {"company": company}) + fy_doc.save() + return fy_doc + + +def make_period_closing_voucher(company, cost_center, posting_date=None, submit=True): + from erpnext.accounts.doctype.account.test_account import create_account + + parent_account = frappe.db.get_value( + "Account", {"company": company, "account_name": "Current Liabilities", "is_group": 1}, "name" + ) + surplus_account = create_account( + account_name="Reserve and Surplus", + is_group=0, + company=company, + root_type="Liability", + report_type="Balance Sheet", + account_currency="INR", + parent_account=parent_account, + doctype="Account", + ) + pcv = frappe.get_doc( + { + "doctype": "Period Closing Voucher", + "transaction_date": posting_date or today(), + "posting_date": posting_date or today(), + "company": company, + "fiscal_year": get_fiscal_year(today(), company=company)[0], + "cost_center": cost_center, + "closing_account_head": surplus_account, + "remarks": "test", + } + ) + pcv.insert() + if submit: + pcv.submit() + + return pcv From 75babd4c18fc555046750a2b640d0ac1f4f435bd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 17 Sep 2024 14:38:26 +0530 Subject: [PATCH 3/6] fix: ignore repost logic on Payment Reconciliation --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 5 +++++ erpnext/accounts/utils.py | 2 ++ erpnext/controllers/accounts_controller.py | 7 ------- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 6d2351934898..d0ab80ccf834 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -195,6 +195,11 @@ def on_submit(self): self.update_booked_depreciation() def on_update_after_submit(self): + # Flag will be set on Reconciliation + # Reconciliation tool will anyways repost ledger entries. So, no need to check and do implicit repost. + if self.flags.get("ignore_reposting_on_reconciliation"): + return + self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []}) if self.needs_repost: self.validate_for_repost() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5c475e8a80fe..d4dfb19eb255 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -685,6 +685,8 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): # will work as update after submit journal_entry.flags.ignore_validate_update_after_submit = True + # Ledgers will be reposted by Reconciliation tool + journal_entry.flags.ignore_reposting_on_reconciliation = True if not do_not_save: journal_entry.save(ignore_permissions=True) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 778f93f043bc..4aa1d236fa43 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3570,13 +3570,6 @@ def check_if_child_table_updated(child_table_before_update, child_table_after_up # Check if any field affecting accounting entry is altered for index, item in enumerate(child_table_before_update): - if item.parenttype == "Journal Entry" and any( - [ - child_table_after_update[index].get(i) != item.get(i) - for i in ["account", "party_type", "party", "debit", "credit"] - ] - ): - continue for field in fields_to_check: if child_table_after_update[index].get(field) != item.get(field): return True From f45638015f8bce4efe7cea3f0035c060d42a3426 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 17 Sep 2024 16:49:03 +0530 Subject: [PATCH 4/6] refactor(test): make use existing test data and dynamic fy creation --- .../test_payment_reconciliation.py | 51 +++++-------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 65d7d42ef3da..b229c9c2c40d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1849,31 +1849,19 @@ def test_cr_note_payment_limit_filter(self): def test_reconciliation_on_closed_period_payment(self): from erpnext.accounts.doctype.account.test_account import create_account - self.create_company() - self.create_cost_center() - - # create bank account - parent_account = frappe.db.get_value( - "Account", {"company": self.company, "account_name": "Bank Accounts", "is_group": 1}, "name" - ) - bank_account = create_account( - account_name="Bank Account", - account_type="Bank", - is_group=0, - company=self.company, - root_type="Asset", - report_type="Balance Sheet", - account_currency="INR", - parent_account=parent_account, - doctype="Account", - ) + # Get current fiscal year + current_fy_start_date = get_fiscal_year(today())[1] # create backdated fiscal year - create_fiscal_year(company=self.company, year_start_date="1990-04-01", year_end_date="1991-03-31") + prev_fy_start_date = add_days(current_fy_start_date, -366) + prev_fy_end_date = add_days(current_fy_start_date, -1) + create_fiscal_year( + company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date + ) # make journal entry for previous year je_1 = frappe.new_doc("Journal Entry") - je_1.posting_date = "1990-06-01" + je_1.posting_date = add_days(prev_fy_start_date, 20) je_1.company = self.company je_1.user_remark = "test" je_1.set( @@ -1888,28 +1876,24 @@ def test_reconciliation_on_closed_period_payment(self): "credit_in_account_currency": 1000, }, { - "account": bank_account, - "cost_center": self.sub_cc, + "account": self.bank, + "cost_center": self.sub_cc.name, "credit_in_account_currency": 0, "debit_in_account_currency": 500, }, { - "account": "Cash - _PR", - "cost_center": self.sub_cc, + "account": self.cash, + "cost_center": self.sub_cc.name, "credit_in_account_currency": 0, "debit_in_account_currency": 500, }, ], ) - je_1.save() je_1.submit() - je_1.reload() - # check journal entry is submitted - self.assertTrue(je_1.docstatus == 1) # make period closing voucher pcv = make_period_closing_voucher( - company=self.company, cost_center=self.cost_center, posting_date="1991-03-31" + company=self.company, cost_center=self.cost_center, posting_date=prev_fy_end_date ) pcv.reload() # check if period closing voucher is completed @@ -1921,11 +1905,7 @@ def test_reconciliation_on_closed_period_payment(self): ) je_2.accounts[0].party_type = "Customer" je_2.accounts[0].party = self.customer - je_2.save() je_2.submit() - je_2.reload() - # check journal entry is submitted - self.assertTrue(je_2.docstatus == 1) # process reconciliation on closed period payment pr = self.create_payment_reconciliation(party_is_customer=True) @@ -1942,11 +1922,6 @@ def test_reconciliation_on_closed_period_payment(self): self.assertEqual(pr.get("invoices"), []) self.assertEqual(pr.get("payments"), []) - # cancel created entires during test - pcv.cancel() - je_1.cancel() - je_2.cancel() - def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From 768bb0312aae3ad67191778f09fe75f4c914a019 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 17 Sep 2024 17:21:26 +0530 Subject: [PATCH 5/6] refactor: update formatting changes --- .../payment_reconciliation/test_payment_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index b229c9c2c40d..f317f3399db0 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1996,7 +1996,7 @@ def make_period_closing_voucher(company, cost_center, posting_date=None, submit= "transaction_date": posting_date or today(), "posting_date": posting_date or today(), "company": company, - "fiscal_year": get_fiscal_year(today(), company=company)[0], + "fiscal_year": get_fiscal_year(posting_date or today(), company=company)[0], "cost_center": cost_center, "closing_account_head": surplus_account, "remarks": "test", From 720a330617c7cb5410bee715dbb4a4111af879f7 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 17 Sep 2024 18:55:28 +0530 Subject: [PATCH 6/6] fix: create fiscal year without overlapping existing Fiscal Years --- .../test_payment_reconciliation.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index f317f3399db0..883c638398c5 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -5,7 +5,7 @@ import frappe from frappe import qb from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, getdate, nowdate, today +from frappe.utils import add_days, add_years, flt, getdate, nowdate, today from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -1847,14 +1847,10 @@ def test_cr_note_payment_limit_filter(self): self.assertEqual(len(pr.payments), 1) def test_reconciliation_on_closed_period_payment(self): - from erpnext.accounts.doctype.account.test_account import create_account - - # Get current fiscal year - current_fy_start_date = get_fiscal_year(today())[1] - # create backdated fiscal year - prev_fy_start_date = add_days(current_fy_start_date, -366) - prev_fy_end_date = add_days(current_fy_start_date, -1) + first_fy_start_date = frappe.db.get_value("Fiscal Year", {"disabled": 0}, "min(year_start_date)") + prev_fy_start_date = add_years(first_fy_start_date, -1) + prev_fy_end_date = add_days(first_fy_start_date, -1) create_fiscal_year( company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date )