Merge pull request #56544 from frappe/chore/test-stock-and-account-value-comparison

test: Stock and Account Value Comparison report coverage
This commit is contained in:
Nabin Hait
2026-07-01 20:30:25 +05:30
committed by GitHub

View File

@@ -6,30 +6,87 @@ from frappe.utils import today
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison import (
create_reposting_entries,
execute,
)
from erpnext.tests.utils import ERPNextTestSuite
PI_COMPANY = "_Test Company with perpetual inventory"
COMPANY = "_Test Company with perpetual inventory"
PI_STORES = "Stores - TCP1"
class TestStockAndAccountValueComparison(ERPNextTestSuite):
def test_balanced_warehouse_not_flagged(self):
warehouse = create_warehouse("_Test SAVC WH", company=COMPANY)
account = frappe.get_value("Warehouse", warehouse, "account")
item = "_Test Item"
make_stock_entry(
item_code=item,
to_warehouse=warehouse,
qty=10,
rate=100,
company=COMPANY,
posting_date="2026-06-01",
)
# Filtering by the isolated account restricts both the stock-ledger and GL
# scans to this fresh warehouse's account only.
rows = self.run_report(account=account)
# The report lists only mismatches (rows where abs(difference_value) > 0.1),
# keyed per voucher. A balanced perpetual warehouse posts equal stock-ledger
# and GL values for the receipt voucher, so nothing should be flagged.
self.assertEqual(rows, [])
def test_stock_account_gl_mismatch_is_flagged(self):
warehouse = create_warehouse("_Test SAVC Mismatch WH", company=COMPANY)
account = frappe.get_value("Warehouse", warehouse, "account")
receipt = make_stock_entry(
item_code="_Test Item",
to_warehouse=warehouse,
qty=10,
rate=100,
company=COMPANY,
posting_date="2026-06-01",
)
# Simulate corruption: the stock-account GL entry for this receipt drifts out of sync
# with the stock ledger (stock value stays 1000, but the account only shows 600).
frappe.db.set_value(
"GL Entry",
{"voucher_no": receipt.name, "account": account, "is_cancelled": 0},
"debit_in_account_currency",
600,
update_modified=False,
)
rows = self.run_report(account=account)
row = next((r for r in rows if r["voucher_no"] == receipt.name), None)
self.assertIsNotNone(row, "Tampered GL entry should cause the voucher to appear in the report")
self.assertEqual(row["ledger_type"], "Stock Ledger Entry")
self.assertEqual(row["stock_value"], 1000) # unchanged stock ledger value
self.assertEqual(row["account_value"], 600) # tampered GL value
self.assertEqual(row["difference_value"], 400) # 1000 - 600, above the 0.1 threshold
def test_purchase_voucher_reposted_transaction_based(self):
# A Purchase Receipt whose GL entries are missing must surface in the report and, when reposted
# from it, be reposted Transaction-based (so its own GL is regenerated) rather than the slower
# Item-and-Warehouse based reposting.
item = make_item(properties={"is_stock_item": 1, "valuation_method": "FIFO"}).name
pr = make_purchase_receipt(item_code=item, company=PI_COMPANY, warehouse=PI_STORES, qty=5, rate=100)
pr = make_purchase_receipt(item_code=item, company=COMPANY, warehouse=PI_STORES, qty=5, rate=100)
# Simulate the out-of-sync state: stock ledger exists but the accounting ledger does not.
frappe.db.delete("GL Entry", {"voucher_type": "Purchase Receipt", "voucher_no": pr.name})
# The receipt now shows up in the comparison report (stock value 500 vs account value 0).
filters = frappe._dict(company=PI_COMPANY, as_on_date=today())
filters = frappe._dict(company=COMPANY, as_on_date=today())
_columns, data = execute(filters)
row = next((d for d in data if d.get("voucher_no") == pr.name), None)
@@ -37,7 +94,7 @@ class TestStockAndAccountValueComparison(ERPNextTestSuite):
self.assertEqual(row.get("voucher_type"), "Purchase Receipt")
# Repost from the report.
create_reposting_entries([row], PI_COMPANY)
create_reposting_entries([row], COMPANY)
# A Transaction-based Repost Item Valuation must have been created for this voucher...
transaction_rivs = frappe.get_all(
@@ -55,3 +112,8 @@ class TestStockAndAccountValueComparison(ERPNextTestSuite):
filters={"based_on": "Item and Warehouse", "item_code": item},
)
self.assertFalse(item_wh_rivs, "Purchase vouchers must not be reposted Item-and-Warehouse based")
def run_report(self, **extra):
filters = {"company": COMPANY, "as_on_date": "2026-12-31"}
filters.update(extra)
return execute(frappe._dict(filters))[1]