from datetime import date from unittest.mock import MagicMock, patch from app.crud.crm.contract import ( align_contract_no_to_sign_date, generate_contract_no, replace_contract_no_date, ) def test_replace_keeps_seq_and_swaps_date(): assert ( replace_contract_no_date("SX/X-JT20250803001", date(2025, 8, 10)) == "SX/X-JT20250810001" ) def test_replace_noop_when_same_date(): assert replace_contract_no_date("SX/X-JT20250803001", date(2025, 8, 3)) == "SX/X-JT20250803001" def test_replace_returns_none_for_nonstandard(): assert replace_contract_no_date("CUSTOM-001", date(2025, 8, 10)) is None assert replace_contract_no_date("", date(2025, 8, 10)) is None assert replace_contract_no_date(None, date(2025, 8, 10)) is None def test_align_returns_original_when_nonstandard(): db = MagicMock() assert ( align_contract_no_to_sign_date( db, "t1", "CUSTOM-001", date(2025, 8, 10) ) == "CUSTOM-001" ) def test_align_bumps_seq_when_occupied(): db = MagicMock() def _exists(db_, tenant, no, exclude_id=None): return no == "SX/X-JT20250810001" with patch("app.crud.crm.contract.contract_no_exists", side_effect=_exists): out = align_contract_no_to_sign_date( db, "t1", "SX/X-JT20250803001", date(2025, 8, 10) ) assert out == "SX/X-JT20250810002" def test_align_exclude_id_skips_self(): db = MagicMock() def _exists(db_, tenant, no, exclude_id=None): if exclude_id == "self-id": return False return no == "SX/X-JT20250810001" with patch("app.crud.crm.contract.contract_no_exists", side_effect=_exists): out = align_contract_no_to_sign_date( db, "t1", "SX/X-JT20250803001", date(2025, 8, 10), exclude_id="self-id", ) assert out == "SX/X-JT20250810001" def _mock_last_contract(contract_no: str): db = MagicMock() last = MagicMock() last.contract_no = contract_no q = MagicMock() q.filter.return_value = q q.order_by.return_value = q q.first.return_value = last db.query.return_value = q return db def test_generate_bumps_past_soft_deleted_occupied_no(): """软删占用的编号仍占唯一索引,生成时须跳过(对齐 uk_contract_tenant_no)。""" db = _mock_last_contract("SX/X-JT20260804005") def _exists(db_, tenant, no, exclude_id=None): return no == "SX/X-JT20260804006" with patch("app.crud.crm.contract.contract_no_exists", side_effect=_exists): out = generate_contract_no(db, "shanxia2.0", sign_date=date(2026, 8, 4)) assert out == "SX/X-JT20260804007" def test_generate_starts_after_soft_deleted_max(): """取最大流水号须含软删,避免复用已软删的最大编号。""" db = _mock_last_contract("SX/X-JT20260804006") with patch("app.crud.crm.contract.contract_no_exists", return_value=False): out = generate_contract_no(db, "shanxia2.0", sign_date=date(2026, 8, 4)) assert out == "SX/X-JT20260804007"