diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 73bdfd5b419..39c046fb689 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -5,7 +5,13 @@ from unittest.mock import patch import frappe -from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_report_data, get_average_age +from erpnext.stock.report.stock_ageing.stock_ageing import ( + BATCH_SLOT_QTY_INDEX, + BATCH_SLOT_VALUE_INDEX, + FIFOSlots, + format_report_data, + get_average_age, +) from erpnext.tests.utils import ERPNextTestSuite @@ -670,6 +676,53 @@ class TestStockAgeing(ERPNextTestSuite): ], ) + def test_batch_pooling_preserves_total_on_repeating_rate(self): + """Ledger (same wh, batch B): [+3 @ 100/3, +6 @ 0, +2 @ 0] + The pooled rate does not terminate, so assert the redistributed + slot values still add back to the batch total.""" + from erpnext.stock.doctype.item.test_item import make_item + + item_code = make_item( + "Test Stock Ageing Batch Pool Residual", + {"is_stock_item": 1, "has_batch_no": 1, "valuation_method": "FIFO"}, + ).name + + batch_no = "SA-POOL-RESIDUAL-BATCH" + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc({"doctype": "Batch", "batch_id": batch_no, "item": item_code}).insert( + ignore_permissions=True + ) + frappe.db.set_value("Batch", batch_no, "use_batchwise_valuation", 1) + + def make_sle(posting_date, voucher_no, actual_qty, qty_after, stock_value_difference): + return frappe._dict( + name=item_code, + actual_qty=actual_qty, + qty_after_transaction=qty_after, + stock_value_difference=stock_value_difference, + valuation_rate=abs(stock_value_difference / actual_qty) if actual_qty else 0, + warehouse="WH 1", + posting_date=posting_date, + voucher_type="Stock Entry", + voucher_no=voucher_no, + has_serial_no=False, + has_batch_no=True, + serial_no=None, + batch_no=batch_no, + ) + + sle = [ + make_sle("2021-12-01", "001", 3, 3, 100), + make_sle("2021-12-02", "002", 6, 9, 0), + make_sle("2021-12-03", "003", 2, 11, 0), + ] + + slots = FIFOSlots(self.filters, sle).generate() + queue = slots[item_code]["fifo_queue"] + + self.assertEqual([slot[BATCH_SLOT_QTY_INDEX] for slot in queue], [3.0, 6.0, 2.0]) + self.assertEqual(sum(slot[BATCH_SLOT_VALUE_INDEX] for slot in queue), 100.0) + def test_batch_issue_at_pooled_rate_keeps_slot_values_positive(self): """Ledger (same wh, batch B): [+10 @ 0, +10 @ 10, -4 @ pooled 5] Consuming the zero-valued head slot at the pooled rate drives it