mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-16 07:58:38 +00:00
fix: keep source rate on bulk apply_price_list when maintain same rate is on
The single-row re-fetch guard skipped the bulk apply_price_list path, so changing the price list, party, or conversion rate on a mapped transaction re-fetched current Item Prices and overwrote the mapped rates, breaking the maintain-same-rate check on save. Guard apply_price_list_on_item with the same source-row lookup, and resolve the parent doctype via ctx.parenttype since the bulk path carries the child doctype in ctx.doctype.
This commit is contained in:
@@ -210,7 +210,10 @@ def get_rate_locked_source_row(ctx: ItemDetailsCtx, doc) -> frappe._dict | None:
|
||||
Order) can only be saved at the source rate. Fetching a newer Item Price here
|
||||
would set a rate the document can never be saved with, so keep the source rate.
|
||||
"""
|
||||
source_fields = maintain_same_rate_source_fields.get(ctx.doctype)
|
||||
if isinstance(doc, str):
|
||||
doc = json.loads(doc)
|
||||
|
||||
source_fields = maintain_same_rate_source_fields.get(ctx.parenttype or ctx.doctype)
|
||||
if not source_fields or not doc or ctx.get("is_return") or not maintain_same_rate_enabled(ctx):
|
||||
return None
|
||||
|
||||
@@ -221,7 +224,7 @@ def get_rate_locked_source_row(ctx: ItemDetailsCtx, doc) -> frappe._dict | None:
|
||||
|
||||
|
||||
def maintain_same_rate_enabled(ctx: ItemDetailsCtx) -> bool:
|
||||
if ctx.doctype in purchase_doctypes:
|
||||
if (ctx.parenttype or ctx.doctype) in purchase_doctypes:
|
||||
if ctx.get("is_internal_supplier"):
|
||||
return False
|
||||
return bool(cint(frappe.get_cached_value("Buying Settings", "None", "maintain_same_rate")))
|
||||
@@ -1694,7 +1697,14 @@ def apply_price_list(ctx: ItemDetailsCtx, as_doc: bool = False, doc: Document |
|
||||
|
||||
def apply_price_list_on_item(ctx, doc=None):
|
||||
item_doc = frappe.get_cached_doc("Item", ctx.item_code)
|
||||
item_details = get_price_list_rate(ctx, item_doc)
|
||||
|
||||
source_row = get_rate_locked_source_row(ctx, doc)
|
||||
if source_row:
|
||||
item_details = frappe._dict(
|
||||
price_list_rate=flt(source_row.get("price_list_rate")) or flt(source_row.get("rate"))
|
||||
)
|
||||
else:
|
||||
item_details = get_price_list_rate(ctx, item_doc)
|
||||
|
||||
ctx.conversion_factor = flt(ctx.conversion_factor) or get_conversion_factor(ctx.item_code, ctx.uom).get(
|
||||
"conversion_factor", 1
|
||||
|
||||
@@ -196,3 +196,66 @@ class TestGetItemDetail(ERPNextTestSuite):
|
||||
# Control: without the setting the newer Item Price would be fetched.
|
||||
set_maintain_same_rate(0)
|
||||
self.assertEqual(fetch_price_list_rate(), 120)
|
||||
|
||||
def test_apply_price_list_keeps_source_rate_when_maintain_same_rate(self):
|
||||
"""#57436: the bulk apply_price_list path (price list / party / conversion rate
|
||||
change) must also keep the source rate on mapped rows, not just re-fetch of a
|
||||
single row. Here a PR row carries its PO rate (175) while the current price list
|
||||
rate is 100; the bulk apply must keep 175.
|
||||
"""
|
||||
from frappe.utils import flt, nowdate
|
||||
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||
from erpnext.stock.get_item_details import apply_price_list
|
||||
|
||||
item_code = "_Test Item"
|
||||
price_list = "_Test Buying Price List"
|
||||
|
||||
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")
|
||||
|
||||
try:
|
||||
po = create_purchase_order(item_code=item_code, rate=175, qty=1)
|
||||
|
||||
row_name = "pr-row-1"
|
||||
pr_doc = {
|
||||
"doctype": "Purchase Receipt",
|
||||
"items": [
|
||||
{
|
||||
"name": row_name,
|
||||
"item_code": item_code,
|
||||
"purchase_order_item": po.items[0].name,
|
||||
"price_list_rate": 175,
|
||||
"rate": 175,
|
||||
}
|
||||
],
|
||||
}
|
||||
ctx = frappe._dict(
|
||||
doctype="Purchase Receipt",
|
||||
supplier=po.supplier,
|
||||
company=po.company,
|
||||
currency=po.currency,
|
||||
conversion_rate=1.0,
|
||||
price_list=price_list,
|
||||
plc_conversion_rate=1.0,
|
||||
transaction_date=nowdate(),
|
||||
items=[
|
||||
frappe._dict(
|
||||
doctype="Purchase Receipt Item",
|
||||
parenttype="Purchase Receipt",
|
||||
item_code=item_code,
|
||||
child_docname=row_name,
|
||||
qty=1,
|
||||
uom=po.items[0].uom,
|
||||
stock_uom=po.items[0].stock_uom,
|
||||
conversion_factor=1.0,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
result = apply_price_list(ctx, doc=pr_doc)
|
||||
self.assertEqual(flt(result["children"][0].get("price_list_rate")), 175)
|
||||
finally:
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", original)
|
||||
frappe.clear_cache(doctype="Buying Settings")
|
||||
|
||||
Reference in New Issue
Block a user