From 8955a1edb44bdfcd748686ca29c7a429e64d7008 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Jun 2026 17:34:31 +0530 Subject: [PATCH] test: add correctness coverage for Stock Ledger report The Stock Ledger report had a test stub with no assertions. Add tests for in/out quantity split and running balance, opening-balance roll-up from movements before the period, and item filtering, sharing a small make_movements/run_report helper. --- .../stock_ledger/test_stock_ledger_report.py | 88 ++++++++++++++++--- 1 file changed, 78 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py b/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py index 8a97a64d1b3..526afc42d6d 100644 --- a/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py +++ b/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py @@ -4,18 +4,86 @@ import frappe from frappe.utils import add_days, today -from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import ( - make_serial_item_with_serial, -) +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.report.stock_ledger.stock_ledger import execute from erpnext.tests.utils import ERPNextTestSuite +WAREHOUSE = "_Test Warehouse - _TC" -class TestStockLedgerReeport(ERPNextTestSuite): - def setUp(self) -> None: - make_serial_item_with_serial(self, "_Test Stock Report Serial Item") - self.filters = frappe._dict( + +class TestStockLedgerReport(ERPNextTestSuite): + """Correctness tests for the Stock Ledger report. + + A shared `make_movements`/`run` pair keeps each test small without persisting + any data: movements are created per test and rolled back, while the report runs + read-only. + """ + + def make_movements(self, item_code, movements): + for movement in movements: + make_stock_entry(item_code=item_code, **movement) + + def run_report(self, item_code, from_date=None, to_date=None): + filters = frappe._dict( company="_Test Company", - from_date=today(), - to_date=add_days(today(), 30), - item_code=["_Test Stock Report Serial Item"], + from_date=from_date or add_days(today(), -1), + to_date=to_date or today(), + item_code=[item_code], + warehouse=WAREHOUSE, ) + return list(execute(filters)[1]) + + def test_in_out_quantities_and_running_balance(self): + item = make_item().name + self.make_movements( + item, + [ + {"qty": 10, "to_warehouse": WAREHOUSE, "basic_rate": 100}, + {"qty": 4, "from_warehouse": WAREHOUSE}, + ], + ) + + rows = self.run_report(item) + receipt = next(row for row in rows if row.get("in_qty")) + issue = next(row for row in rows if row.get("out_qty")) + + self.assertEqual(receipt["in_qty"], 10) + self.assertEqual(receipt["qty_after_transaction"], 10) + self.assertEqual(issue["out_qty"], -4) + self.assertEqual(issue["qty_after_transaction"], 6) + + def test_opening_balance_reflects_movements_before_from_date(self): + item = make_item().name + self.make_movements( + item, + [ + { + "qty": 10, + "to_warehouse": WAREHOUSE, + "basic_rate": 100, + "posting_date": add_days(today(), -10), + }, + {"qty": 4, "from_warehouse": WAREHOUSE, "posting_date": today()}, + ], + ) + + rows = self.run_report(item, from_date=add_days(today(), -5), to_date=today()) + + # the receipt predates the range, so it surfaces as the opening balance + self.assertEqual(rows[0]["item_code"], "'Opening'") + self.assertEqual(rows[0]["qty_after_transaction"], 10) + + # the in-range issue draws down from the opening balance + issue = next(row for row in rows if row.get("out_qty")) + self.assertEqual(issue["qty_after_transaction"], 6) + + def test_filters_to_requested_item_only(self): + item_a = make_item().name + item_b = make_item().name + self.make_movements(item_a, [{"qty": 5, "to_warehouse": WAREHOUSE, "basic_rate": 100}]) + self.make_movements(item_b, [{"qty": 7, "to_warehouse": WAREHOUSE, "basic_rate": 100}]) + + rows = self.run_report(item_a) + item_codes = {row["item_code"] for row in rows if row.get("voucher_no")} + self.assertEqual(item_codes, {item_a})