From 25bcd12e923039c64e34bd4a7f8317907d2a90b0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Jun 2026 10:32:51 +0530 Subject: [PATCH 1/3] test: add coverage for Delivery Note Trends report Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test_delivery_note_trends.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py diff --git a/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py new file mode 100644 index 00000000000..003b481f85e --- /dev/null +++ b/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py @@ -0,0 +1,59 @@ +# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe + +from erpnext.tests.utils import ERPNextTestSuite + + +class TestDeliveryNoteTrends(ERPNextTestSuite): + def run_report(self, **extra): + from erpnext.stock.report.delivery_note_trends.delivery_note_trends import execute + + filters = frappe._dict( + { + "company": "_Test Company", + "fiscal_year": "_Test Fiscal Year 2026", + "period": "Yearly", + "based_on": "Item", + } + ) + filters.update(extra) + return execute(filters)[1] + + def test_delivery_qty_in_trend(self): + # based_on="Item" + period="Yearly": each row is + # [item_code, item_name, currency, yearly_qty, yearly_amt, total_qty, total_amt]. + # A submitted Delivery Note of qty 5 @ rate 200 should sum to qty 5 / amount 1000 + # (base_net_amount) in both the yearly bucket and the Total columns. + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + item = make_item(properties={"is_stock_item": 1, "is_sales_item": 1}).name + + make_stock_entry( + item_code=item, + to_warehouse="_Test Warehouse - _TC", + qty=20, + rate=100, + posting_date="2026-06-01", + ) + create_delivery_note( + item_code=item, + qty=5, + rate=200, + company="_Test Company", + posting_date="2026-06-01", + ) + + data = self.run_report() + + item_rows = [row for row in data if row[0] == item] + self.assertEqual(len(item_rows), 1) + + row = item_rows[0] + self.assertEqual(row[3], 5) # yearly qty bucket + self.assertEqual(row[4], 1000) # yearly amount bucket (base_net_amount) + self.assertEqual(row[5], 5) # Total(Qty) + self.assertEqual(row[6], 1000) # Total(Amt) From 78de0c976a9761fab9ae1b187239807cecd0093f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Jun 2026 13:26:32 +0530 Subject: [PATCH 2/3] test: reuse BootStrapTestData master data to reduce runtime Co-Authored-By: Claude Opus 4.8 (1M context) --- .../delivery_note_trends/test_delivery_note_trends.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py index 003b481f85e..f08fe643648 100644 --- a/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py @@ -27,22 +27,23 @@ class TestDeliveryNoteTrends(ERPNextTestSuite): # A submitted Delivery Note of qty 5 @ rate 200 should sum to qty 5 / amount 1000 # (base_net_amount) in both the yearly bucket and the Total columns. from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note - from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry - item = make_item(properties={"is_stock_item": 1, "is_sales_item": 1}).name + item = "_Test Item" make_stock_entry( item_code=item, - to_warehouse="_Test Warehouse - _TC", + to_warehouse="Stores - _TC", qty=20, rate=100, posting_date="2026-06-01", ) create_delivery_note( item_code=item, + warehouse="Stores - _TC", qty=5, rate=200, + customer="_Test Customer", company="_Test Company", posting_date="2026-06-01", ) From f2adb64f3bc87979053c73d3327ab18c288b0c9b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Jun 2026 14:50:54 +0530 Subject: [PATCH 3/3] test: cover period, based_on and group_by filters in Delivery Note Trends --- .../test_delivery_note_trends.py | 128 +++++++++++++----- 1 file changed, 97 insertions(+), 31 deletions(-) diff --git a/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py index f08fe643648..f5f62c5bde2 100644 --- a/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/test_delivery_note_trends.py @@ -3,58 +3,124 @@ import frappe +from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.report.delivery_note_trends.delivery_note_trends import execute from erpnext.tests.utils import ERPNextTestSuite +ITEM = "_Test Item" +WAREHOUSE = "Stores - _TC" +CUSTOMER = "_Test Customer" + class TestDeliveryNoteTrends(ERPNextTestSuite): - def run_report(self, **extra): - from erpnext.stock.report.delivery_note_trends.delivery_note_trends import execute - + def run_report_full(self, **extra): filters = frappe._dict( { "company": "_Test Company", "fiscal_year": "_Test Fiscal Year 2026", "period": "Yearly", "based_on": "Item", + "group_by": "", } ) filters.update(extra) - return execute(filters)[1] + columns, data = execute(filters)[:2] + return columns, data - def test_delivery_qty_in_trend(self): - # based_on="Item" + period="Yearly": each row is - # [item_code, item_name, currency, yearly_qty, yearly_amt, total_qty, total_amt]. - # A submitted Delivery Note of qty 5 @ rate 200 should sum to qty 5 / amount 1000 - # (base_net_amount) in both the yearly bucket and the Total columns. - from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note - from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + # trend columns are "Label:fieldtype:width" strings; assert by label so the index + # stays correct across period / based_on / group_by combinations. + @staticmethod + def labels(columns): + return [c.split(":")[0] if isinstance(c, str) else c.get("label") for c in columns] - item = "_Test Item" + def find_row(self, columns, data, match): + labels = self.labels(columns) + for row in data: + if all(row[labels.index(label)] == value for label, value in match.items()): + return row + return None + def value(self, columns, row, label): + if not row: + return 0 + return row[self.labels(columns).index(label)] or 0 + + def values(self, match, wanted_labels, **extra): + columns, data = self.run_report_full(**extra) + row = self.find_row(columns, data, match) + return {label: self.value(columns, row, label) for label in wanted_labels} + + def deliver(self, qty=5, rate=200, customer=CUSTOMER, posting_date="2026-06-01"): + # stock the item first so the delivery note can ship, then deliver make_stock_entry( - item_code=item, - to_warehouse="Stores - _TC", - qty=20, - rate=100, - posting_date="2026-06-01", + item_code=ITEM, to_warehouse=WAREHOUSE, qty=qty + 10, rate=100, posting_date=posting_date ) create_delivery_note( - item_code=item, - warehouse="Stores - _TC", - qty=5, - rate=200, - customer="_Test Customer", + item_code=ITEM, + warehouse=WAREHOUSE, + qty=qty, + rate=rate, + customer=customer, company="_Test Company", - posting_date="2026-06-01", + posting_date=posting_date, ) - data = self.run_report() + def test_delivery_qty_in_trend(self): + # A Delivery Note of qty 5 @ rate 200 sums to qty 5 / amount 1000 (base_net_amount) + # in the yearly bucket and the Total columns. + cols = ["_Test Fiscal Year 2026 (Qty)", "_Test Fiscal Year 2026 (Amt)", "Total(Qty)", "Total(Amt)"] + before = self.values({"Item": ITEM}, cols) + self.deliver() + after = self.values({"Item": ITEM}, cols) + self.assertEqual(after[cols[0]] - before[cols[0]], 5) + self.assertEqual(after[cols[1]] - before[cols[1]], 1000) + self.assertEqual(after["Total(Qty)"] - before["Total(Qty)"], 5) + self.assertEqual(after["Total(Amt)"] - before["Total(Amt)"], 1000) - item_rows = [row for row in data if row[0] == item] - self.assertEqual(len(item_rows), 1) + def test_monthly_period_buckets(self): + cols = ["Jun (Qty)", "Jun (Amt)", "Total(Qty)", "Total(Amt)"] + before = self.values({"Item": ITEM}, cols, period="Monthly") + self.deliver(posting_date="2026-06-01") + after = self.values({"Item": ITEM}, cols, period="Monthly") + # the June delivery lands only in the June bucket, and rolls up into the Total columns + self.assertEqual(after["Jun (Qty)"] - before["Jun (Qty)"], 5) + self.assertEqual(after["Jun (Amt)"] - before["Jun (Amt)"], 1000) + self.assertEqual(after["Total(Qty)"] - before["Total(Qty)"], 5) + self.assertEqual(after["Total(Amt)"] - before["Total(Amt)"], 1000) - row = item_rows[0] - self.assertEqual(row[3], 5) # yearly qty bucket - self.assertEqual(row[4], 1000) # yearly amount bucket (base_net_amount) - self.assertEqual(row[5], 5) # Total(Qty) - self.assertEqual(row[6], 1000) # Total(Amt) + def test_quarterly_period_buckets(self): + # 2026-06-01 falls in the Apr-Jun quarter + cols = ["Apr-Jun (Qty)", "Apr-Jun (Amt)", "Total(Qty)"] + before = self.values({"Item": ITEM}, cols, period="Quarterly") + self.deliver(posting_date="2026-06-01") + after = self.values({"Item": ITEM}, cols, period="Quarterly") + self.assertEqual(after["Apr-Jun (Qty)"] - before["Apr-Jun (Qty)"], 5) + self.assertEqual(after["Apr-Jun (Amt)"] - before["Apr-Jun (Amt)"], 1000) + self.assertEqual(after["Total(Qty)"] - before["Total(Qty)"], 5) + + def test_based_on_customer(self): + cols = ["Total(Qty)", "Total(Amt)"] + before = self.values({"Customer": CUSTOMER}, cols, based_on="Customer") + self.deliver(customer=CUSTOMER) + after = self.values({"Customer": CUSTOMER}, cols, based_on="Customer") + self.assertEqual(after["Total(Qty)"] - before["Total(Qty)"], 5) + self.assertEqual(after["Total(Amt)"] - before["Total(Amt)"], 1000) + + def test_based_on_territory(self): + territory = frappe.db.get_value("Customer", CUSTOMER, "territory") + cols = ["Total(Qty)", "Total(Amt)"] + before = self.values({"Territory": territory}, cols, based_on="Territory") + self.deliver(customer=CUSTOMER) + after = self.values({"Territory": territory}, cols, based_on="Territory") + self.assertEqual(after["Total(Qty)"] - before["Total(Qty)"], 5) + self.assertEqual(after["Total(Amt)"] - before["Total(Amt)"], 1000) + + def test_group_by_item_under_customer(self): + # based_on=Customer with group_by=Item produces an item-wise breakdown row + cols = ["Total(Qty)", "Total(Amt)"] + before = self.values({"Item": ITEM}, cols, based_on="Customer", group_by="Item") + self.deliver(customer=CUSTOMER) + after = self.values({"Item": ITEM}, cols, based_on="Customer", group_by="Item") + self.assertEqual(after["Total(Qty)"] - before["Total(Qty)"], 5) + self.assertEqual(after["Total(Amt)"] - before["Total(Amt)"], 1000)