From a9330e8900c6384558bc2ff833ed8c5300eb525a Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 2 Jul 2026 12:25:52 +0530 Subject: [PATCH] fix: don't treat batch slot at FIFO queue head as qty slot An incoming SLE without resolvable serial/batch details hit the negative-head branch in _compute_incoming_stock even when the head was a batch slot, because flt() on the batch number string returns 0.0. _add_to_negative_fifo_head then crashed with "TypeError: can only concatenate str (not 'float') to str". Guard the branch with is_qty_slot, mirroring the existing check in _add_transfer_slot_to_fifo_queue. Co-Authored-By: Claude Fable 5 (cherry picked from commit c47a95a4d259863baa911f6161b5128df95e42d4) --- .../stock/report/stock_ageing/stock_ageing.py | 2 +- .../report/stock_ageing/test_stock_ageing.py | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 2e11fa1664b..b9ec3e7204f 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -492,7 +492,7 @@ class FIFOSlots: self._add_serial_fifo_slots(row, fifo_queue, serial_nos) elif batch_nos and row.get("has_batch_no"): self._add_batch_fifo_slots(row, fifo_queue, batch_nos) - elif fifo_queue and flt(fifo_queue[0][FIFO_QTY_INDEX]) <= 0: + elif fifo_queue and is_qty_slot(fifo_queue[0]) and flt(fifo_queue[0][FIFO_QTY_INDEX]) <= 0: self._add_to_negative_fifo_head(row, fifo_queue) else: fifo_queue.append([flt(row.actual_qty), row.posting_date, flt(row.stock_value_difference)]) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 003d1a51d93..b21a89572c4 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -1438,6 +1438,47 @@ class TestStockAgeing(FrappeTestCase): self.assertEqual(item_result["total_qty"], -4.0) self.assertEqual(item_result["fifo_queue"], [[batch_no, 1, -4.0, "2021-11-10", -40.0]]) + def test_untagged_receipt_with_negative_batch_head(self): + """An incoming SLE without batch details must not treat a negative + batch slot at the queue head as a qty slot (TypeError: str += float).""" + sle = [ + frappe._dict( + name="Enclosure Item", + actual_qty=-10, + qty_after_transaction=-10, + stock_value_difference=-100, + warehouse="WH 1", + posting_date="2021-12-01", + voucher_type="Stock Entry", + voucher_no="001", + has_serial_no=False, + has_batch_no=True, + serial_no=None, + batch_no="QI-06448", + ), + frappe._dict( + name="Enclosure Item", + actual_qty=45, + qty_after_transaction=35, + stock_value_difference=1051.65, + warehouse="WH 1", + posting_date="2021-12-05", + voucher_type="Purchase Receipt", + voucher_no="002", + has_serial_no=False, + serial_no=None, + batch_no=None, + serial_and_batch_bundle="SABB-00001294", + ), + ] + + slots = FIFOSlots(self.filters, sle).generate() + queue = slots["Enclosure Item"]["fifo_queue"] + + self.assertEqual(slots["Enclosure Item"]["total_qty"], 35.0) + self.assertEqual(queue[0][:3], ["QI-06448", None, -10.0]) + self.assertEqual(queue[1], [45.0, "2021-12-05", 1051.65]) + def test_batchwise_valuation_stock_reconciliation_with_bundle(self): from frappe.utils import add_days, getdate, nowdate