mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-26 22:18:18 +00:00
fix: rescale stock ageing FIFO slot values on stock reconciliation
A reconciliation's stock_value_difference includes the revaluation of stock already in the FIFO queue, but the whole amount was attached to the qty-delta slot while older slots kept pre-revaluation values. A downward revaluation therefore produced negative bucket values in the Stock Ageing report, and repeated recos let the queue total drift away from Stock Balance. Re-derive every slot value as qty * valuation_rate after processing a reco SLE, since a reconciliation values the entire balance at its rate. Covers both single-SLE recos and the zero-out/re-add pair that flows through the transfer bucket.
This commit is contained in:
@@ -370,6 +370,7 @@ class FIFOSlots:
|
||||
row, fifo_queue, transferred_item_key, serial_nos, batch_nos, from_end
|
||||
)
|
||||
|
||||
self._revalue_stock_reconciliation_slots(row, fifo_queue)
|
||||
self._update_balances(row, key)
|
||||
self._trim_serial_fifo_queue(row, key, fifo_queue)
|
||||
|
||||
@@ -393,6 +394,14 @@ class FIFOSlots:
|
||||
# Stock reconciliation stores the final balance; FIFO needs the movement delta.
|
||||
row.actual_qty = flt(row.qty_after_transaction) - flt(prev_balance_qty)
|
||||
|
||||
def _revalue_stock_reconciliation_slots(self, row: dict, fifo_queue: list) -> None:
|
||||
if row.voucher_type != "Stock Reconciliation" or row.has_serial_no or row.has_batch_no:
|
||||
return
|
||||
|
||||
for slot in fifo_queue:
|
||||
if is_qty_slot(slot):
|
||||
slot[FIFO_VALUE_INDEX] = flt(slot[FIFO_QTY_INDEX] * flt(row.valuation_rate))
|
||||
|
||||
def _get_serial_and_batch_nos(
|
||||
self, row: dict, bundle_wise_serial_nos: dict, bundle_wise_batch_nos: dict
|
||||
) -> tuple[list, list]:
|
||||
|
||||
@@ -383,6 +383,91 @@ class TestStockAgeing(FrappeTestCase):
|
||||
self.assertEqual(queue, [[60.0, "2025-11-30", 60.0], [30.0, "2026-01-31", 30.0]])
|
||||
self.assertEqual(report_data[0][7:15], [30.0, 30.0, 0.0, 0.0, 60.0, 60.0, 0.0, 0.0])
|
||||
|
||||
def test_stock_reco_revaluation_rescales_queue_values(self):
|
||||
"Ledger (same wh): [+15 @ 100, reco reset >> 20 @ 50]"
|
||||
sle = [
|
||||
frappe._dict(
|
||||
name="Flask Item",
|
||||
actual_qty=15,
|
||||
qty_after_transaction=15,
|
||||
stock_value_difference=1500,
|
||||
valuation_rate=100,
|
||||
warehouse="WH 1",
|
||||
posting_date="2021-12-01",
|
||||
voucher_type="Stock Entry",
|
||||
voucher_no="001",
|
||||
has_serial_no=False,
|
||||
serial_no=None,
|
||||
),
|
||||
frappe._dict(
|
||||
name="Flask Item",
|
||||
actual_qty=0,
|
||||
qty_after_transaction=20,
|
||||
stock_value_difference=(-500),
|
||||
valuation_rate=50,
|
||||
warehouse="WH 1",
|
||||
posting_date="2021-12-02",
|
||||
voucher_type="Stock Reconciliation",
|
||||
voucher_no="002",
|
||||
has_serial_no=False,
|
||||
serial_no=None,
|
||||
),
|
||||
]
|
||||
|
||||
slots = FIFOSlots(self.filters, sle).generate()
|
||||
queue = slots["Flask Item"]["fifo_queue"]
|
||||
|
||||
self.assertEqual(queue, [[15.0, "2021-12-01", 750.0], [5.0, "2021-12-02", 250.0]])
|
||||
|
||||
def test_stock_reco_with_split_out_and_in_sles_revalues_queue(self):
|
||||
"Ledger (same wh): [+10 @ 100, reco out >> 0, reco in >> 12 @ 2]"
|
||||
sle = [
|
||||
frappe._dict(
|
||||
name="Flask Item",
|
||||
actual_qty=10,
|
||||
qty_after_transaction=10,
|
||||
stock_value_difference=1000,
|
||||
valuation_rate=100,
|
||||
warehouse="WH 1",
|
||||
posting_date="2021-12-01",
|
||||
voucher_type="Stock Entry",
|
||||
voucher_no="001",
|
||||
has_serial_no=False,
|
||||
serial_no=None,
|
||||
),
|
||||
frappe._dict(
|
||||
name="Flask Item",
|
||||
actual_qty=(-10),
|
||||
qty_after_transaction=0,
|
||||
stock_value_difference=(-1000),
|
||||
valuation_rate=100,
|
||||
warehouse="WH 1",
|
||||
posting_date="2021-12-02",
|
||||
voucher_type="Stock Reconciliation",
|
||||
voucher_no="002",
|
||||
has_serial_no=False,
|
||||
serial_no=None,
|
||||
),
|
||||
frappe._dict(
|
||||
name="Flask Item",
|
||||
actual_qty=12,
|
||||
qty_after_transaction=12,
|
||||
stock_value_difference=24,
|
||||
valuation_rate=2,
|
||||
warehouse="WH 1",
|
||||
posting_date="2021-12-02",
|
||||
voucher_type="Stock Reconciliation",
|
||||
voucher_no="002",
|
||||
has_serial_no=False,
|
||||
serial_no=None,
|
||||
),
|
||||
]
|
||||
|
||||
slots = FIFOSlots(self.filters, sle).generate()
|
||||
queue = slots["Flask Item"]["fifo_queue"]
|
||||
|
||||
self.assertEqual(queue, [[10.0, "2021-12-01", 20.0], [2.0, "2021-12-02", 4.0]])
|
||||
|
||||
def test_sequential_stock_reco_same_warehouse(self):
|
||||
"""
|
||||
Test back to back stock recos (same warehouse).
|
||||
|
||||
Reference in New Issue
Block a user