fix: update weighted average rate calculation to consider returned and consumed quantities

(cherry picked from commit 35e55d3e13)
This commit is contained in:
ljain112
2026-06-15 14:16:07 +05:30
committed by Mergify
parent 6c37acc180
commit 35e06045bd
2 changed files with 55 additions and 4 deletions

View File

@@ -743,7 +743,14 @@ class SubcontractingInwardController:
"name": ["in", list(data.keys())],
"docstatus": 1,
},
fields=["rate", "name", "required_qty", "received_qty"],
fields=[
"rate",
"name",
"required_qty",
"received_qty",
"returned_qty",
"consumed_qty",
],
)
doc_updates = {}
@@ -751,13 +758,17 @@ class SubcontractingInwardController:
current_qty = flt(data[d.name].transfer_qty) * (1 if self._action == "submit" else -1)
current_rate = flt(data[d.name].rate)
# Calculate weighted average rate
old_total = d.rate * d.received_qty
# Weighted average rate must be computed on the on-hand balance
balance_qty = d.received_qty - d.returned_qty - d.consumed_qty
old_total = d.rate * balance_qty
current_total = current_rate * current_qty
new_balance_qty = balance_qty + current_qty
d.received_qty = d.received_qty + current_qty
d.rate = (
flt((old_total + current_total) / d.received_qty, precision) if d.received_qty else 0.0
flt((old_total + current_total) / new_balance_qty, precision)
if new_balance_qty > 0
else 0.0
)
if not d.required_qty and not d.received_qty:

View File

@@ -88,6 +88,46 @@ class IntegrationTestSubcontractingInwardOrder(ERPNextTestSuite):
self.assertEqual(received_item.received_qty, 5)
self.assertEqual(received_item.rate, 10)
def test_customer_provided_item_rate_with_return_between_receipts(self):
"""Weight the average rate on the on-hand balance, not gross received_qty.
Receive 10 @ 100, return 5, receive 6 @ 130:
balance-weighted (correct) = (5 * 100 + 6 * 130) / 11 = 116.36
gross-weighted (wrong) = (10 * 100 + 6 * 130) / 16 = 111.25
"""
so, scio = create_so_scio()
rm_item = "Basic RM"
def receive(qty, rate):
rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward())
rm_in.items = [item for item in rm_in.items if item.item_code == rm_item]
rm_in.items[0].qty = qty
rm_in.items[0].transfer_qty = qty
rm_in.items[0].basic_rate = rate
rm_in.submit()
scio.reload()
# Receipt 1: 10 @ 100
receive(10, 100)
received_item = next(item for item in scio.received_items if item.rm_item_code == rm_item)
self.assertEqual(received_item.rate, 100)
# Return 5 to the customer
rm_return = frappe.new_doc("Stock Entry").update(scio.make_rm_return())
rm_return.items = [item for item in rm_return.items if item.item_code == rm_item]
rm_return.items[0].qty = 5
rm_return.items[0].transfer_qty = 5
rm_return.submit()
scio.reload()
received_item = next(item for item in scio.received_items if item.rm_item_code == rm_item)
self.assertEqual(received_item.returned_qty, 5)
# Receipt 2: 6 @ 130 — must weight against the balance of 5, not gross 10
receive(6, 130)
received_item = next(item for item in scio.received_items if item.rm_item_code == rm_item)
self.assertAlmostEqual(received_item.rate, (5 * 100 + 6 * 130) / 11, places=2)
def test_add_extra_customer_provided_item(self):
so, scio = create_so_scio()