diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 6fac45ea255..6282782ccde 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -4590,6 +4590,87 @@ class TestPurchaseReceipt(IntegrationTestCase): for row in gl_entries: self.assertTrue(row.account in ["Stock In Hand - TCP1", account]) + def test_lcv_for_repack_entry(self): + from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( + create_landed_cost_voucher, + ) + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + for item in [ + "Potatoes Raw Material Item", + "Fries Finished Goods Item", + ]: + create_item(item) + + pr = make_purchase_receipt( + item_code="Potatoes Raw Material Item", + warehouse="_Test Warehouse - _TC", + qty=100, + rate=50, + ) + + wh1 = create_warehouse("WH A1", company=pr.company) + wh2 = create_warehouse("WH A2", company=pr.company) + + ste = make_stock_entry( + purpose="Repack", + source="_Test Warehouse - _TC", + item_code="Potatoes Raw Material Item", + qty=100, + company=pr.company, + do_not_save=1, + ) + + ste.append( + "items", + { + "item_code": "Fries Finished Goods Item", + "qty": 50, + "t_warehouse": wh1, + }, + ) + + ste.append( + "items", + { + "item_code": "Fries Finished Goods Item", + "qty": 50, + "t_warehouse": wh2, + }, + ) + + ste.insert() + ste.submit() + ste.reload() + + for row in ste.items: + if row.t_warehouse: + self.assertEqual(row.valuation_rate, 50) + + sles = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_type": ste.doctype, "voucher_no": ste.name, "actual_qty": (">", 0)}, + pluck="stock_value_difference", + ) + + self.assertEqual(sles, [2500.0, 2500.0]) + + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2000 * -1) + + ste.reload() + + for row in ste.items: + if row.t_warehouse: + self.assertEqual(row.valuation_rate, 30) + + sles = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_type": ste.doctype, "voucher_no": ste.name, "actual_qty": (">", 0)}, + pluck="stock_value_difference", + ) + + self.assertEqual(sles, [1500.0, 1500.0]) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2c9e57afecb..6d14290fea0 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -655,10 +655,32 @@ class update_entries_after: if sle.dependant_sle_voucher_detail_no: entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle) + if sle.voucher_type == "Stock Entry" and is_repack_entry(sle.voucher_no): + # for repack entries, we need to repost both source and target warehouses + self.update_distinct_item_warehouses_for_repack(sle) if self.exceptions: self.raise_exceptions() + def update_distinct_item_warehouses_for_repack(self, sle): + sles = ( + frappe.get_all( + "Stock Ledger Entry", + filters={ + "voucher_type": "Stock Entry", + "voucher_no": sle.voucher_no, + "actual_qty": (">", 0), + "is_cancelled": 0, + "voucher_detail_no": ("!=", sle.dependant_sle_voucher_detail_no), + }, + fields=["*"], + ) + or [] + ) + + for dependant_sle in sles: + self.update_distinct_item_warehouses(dependant_sle) + def has_stock_reco_with_serial_batch(self, sle): if ( sle.voucher_type == "Stock Reconciliation" @@ -1162,7 +1184,11 @@ class update_entries_after: def get_dynamic_incoming_outgoing_rate(self, sle): # Get updated incoming/outgoing rate from transaction - if sle.recalculate_rate or self.has_landed_cost_based_on_pi(sle): + if ( + sle.recalculate_rate + or self.has_landed_cost_based_on_pi(sle) + or (sle.voucher_type == "Stock Entry" and sle.actual_qty > 0 and is_repack_entry(sle.voucher_no)) + ): rate = self.get_incoming_outgoing_rate_from_transaction(sle) if flt(sle.actual_qty) >= 0: @@ -2428,3 +2454,8 @@ def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj, company): incoming_rate = abs(flt(sn_obj.batch_avg_rate.get(row.batch_no))) return incoming_rate + + +@frappe.request_cache +def is_repack_entry(stock_entry_id): + return frappe.get_cached_value("Stock Entry", stock_entry_id, "purpose") == "Repack"