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 <noreply@anthropic.com>
(cherry picked from commit c47a95a4d2)
This commit is contained in:
Mihir Kandoi
2026-07-02 12:25:52 +05:30
committed by Mergify
parent 71a2d6e43d
commit a9330e8900
2 changed files with 42 additions and 1 deletions

View File

@@ -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)])

View File

@@ -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