From 529ff251fb774e89b130fa75f30268d1c558b4f2 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 8 Aug 2026 11:01:21 +0530 Subject: [PATCH 1/2] fix: incorrect entry detection in Stock Ledger Invariant Check (#57886) (cherry picked from commit b3f97cd38965c08fa10a27216fea03a45cd2684d) --- .../stock_ledger_invariant_check.py | 15 ++++++++-- .../test_stock_ledger_invariant_check.py | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index ffb024acfb1..7827203ae92 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -7,6 +7,8 @@ import frappe from frappe import _ from frappe.utils import cint, flt, get_link_to_form, parse_json +from erpnext.stock.utils import get_valuation_method + SLE_FIELDS = ( "name", "posting_date", @@ -53,6 +55,9 @@ def add_invariant_check_fields(sles, filters): balance_qty = 0.0 balance_stock_value = 0.0 + company = frappe.get_cached_value("Warehouse", filters.warehouse, "company") + valuation_method = get_valuation_method(filters.item_code, company) + incorrect_idx = None float_precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) or 3 currency_precision = ( @@ -90,7 +95,7 @@ def add_invariant_check_fields(sles, filters): ) sle.diff_value_diff = sle.stock_value_from_diff - sle.stock_value - if maintains_fifo_queue(sle): + if maintains_fifo_queue(sle, valuation_method): add_fifo_fields(sle, sles[idx - 1] if idx else None) if incorrect_idx is None and not is_sle_has_correct_data(sle, float_precision, currency_precision): @@ -104,8 +109,10 @@ def add_invariant_check_fields(sles, filters): return sles -def maintains_fifo_queue(sle): - # no queue is maintained for serialized/batchwise-valued stock +def maintains_fifo_queue(sle, valuation_method): + if valuation_method == "Moving Average": + return False + return not ( sle.serial_and_batch_bundle or sle.serial_no or (sle.batch_no and sle.use_batchwise_valuation) ) @@ -138,6 +145,8 @@ def is_sle_has_correct_data(sle, float_precision, currency_precision): return ( flt(sle.difference_in_qty, float_precision) == 0.0 and flt(sle.diff_value_diff, currency_precision) == 0.0 + and flt(sle.fifo_qty_diff, float_precision) == 0.0 + and flt(sle.fifo_value_diff, currency_precision) == 0.0 ) diff --git a/erpnext/stock/report/stock_ledger_invariant_check/test_stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/test_stock_ledger_invariant_check.py index b82e341c84a..67382375862 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/test_stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/test_stock_ledger_invariant_check.py @@ -1,6 +1,8 @@ # Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt +import json + import frappe from frappe.tests.utils import FrappeTestCase @@ -60,6 +62,34 @@ class TestStockLedgerInvariantCheck(FrappeTestCase): self.assertEqual(len(data), 2) # incorrect entry + one before it for context self.assertEqual(data[-1].name, sle.name) + def test_show_incorrect_entries_catches_queue_mismatch(self): + item = self.make_movements() + + sle = frappe.get_last_doc( + "Stock Ledger Entry", {"item_code": item, "warehouse": WAREHOUSE, "is_cancelled": 0} + ) + tampered_queue = json.dumps([[sle.qty_after_transaction + 5, 100]]) + frappe.db.set_value("Stock Ledger Entry", sle.name, "stock_queue", tampered_queue) + + data = self.run_report(item_code=item, show_incorrect_entries=1) + self.assertEqual(len(data), 2) + self.assertEqual(data[-1].name, sle.name) + + def test_moving_average_item_skips_fifo_queue_checks(self): + from erpnext.stock.doctype.item.test_item import make_item + + item = make_item(properties={"valuation_method": "Moving Average"}).name + make_stock_entry(item_code=item, to_warehouse=WAREHOUSE, qty=10, rate=100) + make_stock_entry(item_code=item, from_warehouse=WAREHOUSE, qty=4) + + data = self.run_report(item_code=item) + self.assertTrue(data) + for row in data: + self.assertIsNone(row.fifo_qty_diff) + self.assertIsNone(row.fifo_value_diff) + + self.assertEqual(self.run_report(item_code=item, show_incorrect_entries=1), []) + def test_batch_item_skips_fifo_queue_checks(self): item = make_item( properties={"has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "SLIC-BAT-.####"} From c45ea3545db88f00619bb3d5de9941a32723c2b3 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 8 Aug 2026 11:24:44 +0530 Subject: [PATCH 2/2] fix: get_valuation_method takes no company argument on version-15 --- .../stock_ledger_invariant_check.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index 7827203ae92..ca5e3eec3c8 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -55,8 +55,7 @@ def add_invariant_check_fields(sles, filters): balance_qty = 0.0 balance_stock_value = 0.0 - company = frappe.get_cached_value("Warehouse", filters.warehouse, "company") - valuation_method = get_valuation_method(filters.item_code, company) + valuation_method = get_valuation_method(filters.item_code) incorrect_idx = None float_precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) or 3