"""分期应缴日:小数月间隔(30 天/月)与提前天数规则""" import unittest from decimal import Decimal from app.services.protocol_payment_installment import ( calc_fq_installment_due_date, calc_installment_due_amounts_by_ratios, resolve_installment_due_amounts, ) class TestCalcFqInstallmentDueDate(unittest.TestCase): def test_first_period_equals_start_date(self): self.assertEqual(calc_fq_installment_due_date("20260604", 0, 0.5), "20260604") def test_fractional_interval_half_month_six_periods(self): start = "20260604" interval = 0.5 expected = [ "20260604", "20260520", "20260604", "20260619", "20260705", "20260720", ] for i, exp in enumerate(expected): self.assertEqual( calc_fq_installment_due_date(start, i, interval), exp, msg=f"period {i + 1}", ) def test_fractional_interval_0_1_advance_30_days(self): # +3 天 = 20260607,再提前 30 天 self.assertEqual(calc_fq_installment_due_date("20260604", 1, 0.1), "20260508") def test_integer_interval_six_months(self): # +6 日历月 = 20261204,再提前 30 天 self.assertEqual(calc_fq_installment_due_date("20260604", 1, 6), "20261104") def test_fractional_interval_0_3(self): # index=2 → 0.6 月 → 18 天 → 20260622,再提前 30 天 self.assertEqual(calc_fq_installment_due_date("20260604", 2, 0.3), "20260523") def test_custom_advance_days_15(self): self.assertEqual( calc_fq_installment_due_date("20260604", 1, 6, advance_days=15), "20261119", ) def test_max_contract_amount_is_split_in_decimal_cents_without_float_loss(self): total = Decimal("9999999999999999.99") amounts = resolve_installment_due_amounts(total, [], 2) self.assertEqual(amounts, [Decimal("4999999999999999.99"), Decimal("5000000000000000.00")]) self.assertEqual(sum(amounts), total) def test_max_contract_amount_ratio_split_keeps_exact_tail_difference(self): total = Decimal("9999999999999999.99") amounts = calc_installment_due_amounts_by_ratios(total, [50, 50]) self.assertEqual(amounts, [Decimal("4999999999999999.99"), Decimal("5000000000000000.00")]) self.assertEqual(sum(amounts), total) if __name__ == "__main__": unittest.main()