diff --git a/tests/test_business_facts.py b/tests/test_business_facts.py index 8fb32e7..54e2c82 100644 --- a/tests/test_business_facts.py +++ b/tests/test_business_facts.py @@ -28,6 +28,91 @@ class BusinessFactsTests(unittest.TestCase): def tearDown(self): self.tempdir.cleanup() + def seed_template_fixture(self): + sync_kingdee_orders( + self.vault, + [ + { + "id": "1001", + "billno": "SO-001", + "bizdate": "2026-06-03", + "customer_number": "C01", + "customer_name": "Customer A", + "totalallamount": "100.00", + "settlecurrency_number": "CNY", + "billstatus": "C", + "orderstatus": "D", + "closestatus": "A", + "org_name": "Org A", + "dept_name": "Dept A", + "operator_operatorname": "徐丽", + "billentry": [ + { + "id": "L1", + "material_masterid_number": "P01", + "material_masterid_name": "Part 1", + "qty": "2", + "amountandtax": "60", + "project_name": "Project A", + "warehouse_name": "Warehouse A", + "orderdeliverentry": [ + { + "id": "D1", + "d_plandate": "2026-06-10", + "d_plandeliverydate": "2026-06-08", + "d_planqty": "2", + } + ], + }, + { + "id": "L2", + "material_masterid_number": "P02", + "material_masterid_name": "Part 2", + "qty": "1", + "amountandtax": "40", + "project_name": "Project B", + "warehouse_name": "Warehouse B", + }, + ], + }, + { + "id": "1002", + "billno": "SO-002", + "bizdate": "2026-06-04", + "customer_number": "C02", + "customer_name": "Customer B", + "totalallamount": "300.00", + "settlecurrency_number": "CNY", + "billstatus": "B", + "orderstatus": "C", + "closestatus": "A", + "org_name": "Org B", + "dept_name": "Dept B", + "operator_operatorname": "王五", + "h5a3_basedatafield2_operatorname": "徐丽", + "billentry": [ + { + "id": "L1", + "material_masterid_number": "P01", + "material_masterid_name": "Part 1", + "qty": "3", + "amountandtax": "300", + "project_name": "Project A", + "warehouse_name": "Warehouse A", + "orderdeliverentry": [ + { + "id": "D2", + "d_plandate": "2026-06-11", + "d_planqty": "3", + } + ], + } + ], + }, + ], + synced_at="2026-07-06T09:00:00Z", + ) + def test_monthly_order_count_deduplicates_order_lines(self): sync_kingdee_orders( self.vault, @@ -523,89 +608,7 @@ class BusinessFactsTests(unittest.TestCase): self.assertIn("raw_snapshots", schema["sql_guardrails"]["forbidden_tables"]) def test_execute_business_templates_cover_core_totals_rankings_lists_and_delivery_plans(self): - sync_kingdee_orders( - self.vault, - [ - { - "id": "1001", - "billno": "SO-001", - "bizdate": "2026-06-03", - "customer_number": "C01", - "customer_name": "Customer A", - "totalallamount": "100.00", - "settlecurrency_number": "CNY", - "billstatus": "C", - "orderstatus": "D", - "closestatus": "A", - "org_name": "Org A", - "dept_name": "Dept A", - "operator_operatorname": "徐丽", - "billentry": [ - { - "id": "L1", - "material_masterid_number": "P01", - "material_masterid_name": "Part 1", - "qty": "2", - "amountandtax": "60", - "project_name": "Project A", - "warehouse_name": "Warehouse A", - "orderdeliverentry": [ - { - "id": "D1", - "d_plandate": "2026-06-10", - "d_plandeliverydate": "2026-06-08", - "d_planqty": "2", - } - ], - }, - { - "id": "L2", - "material_masterid_number": "P02", - "material_masterid_name": "Part 2", - "qty": "1", - "amountandtax": "40", - "project_name": "Project B", - "warehouse_name": "Warehouse B", - }, - ], - }, - { - "id": "1002", - "billno": "SO-002", - "bizdate": "2026-06-04", - "customer_number": "C02", - "customer_name": "Customer B", - "totalallamount": "300.00", - "settlecurrency_number": "CNY", - "billstatus": "B", - "orderstatus": "C", - "closestatus": "A", - "org_name": "Org B", - "dept_name": "Dept B", - "operator_operatorname": "王五", - "h5a3_basedatafield2_operatorname": "徐丽", - "billentry": [ - { - "id": "L1", - "material_masterid_number": "P01", - "material_masterid_name": "Part 1", - "qty": "3", - "amountandtax": "300", - "project_name": "Project A", - "warehouse_name": "Warehouse A", - "orderdeliverentry": [ - { - "id": "D2", - "d_plandate": "2026-06-11", - "d_planqty": "3", - } - ], - } - ], - }, - ], - synced_at="2026-07-06T09:00:00Z", - ) + self.seed_template_fixture() count = execute_business_template(self.db_path, "order_count", start_date="2026-06-01", end_date="2026-07-01") sales = execute_business_template(self.db_path, "sales_amount", year=2026, month=6) @@ -641,6 +644,40 @@ class BusinessFactsTests(unittest.TestCase): [("B", 1), ("C", 1)], ) + def test_all_business_templates_execute_with_fixture_data(self): + self.seed_template_fixture() + required_values = { + "customer": "Customer A", + "product": "P01", + "staff": "徐丽", + } + templates = business_schema()["templates"] + template_names = [template["name"] for template in templates] + + for template in templates: + params = { + "year": 2026, + "month": 6, + "limit": 5, + "granularity": "day", + "staff_role_scope": "all", + } + for required in template["required_parameters"]: + params[required] = required_values[required] + + with self.subTest(template=template["name"]): + result = execute_business_template(self.db_path, template["name"], **params) + self.assertEqual(result["template"], template["name"]) + self.assertIsInstance(result["data"], dict) + self.assertIn("answer", result) + self.assertEqual(result["last_sync"], "2026-07-06T09:00:00Z") + self.assertIsInstance(result["is_stale"], bool) + if "rows" in result: + self.assertIsInstance(result["rows"], list) + json.dumps(result, ensure_ascii=False) + + self.assertEqual(len(template_names), len(set(template_names))) + def test_execute_business_template_rejects_unknown_or_missing_parameters(self): with self.assertRaisesRegex(RuntimeError, "unknown business template"): execute_business_template(self.db_path, "not_a_template", year=2026, month=6)