test_crm_contract_no_align.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from datetime import date
  2. from unittest.mock import MagicMock, patch
  3. from app.crud.crm.contract import (
  4. align_contract_no_to_sign_date,
  5. generate_contract_no,
  6. replace_contract_no_date,
  7. )
  8. def test_replace_keeps_seq_and_swaps_date():
  9. assert (
  10. replace_contract_no_date("SX/X-JT20250803001", date(2025, 8, 10))
  11. == "SX/X-JT20250810001"
  12. )
  13. def test_replace_noop_when_same_date():
  14. assert replace_contract_no_date("SX/X-JT20250803001", date(2025, 8, 3)) == "SX/X-JT20250803001"
  15. def test_replace_returns_none_for_nonstandard():
  16. assert replace_contract_no_date("CUSTOM-001", date(2025, 8, 10)) is None
  17. assert replace_contract_no_date("", date(2025, 8, 10)) is None
  18. assert replace_contract_no_date(None, date(2025, 8, 10)) is None
  19. def test_align_returns_original_when_nonstandard():
  20. db = MagicMock()
  21. assert (
  22. align_contract_no_to_sign_date(
  23. db, "t1", "CUSTOM-001", date(2025, 8, 10)
  24. )
  25. == "CUSTOM-001"
  26. )
  27. def test_align_bumps_seq_when_occupied():
  28. db = MagicMock()
  29. def _exists(db_, tenant, no, exclude_id=None):
  30. return no == "SX/X-JT20250810001"
  31. with patch("app.crud.crm.contract.contract_no_exists", side_effect=_exists):
  32. out = align_contract_no_to_sign_date(
  33. db, "t1", "SX/X-JT20250803001", date(2025, 8, 10)
  34. )
  35. assert out == "SX/X-JT20250810002"
  36. def test_align_exclude_id_skips_self():
  37. db = MagicMock()
  38. def _exists(db_, tenant, no, exclude_id=None):
  39. if exclude_id == "self-id":
  40. return False
  41. return no == "SX/X-JT20250810001"
  42. with patch("app.crud.crm.contract.contract_no_exists", side_effect=_exists):
  43. out = align_contract_no_to_sign_date(
  44. db,
  45. "t1",
  46. "SX/X-JT20250803001",
  47. date(2025, 8, 10),
  48. exclude_id="self-id",
  49. )
  50. assert out == "SX/X-JT20250810001"
  51. def _mock_last_contract(contract_no: str):
  52. db = MagicMock()
  53. last = MagicMock()
  54. last.contract_no = contract_no
  55. q = MagicMock()
  56. q.filter.return_value = q
  57. q.order_by.return_value = q
  58. q.first.return_value = last
  59. db.query.return_value = q
  60. return db
  61. def test_generate_bumps_past_soft_deleted_occupied_no():
  62. """软删占用的编号仍占唯一索引,生成时须跳过(对齐 uk_contract_tenant_no)。"""
  63. db = _mock_last_contract("SX/X-JT20260804005")
  64. def _exists(db_, tenant, no, exclude_id=None):
  65. return no == "SX/X-JT20260804006"
  66. with patch("app.crud.crm.contract.contract_no_exists", side_effect=_exists):
  67. out = generate_contract_no(db, "shanxia2.0", sign_date=date(2026, 8, 4))
  68. assert out == "SX/X-JT20260804007"
  69. def test_generate_starts_after_soft_deleted_max():
  70. """取最大流水号须含软删,避免复用已软删的最大编号。"""
  71. db = _mock_last_contract("SX/X-JT20260804006")
  72. with patch("app.crud.crm.contract.contract_no_exists", return_value=False):
  73. out = generate_contract_no(db, "shanxia2.0", sign_date=date(2026, 8, 4))
  74. assert out == "SX/X-JT20260804007"