test_pl_plan_single_contract_rules.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import pytest
  2. from fastapi import HTTPException
  3. from types import SimpleNamespace
  4. from app.schemas.pl.plan import PlanContractLink, PlanLoadBody, PlanStopProductBody
  5. from app.services.pl_plan_stop_service import (
  6. assert_at_most_one_contract,
  7. assert_load_products_in_selected_contract,
  8. )
  9. def test_assert_at_most_one_contract_allows_zero_or_one():
  10. assert assert_at_most_one_contract([], "装货合同") == []
  11. one = [PlanContractLink(contractId="c1", contractNo="N1")]
  12. assert assert_at_most_one_contract(one, "装货合同") == one
  13. def test_assert_at_most_one_contract_rejects_two():
  14. links = [
  15. PlanContractLink(contractId="c1", contractNo="N1"),
  16. PlanContractLink(contractId="c2", contractNo="N2"),
  17. ]
  18. with pytest.raises(HTTPException) as exc:
  19. assert_at_most_one_contract(links, "装货合同")
  20. assert exc.value.status_code == 422
  21. assert "只能选择 1 份" in str(exc.value.detail)
  22. class _FakeQuery:
  23. def __init__(self, rows):
  24. self._rows = rows
  25. def filter(self, *args, **kwargs):
  26. return self
  27. def all(self):
  28. return self._rows
  29. class _FakeDb:
  30. def __init__(self, product_ids):
  31. self._rows = [SimpleNamespace(product_id=pid) for pid in product_ids]
  32. def query(self, model):
  33. return _FakeQuery(self._rows)
  34. def _load_with_products(product_ids, contract_id="c1"):
  35. return PlanLoadBody(
  36. customerId="cust-1",
  37. customerName="外部客户A",
  38. contracts=[PlanContractLink(contractId=contract_id, contractNo="N1")],
  39. products=[
  40. PlanStopProductBody(
  41. productId=pid,
  42. productNo=pid,
  43. productName=f"产品{pid}",
  44. unit="吨",
  45. planQty=1,
  46. )
  47. for pid in product_ids
  48. ],
  49. )
  50. def test_assert_load_products_in_contract_passes_when_all_match():
  51. db = _FakeDb(["p1", "p2"])
  52. load = _load_with_products(["p1"])
  53. assert_load_products_in_selected_contract(db, "shanxia2.0", load)
  54. def test_assert_load_products_in_contract_rejects_outside():
  55. db = _FakeDb(["p1"])
  56. load = _load_with_products(["p2"])
  57. with pytest.raises(HTTPException) as exc:
  58. assert_load_products_in_selected_contract(db, "shanxia2.0", load)
  59. assert exc.value.status_code == 422
  60. assert "不在所选合同明细中" in str(exc.value.detail)
  61. def test_assert_load_products_skips_when_no_contract():
  62. db = _FakeDb([])
  63. load = PlanLoadBody(
  64. customerId="cust-1",
  65. customerName="外部客户A",
  66. contracts=[],
  67. products=[
  68. PlanStopProductBody(
  69. productId="p1",
  70. productNo="p1",
  71. productName="产品p1",
  72. unit="吨",
  73. planQty=1,
  74. )
  75. ],
  76. )
  77. assert_load_products_in_selected_contract(db, "shanxia2.0", load)
  78. def test_assert_load_products_skips_internal_customer():
  79. from app.services.pl_internal_customer import INTERNAL_CUSTOMER_NAMES
  80. internal = next(iter(INTERNAL_CUSTOMER_NAMES))
  81. db = _FakeDb([])
  82. load = PlanLoadBody(
  83. customerId="cust-1",
  84. customerName=internal,
  85. contracts=[],
  86. products=[
  87. PlanStopProductBody(
  88. productId="p1",
  89. productNo="p1",
  90. productName="产品p1",
  91. unit="吨",
  92. planQty=1,
  93. )
  94. ],
  95. )
  96. assert_load_products_in_selected_contract(db, "shanxia2.0", load)