mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-13 14:41:53 +00:00
fix: use live source warehouse valuation for internal transfer purchase receipts (backport #56431) (#56677)
fix: use live source warehouse valuation for internal transfer purchase receipts (#56431)
fix: anchor incoming SLE rate to DN rate for intra-company PR transfers
(cherry picked from commit 35de9deb0a)
Co-authored-by: Shllokkk <140623894+Shllokkk@users.noreply.github.com>
This commit is contained in:
@@ -1976,6 +1976,92 @@ class TestPurchaseReceipt(ERPNextTestSuite):
|
|||||||
|
|
||||||
self.assertEqual(query[0].value, 0)
|
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()
|
||||||
|
|
||||||
|
# Simulate the failure path
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Purchase Receipt Item",
|
||||||
|
pr.items[0].name,
|
||||||
|
{"sales_incoming_rate": 0, "valuation_rate": 80},
|
||||||
|
)
|
||||||
|
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"],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
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(
|
def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_invoice(
|
||||||
self,
|
self,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -877,10 +877,16 @@ class update_entries_after:
|
|||||||
if (
|
if (
|
||||||
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
||||||
and sle.voucher_detail_no
|
and sle.voucher_detail_no
|
||||||
and sle.actual_qty < 0
|
|
||||||
and is_internal_transfer(sle)
|
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()
|
dimensions = get_inventory_dimensions()
|
||||||
has_dimensions = False
|
has_dimensions = False
|
||||||
|
|||||||
Reference in New Issue
Block a user