"""定价引擎测试 — 覆盖报价计算、定价规则CRUD、供应商成本、价格层级。 测试用例: - PRICE-001 ~ PRICE-007: 定价规则 CRUD - PRICE-010 ~ PRICE-020: 报价计算(面积/重量/按件/按米、附加费、税、条件公式) - PRICE-030 ~ PRICE-033: 供应商成本 CRUD - PRICE-040 ~ PRICE-043: 价格层级 CRUD """ from __future__ import annotations import json import pytest # ============================================================ # 定价引擎单元测试(直接调用 PricingEngine,不走 API) # ============================================================ @pytest.mark.pricing class TestPricingEngineUnit: """定价引擎核心计算逻辑的单元测试。""" def _make_rule(self, **overrides): """创建模拟定价规则对象。""" class FakeRule: pass rule = FakeRule() rule.base_unit_price = overrides.get("base_unit_price", 50.0) rule.pricing_type = overrides.get("pricing_type", "area") rule.pricing_unit = overrides.get("pricing_unit", "㎡") rule.pricing_inputs = overrides.get("pricing_inputs", json.dumps([ {"key": "length", "type": "number", "default_unit": "m"}, {"key": "width", "type": "number", "default_unit": "m"}, ])) rule.formula_expr = overrides.get("formula_expr", "$input.length_m * $input.width_m * $rule.base_unit_price") rule.formula_constants = overrides.get("formula_constants", None) rule.surcharge_json = overrides.get("surcharge_json", None) rule.formula_note = overrides.get("formula_note", None) rule.tax_rate = overrides.get("tax_rate", 0) rule.tax_inclusive = overrides.get("tax_inclusive", 0) return rule def test_area_pricing_basic(self): """PRICE-010: 面积定价 — 2m x 1.2m x ¥50/㎡ = ¥120""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0) result = pricing_engine.calculate(rule, {"length": 2, "width": 1.2}) assert result["base_cost"] == 120.0 assert result["cost_price"] == 120.0 assert result["total_surcharge"] == 0 def test_area_pricing_cm_unit(self): """单位换算: cm → m, 200cm x 120cm x ¥50/㎡ = ¥120""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0) result = pricing_engine.calculate(rule, { "length": 200, "length_unit": "cm", "width": 120, "width_unit": "cm", }) assert result["base_cost"] == 120.0 def test_area_pricing_mm_unit(self): """单位换算: mm → m, 2000mm x 1200mm x ¥50/㎡ = ¥120""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0) result = pricing_engine.calculate(rule, { "length": 2000, "length_unit": "mm", "width": 1200, "width_unit": "mm", }) assert result["base_cost"] == 120.0 def test_formula_with_constants(self): """PRICE-019: 含常量的公式计算, $const.rate 作为乘数""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( base_unit_price=50.0, formula_expr="$input.length_m * $input.width_m * $rule.base_unit_price * $const.rate", formula_constants=json.dumps({"rate": 1.5}), ) # 1*1*50*1.5 = 75 result = pricing_engine.calculate(rule, {"length": 1, "width": 1}) assert result["base_cost"] == 75.0 def test_surcharge_per_sqm(self): """PRICE-012: 按面积附加费, 2m*1.2m=2.4㎡, ¥10/㎡ = ¥24""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( base_unit_price=50.0, surcharge_json=json.dumps({ "urgent": {"name": "加急费", "method": "per_sqm", "price": 10} }), ) result = pricing_engine.calculate(rule, { "length": 2, "width": 1.2, "surcharge_urgent": True, }) assert result["total_surcharge"] == 24.0 assert len(result["surcharge_items"]) == 1 assert result["surcharge_items"][0]["key"] == "urgent" def test_surcharge_per_piece(self): """PRICE-013: 按件附加费, 10件 x ¥5/件 = ¥50""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( base_unit_price=50.0, surcharge_json=json.dumps({ "packaging": {"name": "包装费", "method": "per_piece", "price": 5, "input_key": "qty"} }), ) result = pricing_engine.calculate(rule, { "length": 2, "width": 1.2, "qty": 10, "surcharge_packaging": True, }) assert result["total_surcharge"] == 50.0 def test_surcharge_per_linear_m(self): """PRICE-014: 按米附加费, 20m x ¥3/m = ¥60""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( base_unit_price=50.0, surcharge_json=json.dumps({ "edge": {"name": "包边费", "method": "per_linear_m", "price": 3, "input_key": "edge_length"} }), ) result = pricing_engine.calculate(rule, { "length": 2, "width": 1.2, "edge_length": 20, "surcharge_edge": True, }) assert result["total_surcharge"] == 60.0 def test_surcharge_not_enabled(self): """未启用的附加费不计入""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( base_unit_price=50.0, surcharge_json=json.dumps({ "urgent": {"name": "加急费", "method": "per_sqm", "price": 10} }), ) result = pricing_engine.calculate(rule, {"length": 2, "width": 1.2}) assert result["total_surcharge"] == 0 assert len(result["surcharge_items"]) == 0 def test_tax_exclusive(self): """PRICE-017: 不含税定价, cost=100, tax_rate=13%, tax_amount=13, price_in_tax=113""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0, tax_rate=13, tax_inclusive=0) # 2m x 1m x 50 = 100 result = pricing_engine.calculate(rule, {"length": 2, "width": 1}) assert result["cost_price"] == 100.0 assert result["tax_rate"] == 13 assert result["price_ex_tax"] == 100.0 assert result["tax_amount"] == 13.0 assert result["price_in_tax"] == 113.0 def test_tax_inclusive(self): """PRICE-016: 含税定价, cost=113(含税), tax_rate=13%, price_ex_tax≈100""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=56.5, tax_rate=13, tax_inclusive=1) # 2m x 1m x 56.5 = 113 result = pricing_engine.calculate(rule, {"length": 2, "width": 1}) assert result["cost_price"] == 113.0 assert result["price_in_tax"] == 113.0 assert result["price_ex_tax"] == 100.0 assert result["tax_amount"] == 13.0 def test_no_tax(self): """无税率时, tax_amount=0, price_in_tax=cost_price""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0, tax_rate=0) result = pricing_engine.calculate(rule, {"length": 2, "width": 1}) assert result["tax_amount"] == 0 assert result["price_in_tax"] == result["cost_price"] def test_empty_inputs(self): """空输入返回 0""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule() result = pricing_engine.calculate(rule, {}) assert result["base_cost"] == 0.0 def test_invalid_formula_returns_zero(self): """无效公式返回 0, 不抛异常""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(formula_expr="$nonexistent.field * ???") result = pricing_engine.calculate(rule, {"length": 2, "width": 1}) assert result["base_cost"] == 0.0 def test_formula_detail_text(self): """公式说明包含关键信息""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0) result = pricing_engine.calculate(rule, {"length": 2, "width": 1.2}) assert "1.2" in result["formula_detail"] assert "120.00" in result["formula_detail"] def test_formula_detail_with_surcharge(self): """公式说明包含附加费项""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( base_unit_price=50.0, surcharge_json=json.dumps({ "urgent": {"name": "加急费", "method": "per_sqm", "price": 10} }), ) result = pricing_engine.calculate(rule, { "length": 2, "width": 1.2, "surcharge_urgent": True, }) assert "加急费" in result["formula_detail"] def test_formula_detail_with_tax(self): """公式说明包含税率信息""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(base_unit_price=50.0, tax_rate=13, tax_inclusive=0) result = pricing_engine.calculate(rule, {"length": 2, "width": 1}) assert "13" in result["formula_detail"] assert "税" in result["formula_detail"] def test_get_surcharge_options(self): """附加费选项列表""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule(surcharge_json=json.dumps({ "urgent": {"name": "加急费", "method": "per_sqm", "price": 10}, "pack": {"name": "包装费", "method": "per_piece", "price": 5, "input_key": "qty"}, })) options = pricing_engine.get_available_surcharge_options(rule) assert len(options) == 2 assert options[0]["key"] == "urgent" assert options[1]["key"] == "pack" def test_conditional_formula(self): """条件公式: ${1 > 0} ? 100 : 50 → 100""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( formula_expr="${$input.length_m > 1} ? $input.length_m * $rule.base_unit_price : $rule.base_unit_price", ) result = pricing_engine.calculate(rule, {"length": 2, "width": 1}) assert result["base_cost"] == 100.0 def test_conditional_formula_false_branch(self): """条件公式假分支: length=0.5 <= 1, 走 50""" from backend.app.services.pricing_engine import pricing_engine rule = self._make_rule( formula_expr="${$input.length_m > 1} ? $input.length_m * $rule.base_unit_price : $rule.base_unit_price", ) result = pricing_engine.calculate(rule, {"length": 0.5, "width": 1}) assert result["base_cost"] == 50.0 # ============================================================ # 定价规则 CRUD API 测试 # ============================================================ @pytest.mark.pricing @pytest.mark.p1 class TestPricingRuleCRUD: """PRICE-001 ~ PRICE-007: 定价规则 API 测试。""" def _create_rule(self, client, headers, product_id=1, **overrides): """辅助: 创建定价规则。""" payload = { "product_id": product_id, "product_name": f"产品{product_id}", "pricing_type": "area", "base_unit_price": 50.0, "pricing_inputs": json.dumps([ {"key": "length", "type": "number", "default_unit": "m"}, {"key": "width", "type": "number", "default_unit": "m"}, ]), "formula_expr": "$input.length_m * $input.width_m * $rule.base_unit_price", } payload.update(overrides) resp = client.post("/api/pricing-rules", headers=headers, json=payload) return resp def test_create_pricing_rule(self, client, admin_headers, make_product): """PRICE-001: 创建面积定价规则。""" product = make_product() resp = self._create_rule(client, admin_headers, product_id=product.id) assert resp.status_code == 200 data = resp.json() assert data["code"] == 0 assert data["data"]["product_id"] == product.id assert data["data"]["pricing_type"] == "area" assert data["data"]["base_unit_price"] == 50.0 def test_create_duplicate_rule_rejected(self, client, admin_headers, make_product): """PRICE-001b: 同产品重复创建应拒绝。""" product = make_product() self._create_rule(client, admin_headers, product_id=product.id) resp = self._create_rule(client, admin_headers, product_id=product.id) assert resp.status_code == 400 def test_list_pricing_rules(self, client, admin_headers, make_product): """PRICE-007: 查询定价规则列表。""" product = make_product() self._create_rule(client, admin_headers, product_id=product.id) resp = client.get("/api/pricing-rules", headers=admin_headers) assert resp.status_code == 200 data = resp.json() assert data["data"]["total"] >= 1 def test_list_pricing_rules_by_product(self, client, admin_headers, make_product): """按产品ID筛选定价规则。""" product = make_product() self._create_rule(client, admin_headers, product_id=product.id) resp = client.get(f"/api/pricing-rules?product_id={product.id}", headers=admin_headers) assert resp.status_code == 200 assert resp.json()["data"]["total"] >= 1 def test_get_pricing_rule(self, client, admin_headers, make_product): """按产品ID获取定价规则。""" product = make_product() self._create_rule(client, admin_headers, product_id=product.id) resp = client.get(f"/api/pricing-rules/{product.id}", headers=admin_headers) assert resp.status_code == 200 assert resp.json()["data"]["product_id"] == product.id def test_get_pricing_rule_not_found(self, client, admin_headers): """查询不存在的产品定价规则返回 404。""" resp = client.get("/api/pricing-rules/99999", headers=admin_headers) assert resp.status_code == 404 def test_update_pricing_rule(self, client, admin_headers, make_product): """PRICE-005: 更新定价规则。""" product = make_product() create_resp = self._create_rule(client, admin_headers, product_id=product.id) rule_id = create_resp.json()["data"]["id"] resp = client.put(f"/api/pricing-rules/{rule_id}", headers=admin_headers, json={ "base_unit_price": 80.0, }) assert resp.status_code == 200 assert resp.json()["data"]["base_unit_price"] == 80.0 def test_update_pricing_rule_not_found(self, client, admin_headers): """更新不存在的定价规则返回 404。""" resp = client.put("/api/pricing-rules/99999", headers=admin_headers, json={ "base_unit_price": 80.0, }) assert resp.status_code == 404 def test_delete_pricing_rule(self, client, admin_headers, make_product): """PRICE-006: 删除定价规则(软删除)。""" product = make_product() create_resp = self._create_rule(client, admin_headers, product_id=product.id) rule_id = create_resp.json()["data"]["id"] resp = client.delete(f"/api/pricing-rules/{rule_id}", headers=admin_headers) assert resp.status_code == 200 assert resp.json()["data"]["deleted"] is True # 删除后查询应返回 404 resp = client.get(f"/api/pricing-rules/{product.id}", headers=admin_headers) assert resp.status_code == 404 def test_salesman_cannot_create_rule(self, client, salesman_headers, make_product): """业务员不能创建定价规则。""" product = make_product() resp = self._create_rule(client, salesman_headers, product_id=product.id) assert resp.status_code == 403 def test_salesman_cannot_update_rule(self, client, salesman_headers, admin_headers, make_product): """业务员不能更新定价规则。""" product = make_product() create_resp = self._create_rule(client, admin_headers, product_id=product.id) rule_id = create_resp.json()["data"]["id"] resp = client.put(f"/api/pricing-rules/{rule_id}", headers=salesman_headers, json={ "base_unit_price": 80.0, }) assert resp.status_code == 403 # ============================================================ # 报价计算 API 测试 # ============================================================ @pytest.mark.pricing @pytest.mark.p1 class TestQuotationCalculation: """PRICE-010 ~ PRICE-020: 报价计算 API 测试。""" def _setup_rule(self, client, headers, db_session, product, **overrides): """辅助: 在数据库中创建定价规则。""" from backend.app.models.business import ProductPricingRule defaults = { "product_id": product.id, "product_name": product.product_name, "pricing_type": "area", "base_unit_price": 50.0, "pricing_unit": "㎡", "pricing_inputs": json.dumps([ {"key": "length", "type": "number", "default_unit": "m"}, {"key": "width", "type": "number", "default_unit": "m"}, ]), "formula_expr": "$input.length_m * $input.width_m * $rule.base_unit_price", "tax_rate": 0, "tax_inclusive": 0, "status": 1, "deleted": 0, } defaults.update(overrides) rule = ProductPricingRule(**defaults) db_session.add(rule) db_session.flush() return rule def test_calculate_area(self, client, admin_headers, db_session, make_product): """PRICE-010: 面积报价计算, 2m x 1.2m x ¥50 = ¥120。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0) resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1.2}, }) assert resp.status_code == 200 data = resp.json()["data"] assert data["base_cost"] == 120.0 assert data["cost_price"] == 120.0 def test_calculate_with_surcharge(self, client, admin_headers, db_session, make_product): """报价计算含附加费。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0, surcharge_json=json.dumps({ "urgent": {"name": "加急费", "method": "per_sqm", "price": 10} })) resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1.2}, "surcharge_selections": {"surcharge_urgent": True}, }) assert resp.status_code == 200 data = resp.json()["data"] assert data["total_surcharge"] == 24.0 assert data["cost_price"] == 144.0 def test_calculate_with_tax_exclusive(self, client, admin_headers, db_session, make_product): """PRICE-017: 不含税报价计算, 100 + 13% = 113。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0, tax_rate=13, tax_inclusive=0) resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1}, }) data = resp.json()["data"] assert data["tax_rate"] == 13 assert data["price_ex_tax"] == 100.0 assert data["tax_amount"] == 13.0 assert data["price_in_tax"] == 113.0 def test_calculate_with_tax_inclusive(self, client, admin_headers, db_session, make_product): """PRICE-016: 含税报价计算。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=56.5, tax_rate=13, tax_inclusive=1) resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1}, }) data = resp.json()["data"] assert data["price_in_tax"] == 113.0 assert data["price_ex_tax"] == 100.0 def test_calculate_no_rule_returns_404(self, client, admin_headers, make_product): """PRICE-020: 无定价规则的产品返回 404。""" product = make_product() resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1}, }) assert resp.status_code == 404 def test_calculate_with_price_tiers(self, client, admin_headers, db_session, make_product): """PRICE-018: 含价格层级的报价。""" from backend.app.models.business import ProductPriceTier product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0) tier = ProductPriceTier( product_id=product.id, tier_code="vip", tier_name="VIP", price=45.0, price_unit="㎡", status=1, deleted=0, ) db_session.add(tier) db_session.flush() resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1}, }) data = resp.json()["data"] assert "vip" in data["sale_price_tier"] assert data["sale_price_tier"]["vip"] == 45.0 def test_calculate_formula_detail(self, client, admin_headers, db_session, make_product): """报价结果包含公式说明。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0) resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1.2}, }) data = resp.json()["data"] assert "formula_detail" in data assert "120.00" in data["formula_detail"] def test_calculate_available_surcharge_options(self, client, admin_headers, db_session, make_product): """报价结果包含可用附加费选项。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0, surcharge_json=json.dumps({ "urgent": {"name": "加急费", "method": "per_sqm", "price": 10} })) resp = client.post("/api/quotation/calculate", headers=admin_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1}, }) data = resp.json()["data"] assert len(data["available_surcharge_options"]) == 1 assert data["available_surcharge_options"][0]["key"] == "urgent" def test_salesman_can_calculate(self, client, salesman_headers, admin_headers, db_session, make_product): """业务员可以调用报价计算。""" product = make_product() self._setup_rule(client, admin_headers, db_session, product, base_unit_price=50.0) resp = client.post("/api/quotation/calculate", headers=salesman_headers, json={ "product_id": product.id, "user_inputs": {"length": 2, "width": 1}, }) assert resp.status_code == 200 # ============================================================ # 供应商成本 CRUD API 测试 # ============================================================ @pytest.mark.pricing @pytest.mark.p1 class TestSupplierCostCRUD: """PRICE-030 ~ PRICE-033: 供应商成本 API 测试。""" def _create_cost(self, client, headers, product_id, supplier_id, **overrides): payload = { "product_id": product_id, "supplier_id": supplier_id, "cost_price": 35.0, "cost_unit": "㎡", } payload.update(overrides) return client.post("/api/supplier-costs", headers=headers, json=payload) def test_create_supplier_cost(self, client, admin_headers, make_product, make_supplier): """PRICE-030: 创建供应商成本。""" product = make_product() supplier = make_supplier() resp = self._create_cost(client, admin_headers, product.id, supplier.id) assert resp.status_code == 200 assert resp.json()["data"]["cost_price"] == 35.0 def test_list_supplier_costs(self, client, admin_headers, make_product, make_supplier): """PRICE-031: 查询供应商成本列表。""" product = make_product() supplier = make_supplier() self._create_cost(client, admin_headers, product.id, supplier.id) resp = client.get(f"/api/supplier-costs?product_id={product.id}", headers=admin_headers) assert resp.status_code == 200 assert len(resp.json()["data"]["list"]) >= 1 def test_update_supplier_cost(self, client, admin_headers, make_product, make_supplier): """PRICE-032: 更新供应商成本。""" product = make_product() supplier = make_supplier() create_resp = self._create_cost(client, admin_headers, product.id, supplier.id) cost_id = create_resp.json()["data"]["id"] resp = client.put(f"/api/supplier-costs/{cost_id}", headers=admin_headers, json={ "cost_price": 40.0, }) assert resp.status_code == 200 assert resp.json()["data"]["cost_price"] == 40.0 def test_delete_supplier_cost(self, client, admin_headers, make_product, make_supplier): """PRICE-033: 删除供应商成本(软删除)。""" product = make_product() supplier = make_supplier() create_resp = self._create_cost(client, admin_headers, product.id, supplier.id) cost_id = create_resp.json()["data"]["id"] resp = client.delete(f"/api/supplier-costs/{cost_id}", headers=admin_headers) assert resp.status_code == 200 # 删除后列表应为空 resp = client.get(f"/api/supplier-costs?product_id={product.id}", headers=admin_headers) assert len(resp.json()["data"]["list"]) == 0 def test_salesman_cannot_create_cost(self, client, salesman_headers, make_product, make_supplier): """业务员不能创建供应商成本。""" product = make_product() supplier = make_supplier() resp = self._create_cost(client, salesman_headers, product.id, supplier.id) assert resp.status_code == 403 # ============================================================ # 价格层级 CRUD API 测试 # ============================================================ @pytest.mark.pricing @pytest.mark.p1 class TestPriceTierCRUD: """PRICE-040 ~ PRICE-043: 价格层级 API 测试。""" def _create_tier(self, client, headers, product_id, **overrides): payload = { "product_id": product_id, "tier_code": "vip", "tier_name": "VIP客户", "price": 45.0, "price_unit": "㎡", } payload.update(overrides) return client.post("/api/price-tiers", headers=headers, json=payload) def test_create_price_tier(self, client, admin_headers, make_product): """PRICE-040: 创建价格层级。""" product = make_product() resp = self._create_tier(client, admin_headers, product.id) assert resp.status_code == 200 assert resp.json()["data"]["tier_code"] == "vip" assert resp.json()["data"]["price"] == 45.0 def test_list_price_tiers(self, client, admin_headers, make_product): """PRICE-041: 查询价格层级列表。""" product = make_product() self._create_tier(client, admin_headers, product.id) resp = client.get(f"/api/price-tiers?product_id={product.id}", headers=admin_headers) assert resp.status_code == 200 assert len(resp.json()["data"]["list"]) >= 1 def test_update_price_tier(self, client, admin_headers, make_product): """PRICE-042: 更新价格层级。""" product = make_product() create_resp = self._create_tier(client, admin_headers, product.id) tier_id = create_resp.json()["data"]["id"] resp = client.put(f"/api/price-tiers/{tier_id}", headers=admin_headers, json={ "price": 42.0, }) assert resp.status_code == 200 assert resp.json()["data"]["price"] == 42.0 def test_delete_price_tier(self, client, admin_headers, make_product): """PRICE-043: 删除价格层级(软删除)。""" product = make_product() create_resp = self._create_tier(client, admin_headers, product.id) tier_id = create_resp.json()["data"]["id"] resp = client.delete(f"/api/price-tiers/{tier_id}", headers=admin_headers) assert resp.status_code == 200 # 删除后列表应为空 resp = client.get(f"/api/price-tiers?product_id={product.id}", headers=admin_headers) assert len(resp.json()["data"]["list"]) == 0 def test_salesman_can_list_tiers(self, client, salesman_headers, admin_headers, make_product): """业务员可以查看价格层级。""" product = make_product() self._create_tier(client, admin_headers, product.id) resp = client.get(f"/api/price-tiers?product_id={product.id}", headers=salesman_headers) assert resp.status_code == 200 def test_salesman_cannot_create_tier(self, client, salesman_headers, make_product): """业务员不能创建价格层级。""" product = make_product() resp = self._create_tier(client, salesman_headers, product.id) assert resp.status_code == 403