from datetime import date, timedelta from odoo.tests.common import TransactionCase from odoo.tests import tagged from odoo.addons.fusion_accounting_followup.services.level_resolver import ( FollowupLevelSpec, resolve_level, ) from odoo.addons.fusion_accounting_followup.services.overdue_aging import compute_aging @tagged('post_install', '-at_install') class TestLevelResolver(TransactionCase): def setUp(self): super().setUp() self.levels = [ FollowupLevelSpec(sequence=1, name='Reminder', delay_days=7, tone='gentle'), FollowupLevelSpec(sequence=2, name='Warning', delay_days=30, tone='firm'), FollowupLevelSpec(sequence=3, name='Legal Notice', delay_days=60, tone='legal'), ] def test_no_overdue_returns_none(self): as_of = date(2026, 4, 19) lines = [{'date_maturity': as_of + timedelta(days=10), 'amount_residual': 100}] report = compute_aging(move_lines=lines, as_of=as_of) result = resolve_level(aging_report=report, levels=self.levels) self.assertIsNone(result) def test_15_days_overdue_picks_reminder(self): as_of = date(2026, 4, 19) lines = [{'date_maturity': as_of - timedelta(days=15), 'amount_residual': 100}] report = compute_aging(move_lines=lines, as_of=as_of) result = resolve_level(aging_report=report, levels=self.levels) self.assertEqual(result.name, 'Reminder') def test_45_days_overdue_picks_warning(self): as_of = date(2026, 4, 19) lines = [{'date_maturity': as_of - timedelta(days=45), 'amount_residual': 200}] report = compute_aging(move_lines=lines, as_of=as_of) result = resolve_level(aging_report=report, levels=self.levels) self.assertEqual(result.name, 'Warning') def test_75_days_overdue_picks_legal(self): as_of = date(2026, 4, 19) lines = [{'date_maturity': as_of - timedelta(days=75), 'amount_residual': 300}] report = compute_aging(move_lines=lines, as_of=as_of) result = resolve_level(aging_report=report, levels=self.levels) self.assertEqual(result.name, 'Legal Notice') def test_no_levels_returns_none(self): as_of = date(2026, 4, 19) lines = [{'date_maturity': as_of - timedelta(days=30), 'amount_residual': 100}] report = compute_aging(move_lines=lines, as_of=as_of) result = resolve_level(aging_report=report, levels=[]) self.assertIsNone(result) def test_invalid_tone_raises(self): with self.assertRaises(ValueError): FollowupLevelSpec(sequence=1, name='X', delay_days=7, tone='invalid')