From 074c84e8809a85aeb1f94be3432b78dcad0c8584 Mon Sep 17 00:00:00 2001 From: Afsal Syed <146159709+Afsalsyed@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:51:51 +0530 Subject: [PATCH] fix: persist redistributed additional costs during stock entry repost (#58433) * fix: persist redistributed additional costs during stock entry repost * test: cover additional cost persistence on stock entry recalculation --- .../test_repost_item_valuation.py | 52 ++++++++++++++++++- erpnext/stock/stock_ledger.py | 4 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 57da344d3f0..750b504f86d 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, call, patch import frappe -from frappe.utils import add_days, add_to_date, now, nowdate, today +from frappe.utils import add_days, add_to_date, flt, now, nowdate, today from erpnext.accounts import utils as accounts_utils from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice @@ -626,6 +626,56 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin): # incoming rate after reposting should be 150 self.assertSLEs(se, [{"incoming_rate": 150}]) + def test_recalculate_stock_entry_additional_cost_updates_all_incoming_rows(self): + from erpnext.stock.stock_ledger import update_entries_after + + company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company") + warehouse = "Stores - TCP1" + items = [ + self.make_item(f"_Test Repost Addl Cost {x}", {"is_stock_item": 1}).name for x in ("A", "B", "C") + ] + + for item_code in items: + make_stock_entry(item_code=item_code, target=warehouse, company=company, qty=100, rate=10) + + transfer = make_stock_entry(company=company, purpose="Material Transfer", do_not_save=True) + transfer.from_warehouse = warehouse + transfer.to_warehouse = warehouse + transfer.items = [] + for item_code in items: + transfer.append( + "items", + { + "item_code": item_code, + "qty": 100, + "s_warehouse": warehouse, + "t_warehouse": warehouse, + "uom": "Nos", + "conversion_factor": 1, + }, + ) + transfer.append( + "additional_costs", + { + "expense_account": "Expenses Included In Valuation - TCP1", + "description": "freight", + "amount": 100, + }, + ) + transfer.insert() + transfer.submit() + + first_row = transfer.items[0] + frappe.db.set_value("Stock Entry Detail", first_row.name, "basic_rate", first_row.basic_rate + 1) + update_entries_after.recalculate_amounts_in_stock_entry(MagicMock(), transfer.name, first_row.name) + + transfer.load_from_db() + detail_additional_cost = sum(row.additional_cost for row in transfer.items) + net_added_to_stock = sum(row.amount - row.basic_amount for row in transfer.items) + + self.assertEqual(flt(detail_additional_cost, 2), flt(transfer.total_additional_costs, 2)) + self.assertEqual(flt(net_added_to_stock, 2), flt(transfer.total_additional_costs, 2)) + def test_repost_multi_line_moving_average_return(self): from erpnext.controllers.sales_and_purchase_return import make_return_doc diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 20b67783634..a469acc7a06 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1597,12 +1597,14 @@ class update_entries_after: stock_entry = frappe.get_lazy_doc("Stock Entry", voucher_no, for_update=True) stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False) stock_entry.db_update() + update_additional_cost_rows = bool(stock_entry.get("additional_costs")) for d in stock_entry.items: - # Update only the row that matches the voucher_detail_no or the row containing the FG/Scrap Item. + # Additional costs are redistributed across all incoming rows. if ( d.name == voucher_detail_no or (not d.s_warehouse and d.t_warehouse) or stock_entry.purpose in ["Manufacture", "Repack"] + or (update_additional_cost_rows and d.t_warehouse) ): d.db_update()