fix(stock): preserve rates for unsaved mapped rows (backport #58852) (backport #58898) (#58916)

fix(stock): preserve rates for unsaved mapped rows (backport #58852) (#58898)

* fix: keep each row's own rate under maintain same rate

* test: cover maintain-same-rate rate lock for batch items

(cherry picked from commit 32d8c359ed)

Co-authored-by: Shllokkk <140623894+Shllokkk@users.noreply.github.com>
This commit is contained in:
mergify[bot]
2026-09-09 14:53:20 +05:30
committed by GitHub
parent fdc48277a8
commit 0deed27cfe
2 changed files with 67 additions and 1 deletions

View File

@@ -192,7 +192,11 @@ def get_rate_locked_source_row(args, doc):
if not source_fields or not doc or args.get("is_return") or not maintain_same_rate_enabled(args):
return None
row = next((d for d in doc.get("items") or [] if d.get("name") == args.child_docname), None)
row = (
next((d for d in doc.get("items") or [] if d.get("name") == args.child_docname), None)
if args.child_docname
else args
)
if not row:
return None

View File

@@ -422,3 +422,65 @@ class TestGetItemDetail(FrappeTestCase):
frappe.set_user("Administrator")
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", original)
frappe.clear_cache(doctype="Buying Settings")
def test_rate_lock_keeps_each_rows_rate_for_batch_items(self):
"""Batch rows mapped PR->PI must each keep their own rate, not collapse onto the first."""
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
get_batch_from_bundle,
)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
original = frappe.db.get_single_value("Buying Settings", "maintain_same_rate")
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
frappe.clear_cache(doctype="Buying Settings")
self.addCleanup(frappe.clear_cache, doctype="Buying Settings")
self.addCleanup(frappe.db.set_single_value, "Buying Settings", "maintain_same_rate", original)
def batched_item():
code = make_item(
properties={
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "FBQ-.#####",
}
).name
se = make_stock_entry(item_code=code, target="_Test Warehouse - _TC", qty=5, basic_rate=100)
return code, get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
item_a, batch_a = batched_item()
item_b, batch_b = batched_item()
# one PO with both items at different rates
po = create_purchase_order(item_code=item_a, qty=5, rate=28, do_not_save=True)
po.append(
"items",
{
"item_code": item_b,
"qty": 5,
"rate": 275,
"warehouse": "_Test Warehouse - _TC",
"schedule_date": frappe.utils.nowdate(),
},
)
po.set_missing_values()
po.insert()
po.submit()
# receive both against their own batches
pr = make_purchase_receipt(po.name)
for row in pr.items:
row.use_serial_batch_fields = 1
pr.items[0].batch_no = batch_a
pr.items[1].batch_no = batch_b
pr.insert()
pr.submit()
# the batch_no branch force-writes the fetched rate during mapping
pi = make_purchase_invoice(pr.name)
self.assertEqual(pi.items[0].rate, 28)
self.assertEqual(pi.items[1].rate, 275) # used to collapse onto the first row (28)