fix(stock): drop unused serial balance from Stock Ledger (backport #59392)

Stock Ledger no longer computes an unused serial balance.

(cherry picked from commit b78c2bc509)
This commit is contained in:
Mihir Kandoi
2026-10-15 14:13:51 +05:30
parent cf0aa72cae
commit 050702a7fc
2 changed files with 13 additions and 33 deletions
@@ -13,8 +13,6 @@ from pypika import Order
from pypika.analytics import RowNumber
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for
from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter
from erpnext.stock.utils import (
is_reposting_item_valuation_in_progress,
@@ -65,8 +63,6 @@ def execute(filters=None):
actual_qty = opening_rows[0].get("qty_after_transaction", 0)
stock_value = opening_rows[0].get("stock_value", 0)
available_serial_nos = {}
batch_balance_dict = frappe._dict({})
if actual_qty and filters.get("batch_no"):
batch_balance_dict[filters.batch_no] = [actual_qty, stock_value]
@@ -109,9 +105,6 @@ def execute(filters=None):
sle.update({"in_qty": max(sle.actual_qty, 0), "out_qty": min(sle.actual_qty, 0)})
if sle.serial_no:
update_available_serial_nos(available_serial_nos, sle)
if sle.actual_qty < 0:
sle["in_out_rate"] = flt(sle.stock_value_difference / sle.actual_qty, precision)
sle["incoming_rate"] = 0
@@ -258,32 +251,6 @@ def get_serial_batch_bundle_details(sl_entries, filters=None):
return _bundle_details
def update_available_serial_nos(available_serial_nos, sle):
serial_nos = get_serial_nos(sle.serial_no)
key = (sle.item_code, sle.warehouse)
if key not in available_serial_nos:
stock_balance = get_stock_balance_for(
sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time
)
serials = get_serial_nos(stock_balance["serial_nos"]) if stock_balance["serial_nos"] else []
available_serial_nos.setdefault(key, serials)
existing_serial_no = available_serial_nos[key]
for sn in serial_nos:
if sle.actual_qty > 0:
if sn in existing_serial_no:
existing_serial_no.remove(sn)
else:
existing_serial_no.append(sn)
else:
if sn in existing_serial_no:
existing_serial_no.remove(sn)
else:
existing_serial_no.append(sn)
sle.balance_serial_no = "\n".join(existing_serial_no)
def get_columns(filters):
columns = [
{"label": _("Date"), "fieldname": "date", "fieldtype": "Datetime", "width": 150},
@@ -2,11 +2,13 @@
# See license.txt
import frappe
from frappe.core.doctype.user_permission.test_user_permission import create_user
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, today
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.stock.serial_batch_bundle import get_serial_nos_from_bundle
WAREHOUSE = "Stores - _TC"
@@ -81,6 +83,17 @@ class TestStockLedgerReport(FrappeTestCase):
issue = next(row for row in rows if row.get("out_qty"))
self.assertEqual(issue["qty_after_transaction"], 6)
def test_stock_user_can_view_rows_with_serial_nos_as_text(self):
item = "_Test Serialized Item With Series"
entry = make_stock_entry(item_code=item, qty=1, to_warehouse=WAREHOUSE, basic_rate=100)
serial_no = get_serial_nos_from_bundle(entry.items[0].serial_and_batch_bundle)[0]
frappe.db.set_value("Stock Ledger Entry", {"voucher_no": entry.name}, "serial_no", serial_no)
with self.set_user(create_user("_test_stock_ledger_user@example.com", "Stock User").name):
rows = self.run_report(item)
self.assertIn(entry.name, [row.get("voucher_no") for row in rows])
def test_filters_to_requested_item_only(self):
item_a = "_Test Item"
item_b = "_Test Item 2"