fix: use live source warehouse valuation for internal transfer purchse receipts (#56812)

fix: use live source warehouse valuation for internal transfer purchase receipts
This commit is contained in:
Shllokkk
2026-07-03 22:49:33 +05:30
committed by GitHub
parent 47121cecb3
commit ece1c9872f
2 changed files with 105 additions and 3 deletions

View File

@@ -1662,6 +1662,93 @@ class TestPurchaseReceipt(FrappeTestCase):
self.assertEqual(query[0].value, 0)
def test_internal_transfer_pr_incoming_sle_anchored_to_dn_rate(self):
"""Internal-transfer PR's inward SLE must use DN.incoming_rate even when
PR.item.valuation_rate was wrong at submit, so divisional_loss does not
leak to COGS."""
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.stock_ledger import update_entries_after
prepare_data_for_internal_transfer()
customer = "_Test Internal Customer 2"
company = "_Test Company with perpetual inventory"
from_warehouse = create_warehouse("_Test Drift From", company=company)
transit_warehouse = create_warehouse("_Test Drift Transit", company=company)
to_warehouse = create_warehouse("_Test Drift Receiver", company=company)
item_doc = create_item("Test Internal Drift Item")
make_purchase_receipt(
item_code=item_doc.name,
company=company,
posting_date=add_days(today(), -1),
warehouse=from_warehouse,
qty=10,
rate=100,
)
dn = create_delivery_note(
item_code=item_doc.name,
company=company,
customer=customer,
cost_center="Main - TCP1",
expense_account="Cost of Goods Sold - TCP1",
qty=1,
rate=100,
warehouse=from_warehouse,
target_warehouse=transit_warehouse,
)
self.assertEqual(flt(dn.items[0].incoming_rate), 100.0)
pr = make_inter_company_purchase_receipt(dn.name)
pr.items[0].warehouse = to_warehouse
pr.submit()
inward_sle = frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"warehouse": to_warehouse,
"is_cancelled": 0,
},
["name", "item_code", "warehouse", "posting_date", "posting_time", "creation", "incoming_rate"],
as_dict=True,
)
self.assertEqual(flt(inward_sle.incoming_rate), 100.0)
frappe.db.set_value(
"Purchase Receipt Item",
pr.items[0].name,
{"sales_incoming_rate": 0, "valuation_rate": 80},
)
frappe.db.set_value(
"Stock Ledger Entry",
inward_sle.name,
{"incoming_rate": 80, "stock_value_difference": 80},
)
update_entries_after(
{
"item_code": inward_sle.item_code,
"warehouse": inward_sle.warehouse,
"posting_date": inward_sle.posting_date,
"posting_time": inward_sle.posting_time,
"sle_id": inward_sle.name,
"creation": inward_sle.creation,
}
)
refreshed = frappe.db.get_value(
"Stock Ledger Entry",
inward_sle.name,
["incoming_rate", "stock_value_difference"],
as_dict=True,
)
self.assertEqual(flt(refreshed.incoming_rate), 100.0)
self.assertEqual(flt(refreshed.stock_value_difference), 100.0)
def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_invoice(
self,
):

View File

@@ -870,10 +870,16 @@ class update_entries_after:
if (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
and sle.actual_qty < 0
and is_internal_transfer(sle)
):
sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle)
# Anchor both legs of an internal-transfer PR/PI to the DN/SI incoming_rate;
# otherwise an inward SLE that inherits a stale PR.valuation_rate leaks the
# gap to COGS via divisional_loss.
rate = get_incoming_rate_for_inter_company_transfer(sle)
if sle.actual_qty < 0:
sle.outgoing_rate = rate
elif rate:
sle.incoming_rate = rate
dimensions = get_inventory_dimensions()
has_dimensions = False
@@ -2430,7 +2436,16 @@ def get_incoming_rate_for_inter_company_transfer(sle) -> float:
if lcv_amount:
lcv_rate = flt(lcv_amount / abs(sle.actual_qty))
return rate + lcv_rate
charges_rate = 0.0
if flt(sle.actual_qty) > 0:
charge_fields = ["item_tax_amount", "rm_supp_cost"]
charges = frappe.db.get_value(
f"{sle.voucher_type} Item", sle.voucher_detail_no, charge_fields, as_dict=True
)
if charges:
charges_rate = flt(sum(flt(charges.get(f)) for f in charge_fields)) / abs(sle.actual_qty)
return rate + lcv_rate + charges_rate
def is_internal_transfer(sle):