mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-15 23:48:38 +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())],
|
"name": ["in", list(data.keys())],
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
},
|
},
|
||||||
fields=["rate", "name", "required_qty", "received_qty"],
|
fields=[
|
||||||
|
"rate",
|
||||||
|
"name",
|
||||||
|
"required_qty",
|
||||||
|
"received_qty",
|
||||||
|
"returned_qty",
|
||||||
|
"consumed_qty",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
doc_updates = {}
|
doc_updates = {}
|
||||||
@@ -751,13 +758,17 @@ class SubcontractingInwardController:
|
|||||||
current_qty = flt(data[d.name].transfer_qty) * (1 if self._action == "submit" else -1)
|
current_qty = flt(data[d.name].transfer_qty) * (1 if self._action == "submit" else -1)
|
||||||
current_rate = flt(data[d.name].rate)
|
current_rate = flt(data[d.name].rate)
|
||||||
|
|
||||||
# Calculate weighted average rate
|
# Weighted average rate must be computed on the on-hand balance
|
||||||
old_total = d.rate * d.received_qty
|
balance_qty = d.received_qty - d.returned_qty - d.consumed_qty
|
||||||
|
old_total = d.rate * balance_qty
|
||||||
current_total = current_rate * current_qty
|
current_total = current_rate * current_qty
|
||||||
|
|
||||||
|
new_balance_qty = balance_qty + current_qty
|
||||||
d.received_qty = d.received_qty + current_qty
|
d.received_qty = d.received_qty + current_qty
|
||||||
d.rate = (
|
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:
|
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.received_qty, 5)
|
||||||
self.assertEqual(received_item.rate, 10)
|
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):
|
def test_add_extra_customer_provided_item(self):
|
||||||
so, scio = create_so_scio()
|
so, scio = create_so_scio()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user