Files
zkxw/backend/tests/test_protocol_pay_mode_switch.py
T
zlwuandCursor 57487fd0dc fix(protocol): save DJ pay details without blocking on due-sum mismatch
Persist payment before samples to avoid consuming sample codes on validation failure, soft-delete samples on create rollback, and surface DJ due-amount mismatch as a hint only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 17:49:36 +08:00

171 lines
5.0 KiB
Python

"""缴费模式切换校验"""
import unittest
from app.services.protocol_pay_mode_switch import (
dj_due_amount_mismatch_hint,
validate_payment_details_for_save,
)
class TestValidatePaymentDetailsForSave(unittest.TestCase):
def test_dj_single_dfj_full_contract(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[{"payStatus": "dfj", "dueAmount": 10000}],
)
def test_dj_allows_multiple_dfj_when_sum_matches(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 8000},
],
)
def test_dj_allows_multiple_dfj_wrong_sum(self):
"""应缴合计与合同不一致不阻断保存,仅提示。"""
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 7000},
],
)
hint = dj_due_amount_mismatch_hint(
10000,
[
{"payStatus": "dfj", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 7000},
],
)
self.assertIsNotNone(hint)
self.assertIn("之和应为", hint or "")
def test_dj_partial_paid_remaining(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "yjf", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 8000},
],
)
def test_dj_partial_paid_wrong_remaining_allowed(self):
validate_payment_details_for_save(
"dj",
10000,
None,
[
{"payStatus": "yjf", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 10000},
],
)
hint = dj_due_amount_mismatch_hint(
10000,
[
{"payStatus": "yjf", "dueAmount": 2000},
{"payStatus": "dfj", "dueAmount": 10000},
],
)
self.assertIsNotNone(hint)
def test_djfq_same_as_fq(self):
validate_payment_details_for_save(
"djfq",
41000,
4,
[
{"payStatus": "dfj", "installmentNo": 1, "dueAmount": 29360},
{"payStatus": "dfj", "installmentNo": 2, "dueAmount": 3880},
{"payStatus": "dfj", "installmentNo": 3, "dueAmount": 3880},
{"payStatus": "dfj", "installmentNo": 4, "dueAmount": 3880},
],
installment_ratios=[29360, 3880, 3880, 3880],
)
def test_fq_count_mismatch(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"fq",
6000,
6,
[{"payStatus": "dfj", "dueAmount": 6000}],
)
def test_fq_partial_paid_valid(self):
validate_payment_details_for_save(
"fq",
10000,
4,
[
{"payStatus": "yjf", "installmentNo": 1, "dueAmount": 2500},
{"payStatus": "yjf", "installmentNo": 2, "dueAmount": 2500},
{"payStatus": "dfj", "installmentNo": 3, "dueAmount": 2500},
{"payStatus": "dfj", "installmentNo": 4, "dueAmount": 2500},
],
)
def test_fq_jfqrz_rejected(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"fq",
6000,
2,
[
{"payStatus": "jfqrz", "dueAmount": 3000},
{"payStatus": "dfj", "dueAmount": 3000},
],
)
def test_dj_ytf_allowed(self):
"""已退费为终态,保存协议单时不应阻断。"""
validate_payment_details_for_save(
"dj",
10000,
None,
[{"payStatus": "ytf", "dueAmount": 10000}],
)
def test_fq_ytf_allowed(self):
validate_payment_details_for_save(
"fq",
6000,
2,
[
{"payStatus": "ytf", "installmentNo": 1, "dueAmount": 3000},
{"payStatus": "dfj", "installmentNo": 2, "dueAmount": 3000},
],
)
def test_blocking_status_rejected(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"dj",
10000,
None,
[{"payStatus": "jfqrz", "dueAmount": 10000}],
)
def test_tfqrz_blocking_rejected(self):
with self.assertRaises(ValueError):
validate_payment_details_for_save(
"dj",
10000,
None,
[{"payStatus": "tfqrz", "dueAmount": 10000}],
)
if __name__ == "__main__":
unittest.main()