mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-13 17:20:36 +00:00
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:
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user