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>
171 lines
5.0 KiB
Python
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()
|