mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-26 04:18:35 +00:00
fix: update weighted average rate calculation to consider returned and consumed quantities
(cherry picked from commit 35e55d3e13)
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user