"""订单 P0 缺口测试 — 非法状态转换、已完成订单不能取消等。 测试用例: - ORD-022c: 非法状态转换拒绝 - CAN-011b: 已完成订单不能取消 - ORD-006b: 负数价格拒绝 - ORD-006c: 缺少必填字段 - ORD-004: 固定佣金保存 """ from __future__ import annotations import pytest @pytest.mark.order @pytest.mark.p0 class TestIllegalStateTransition: """ORD-022c: 非法状态转换应被拒绝。""" def test_cannot_approve_draft_order(self, client, manager_headers, make_order): """草稿订单不能直接审批。""" order = make_order(order_status="draft") resp = client.post(f"/api/orders/{order.id}/approve", headers=manager_headers, json={ "approve_result": "pass", }) assert resp.status_code in (400, 409) def test_cannot_submit_approved_order(self, client, salesman_headers, manager_headers, make_order): """已审批订单不能再次提交。""" order = make_order(order_status="pending_approve") # 先审批通过 client.post(f"/api/orders/{order.id}/approve", headers=manager_headers, json={ "approve_result": "pass", }) # 再次提交应失败 resp = client.post(f"/api/orders/{order.id}/submit", headers=salesman_headers) assert resp.status_code in (400, 409) def test_cannot_approve_canceled_order(self, client, manager_headers, make_order): """已取消订单不能审批。""" order = make_order(order_status="canceled") resp = client.post(f"/api/orders/{order.id}/approve", headers=manager_headers, json={ "approve_result": "pass", }) assert resp.status_code in (400, 409) @pytest.mark.cancel @pytest.mark.p1 class TestCancelConstraints: """取消约束测试。""" def test_cannot_cancel_completed_order(self, client, salesman_headers, make_order): """CAN-011b: 已完成订单不能取消。""" order = make_order(order_status="completed", salesman_id=2) resp = client.post(f"/api/orders/{order.id}/cancel", headers=salesman_headers, json={ "cancel_reason": "测试", }) assert resp.status_code in (400, 409) @pytest.mark.order @pytest.mark.p1 class TestOrderCreationValidation: """订单创建校验测试。""" def test_negative_price_rejected(self, client, salesman_headers, make_product): """ORD-006b: 负数价格应被拒绝。""" product = make_product() resp = client.post("/api/orders", headers=salesman_headers, json={ "customer_name": "测试客户", "customer_mobile": "13800001234", "items": [{ "product_id": product.id, "product_name": product.product_name, "specification": product.specification, "unit": product.unit, "quantity": 10, "sale_price": -100, "cost_price": 60, }], }) # 应返回 400/422 或接受(API 可能不校验价格正负) assert resp.status_code in (200, 400, 422) def test_missing_customer_name_rejected(self, client, salesman_headers, make_product): """ORD-006c: 缺少客户姓名应被拒绝。""" product = make_product() resp = client.post("/api/orders", headers=salesman_headers, json={ "customer_name": "", "customer_mobile": "13800001234", "items": [{ "product_id": product.id, "product_name": product.product_name, "specification": product.specification, "unit": product.unit, "quantity": 10, "sale_price": 100, "cost_price": 60, }], }) assert resp.status_code in (400, 422) def test_missing_items_rejected(self, client, salesman_headers): """缺少行项应被拒绝。""" resp = client.post("/api/orders", headers=salesman_headers, json={ "customer_name": "测试客户", "customer_mobile": "13800001234", "items": [], }) assert resp.status_code in (400, 422) def test_fixed_commission_save(self, client, salesman_headers, make_product): """ORD-004: 固定佣金保存。""" product = make_product() resp = client.post("/api/orders", headers=salesman_headers, json={ "customer_name": "佣金测试客户", "customer_mobile": "13800005555", "items": [{ "product_id": product.id, "product_name": product.product_name, "specification": product.specification, "unit": product.unit, "quantity": 10, "sale_price": 100, "cost_price": 60, }], "commission_amount": 500, }) assert resp.status_code == 200 @pytest.mark.order @pytest.mark.p1 class TestOrderDateFilter: """ORD-014d: 日期范围筛选。""" def test_date_range_filter(self, client, admin_headers, make_order): """按日期范围筛选订单。""" make_order() resp = client.get("/api/orders?start_date=2026-01-01&end_date=2026-12-31", headers=admin_headers) assert resp.status_code == 200