diff --git a/erpnext/stock/report/test_reports.py b/erpnext/stock/report/test_reports.py new file mode 100644 index 000000000000..d7fb5b2bf3fa --- /dev/null +++ b/erpnext/stock/report/test_reports.py @@ -0,0 +1,63 @@ +import unittest +from typing import List, Tuple + +from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report + +DEFAULT_FILTERS = { + "company": "_Test Company", + "from_date": "2010-01-01", + "to_date": "2030-01-01", +} + + +REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [ + ("Stock Ledger", {"_optional": True}), + ("Stock Balance", {"_optional": True}), + ("Stock Projected Qty", {"_optional": True}), + ("Batch-Wise Balance History", {}), + ("Itemwise Recommended Reorder Level", {"item_group": "All Item Groups"}), + ("COGS By Item Group", {}), + ("Stock Qty vs Serial No Count", {"warehouse": "_Test Warehouse - _TC"}), + ( + "Stock and Account Value Comparison", + { + "company": "_Test Company with perpetual inventory", + "account": "Stock In Hand - TCP1", + "as_on_date": "2021-01-01", + }, + ), + ("Product Bundle Balance", {"date": "2022-01-01", "_optional": True}), + ( + "Stock Analytics", + { + "from_date": "2021-01-01", + "to_date": "2021-12-31", + "value_quantity": "Quantity", + "_optional": True, + }, + ), + ("Warehouse wise Item Balance Age and Value", {"_optional": True}), + ("Item Variant Details", {"item": "_Test Variant Item",}), + ("Total Stock Summary", {"group_by": "warehouse",}), + ("Batch Item Expiry Status", {}), + ("Stock Ageing", {"range1": 30, "range2": 60, "range3": 90, "_optional": True}), +] + +OPTIONAL_FILTERS = { + "warehouse": "_Test Warehouse - _TC", + "item": "_Test Item", + "item_group": "_Test Item Group", +} + + +class TestReports(unittest.TestCase): + def test_execute_all_stock_reports(self): + """Test that all script report in stock modules are executable with supported filters""" + for report, filter in REPORT_FILTER_TEST_CASES: + execute_script_report( + report_name=report, + module="Stock", + filters=filter, + default_filters=DEFAULT_FILTERS, + optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None, + ) diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index 2156bd51a4a9..a3cab4b59da7 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -3,8 +3,13 @@ import copy from contextlib import contextmanager +from typing import Any, Dict, NewType, Optional import frappe +from frappe.core.doctype.report.report import get_report_module_dotted_path + +ReportFilters = Dict[str, Any] +ReportName = NewType("ReportName", str) def create_test_contact_and_address(): @@ -78,3 +83,39 @@ def test_case(self): for key, value in previous_settings.items(): setattr(settings, key, value) settings.save() + + +def execute_script_report( + report_name: ReportName, + module: str, + filters: ReportFilters, + default_filters: Optional[ReportFilters] = None, + optional_filters: Optional[ReportFilters] = None + ): + """Util for testing execution of a report with specified filters. + + Tests the execution of report with default_filters + filters. + Tests the execution using optional_filters one at a time. + + Args: + report_name: Human readable name of report (unscrubbed) + module: module to which report belongs to + filters: specific values for filters + default_filters: default values for filters such as company name. + optional_filters: filters which should be tested one at a time in addition to default filters. + """ + + if default_filters is None: + default_filters = {} + + report_execute_fn = frappe.get_attr(get_report_module_dotted_path(module, report_name) + ".execute") + report_filters = frappe._dict(default_filters).copy().update(filters) + + report_data = report_execute_fn(report_filters) + + if optional_filters: + for key, value in optional_filters.items(): + filter_with_optional_param = report_filters.copy().update({key: value}) + report_execute_fn(filter_with_optional_param) + + return report_data