diff --git a/backend/app/services/protocol_transfer_status.py b/backend/app/services/protocol_transfer_status.py index a54220e4..b3cd4d82 100644 --- a/backend/app/services/protocol_transfer_status.py +++ b/backend/app/services/protocol_transfer_status.py @@ -14,6 +14,16 @@ def can_confirm_receipt(pay_status: Optional[str]) -> bool: return (pay_status or "").strip() == "jfqrz" +def can_withdraw_receipt(pay_status: Optional[str]) -> bool: + """仅缴费确认中可撤回为待缴费。""" + return (pay_status or "").strip() == "jfqrz" + + +def can_edit_transfer(pay_status: Optional[str]) -> bool: + """财务确认收款(yjf)前可编辑:待缴费 / 缴费确认中。""" + return (pay_status or "").strip() in ("dfj", "jfqrz") + + def schedule_paid_and_arrears( *, due: Union[Decimal, float, int, str, None], diff --git a/backend/tests/test_presign_order.py b/backend/tests/test_presign_order.py index a80d284b..8daf6536 100644 --- a/backend/tests/test_presign_order.py +++ b/backend/tests/test_presign_order.py @@ -13,6 +13,22 @@ class TestPresignTransferStatus(unittest.TestCase): self.assertTrue(can_confirm_receipt("jfqrz")) self.assertFalse(can_confirm_receipt("dfj")) + def test_can_withdraw_only_jfqrz(self): + from app.services.protocol_transfer_status import can_withdraw_receipt + + self.assertTrue(can_withdraw_receipt("jfqrz")) + self.assertFalse(can_withdraw_receipt("dfj")) + self.assertFalse(can_withdraw_receipt("yjf")) + self.assertFalse(can_withdraw_receipt(None)) + self.assertFalse(can_withdraw_receipt("")) + + +class TestFinanceTransferFields(unittest.TestCase): + def test_sample_type_names_helper_exists(self): + from app.crud.presign_order import _sample_type_names + + self.assertTrue(callable(_sample_type_names)) + class TestConvertRules(unittest.TestCase): def test_convert_requires_active_status(self): @@ -26,5 +42,59 @@ class TestConvertRules(unittest.TestCase): assert_presign_convertible(order_status="draft") +class TestConvertStaffMapping(unittest.TestCase): + """转正式:采集专员→采集专员+市场专员;采集经理→地区经理。""" + + def test_collector_fills_collector_and_sales(self): + from app.crud.presign_order import resolve_convert_staff_fields + + out = resolve_convert_staff_fields( + collector_user_id="c1", + sales_user_id="s1", + region_manager_user_id="m1", + fallback_user_id="u0", + ) + self.assertEqual(out["collector_user_id"], "c1") + self.assertEqual(out["sales_user_id"], "c1") + self.assertEqual(out["region_manager_user_id"], "m1") + + def test_collector_fallback_to_operator_when_empty(self): + from app.crud.presign_order import resolve_convert_staff_fields + + out = resolve_convert_staff_fields( + collector_user_id=None, + sales_user_id="s1", + region_manager_user_id="", + fallback_user_id="u0", + ) + self.assertEqual(out["collector_user_id"], "u0") + self.assertEqual(out["sales_user_id"], "u0") + self.assertIsNone(out["region_manager_user_id"]) + + +class TestProtocolDepositAssociation(unittest.TestCase): + """正式协议意向金通过 source_presign_order_id 关联预签,不落快照表。""" + + def test_list_deposit_transfers_for_protocol_uses_presign_source(self): + from app.crud.protocol_sample_payment import list_deposit_transfers_for_protocol + + # 无来源预签时返回空列表 + class _FakeDb: + pass + + self.assertEqual( + list_deposit_transfers_for_protocol( + _FakeDb(), source_presign_order_id=None, tenant_no="t1" + ), + [], + ) + self.assertEqual( + list_deposit_transfers_for_protocol( + _FakeDb(), source_presign_order_id=" ", tenant_no="t1" + ), + [], + ) + + if __name__ == "__main__": unittest.main()