From 36e5945c667595811688bd9eeaa07af9ecc5ab08 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 13 Sep 2024 16:48:32 +0530 Subject: [PATCH 1/3] fix: fetch cost center allocation percentage only from the applicable allocation --- erpnext/accounts/general_ledger.py | 63 ++++++++++++++++-------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 84217da1a6be..0b91b7bedc51 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -180,50 +180,53 @@ def process_gl_map(gl_map, merge_entries=True, precision=None): def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None): - cost_center_allocation = get_cost_center_allocation_data(gl_map[0]["company"], gl_map[0]["posting_date"]) - if not cost_center_allocation: - return gl_map - new_gl_map = [] for d in gl_map: cost_center = d.get("cost_center") # Validate budget against main cost center validate_expense_against_budget(d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)) - - if cost_center and cost_center_allocation.get(cost_center): - for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items(): - gle = copy.deepcopy(d) - gle.cost_center = sub_cost_center - for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"): - gle[field] = flt(flt(d.get(field)) * percentage / 100, precision) - new_gl_map.append(gle) - else: + cost_center_allocation = get_cost_center_allocation_data( + gl_map[0]["company"], gl_map[0]["posting_date"], cost_center + ) + if not cost_center_allocation: new_gl_map.append(d) + continue + + for sub_cost_center, percentage in cost_center_allocation: + gle = copy.deepcopy(d) + gle.cost_center = sub_cost_center + for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"): + gle[field] = flt(flt(d.get(field)) * percentage / 100, precision) + new_gl_map.append(gle) return new_gl_map -def get_cost_center_allocation_data(company, posting_date): - par = frappe.qb.DocType("Cost Center Allocation") - child = frappe.qb.DocType("Cost Center Allocation Percentage") +def get_cost_center_allocation_data(company, posting_date, cost_center): + cost_center_allocation = frappe.db.get_value( + "Cost Center Allocation", + { + "docstatus": 1, + "company": company, + "valid_from": ("<=", posting_date), + "main_cost_center": cost_center, + }, + pluck="name", + order_by="valid_from desc", + ) - records = ( - frappe.qb.from_(par) - .inner_join(child) - .on(par.name == child.parent) - .select(par.main_cost_center, child.cost_center, child.percentage) - .where(par.docstatus == 1) - .where(par.company == company) - .where(par.valid_from <= posting_date) - .orderby(par.valid_from, order=frappe.qb.desc) - ).run(as_dict=True) + if not cost_center_allocation: + return [] - cc_allocation = frappe._dict() - for d in records: - cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(d.cost_center, d.percentage) + records = frappe.db.get_all( + "Cost Center Allocation Percentage", + {"parent": cost_center_allocation}, + ["cost_center", "percentage"], + as_list=True, + ) - return cc_allocation + return records def merge_similar_entries(gl_map, precision=None): From 4d5d6150e134a3af99cbd4a9b5e3eb61c87dd636 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 13 Sep 2024 16:54:29 +0530 Subject: [PATCH 2/3] test: add unit test for validating multiple cost center allocation with different child cost center --- .../test_cost_center_allocation.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py index 65784dbb6c72..c333bbbf1305 100644 --- a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py +++ b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py @@ -24,6 +24,7 @@ def setUp(self): "Main Cost Center 2", "Sub Cost Center 1", "Sub Cost Center 2", + "Sub Cost Center 3", ] for cc in cost_centers: create_cost_center(cost_center_name=cc, company="_Test Company") @@ -141,6 +142,49 @@ def test_valid_from_based_on_existing_gle(self): jv.cancel() + def test_multiple_cost_center_allocation_on_same_main_cost_center(self): + create_cost_center_allocation( + "_Test Company", + "Main Cost Center 1 - _TC", + {"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40}, + valid_from=add_days(today(), -5), + ) + + create_cost_center_allocation( + "_Test Company", + "Main Cost Center 1 - _TC", + {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}, + valid_from=add_days(today(), -1), + ) + + jv = make_journal_entry( + "Cash - _TC", + "Sales - _TC", + 100, + cost_center="Main Cost Center 1 - _TC", + posting_date=today(), + submit=True, + ) + + expected_values = {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50} + + gle = frappe.qb.DocType("GL Entry") + gl_entries = ( + frappe.qb.from_(gle) + .select(gle.cost_center, gle.debit, gle.credit) + .where(gle.voucher_type == "Journal Entry") + .where(gle.voucher_no == jv.name) + .where(gle.account == "Sales - _TC") + .orderby(gle.cost_center) + ).run(as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertTrue(gle.cost_center in expected_values) + self.assertEqual(gle.debit, 0) + self.assertEqual(gle.credit, expected_values[gle.cost_center]) + def create_cost_center_allocation( company, From 3c65b98b49d2e7760c4bc01fcc0221f77a529a2b Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 13 Sep 2024 18:46:10 +0530 Subject: [PATCH 3/3] fix: cancel cost center allocation and journal entry after test --- .../test_cost_center_allocation.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py index c333bbbf1305..4abc82d8becd 100644 --- a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py +++ b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py @@ -22,6 +22,7 @@ def setUp(self): cost_centers = [ "Main Cost Center 1", "Main Cost Center 2", + "Main Cost Center 3", "Sub Cost Center 1", "Sub Cost Center 2", "Sub Cost Center 3", @@ -37,7 +38,7 @@ def test_gle_based_on_cost_center_allocation(self): ) jv = make_journal_entry( - "_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True + "Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True ) expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]] @@ -121,7 +122,7 @@ def test_total_percentage(self): def test_valid_from_based_on_existing_gle(self): # GLE posted against Sub Cost Center 1 on today jv = make_journal_entry( - "_Test Cash - _TC", + "Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", @@ -143,16 +144,16 @@ def test_valid_from_based_on_existing_gle(self): jv.cancel() def test_multiple_cost_center_allocation_on_same_main_cost_center(self): - create_cost_center_allocation( + coa1 = create_cost_center_allocation( "_Test Company", - "Main Cost Center 1 - _TC", + "Main Cost Center 3 - _TC", {"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40}, valid_from=add_days(today(), -5), ) - create_cost_center_allocation( + coa2 = create_cost_center_allocation( "_Test Company", - "Main Cost Center 1 - _TC", + "Main Cost Center 3 - _TC", {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}, valid_from=add_days(today(), -1), ) @@ -161,7 +162,7 @@ def test_multiple_cost_center_allocation_on_same_main_cost_center(self): "Cash - _TC", "Sales - _TC", 100, - cost_center="Main Cost Center 1 - _TC", + cost_center="Main Cost Center 3 - _TC", posting_date=today(), submit=True, ) @@ -185,6 +186,10 @@ def test_multiple_cost_center_allocation_on_same_main_cost_center(self): self.assertEqual(gle.debit, 0) self.assertEqual(gle.credit, expected_values[gle.cost_center]) + coa1.cancel() + coa2.cancel() + jv.cancel() + def create_cost_center_allocation( company,