From 63f3e0ec142cabdc2fae6580c59b8810629d0e1e Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sun, 19 Apr 2026 20:39:17 -0400 Subject: [PATCH] feat(fusion_accounting_followup): tone_selector service Made-with: Cursor --- fusion_accounting_followup/__manifest__.py | 2 +- .../services/__init__.py | 1 + .../services/tone_selector.py | 18 +++++++++++++ fusion_accounting_followup/tests/__init__.py | 1 + .../tests/test_tone_selector.py | 25 +++++++++++++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 fusion_accounting_followup/services/tone_selector.py create mode 100644 fusion_accounting_followup/tests/test_tone_selector.py diff --git a/fusion_accounting_followup/__manifest__.py b/fusion_accounting_followup/__manifest__.py index 235a17ba..cd00c486 100644 --- a/fusion_accounting_followup/__manifest__.py +++ b/fusion_accounting_followup/__manifest__.py @@ -1,6 +1,6 @@ { 'name': 'Fusion Accounting Follow-up', - 'version': '19.0.1.0.3', + 'version': '19.0.1.0.4', 'category': 'Accounting/Accounting', 'summary': 'AI-augmented customer follow-ups (dunning) for unpaid invoices.', 'description': """ diff --git a/fusion_accounting_followup/services/__init__.py b/fusion_accounting_followup/services/__init__.py index 1758e546..1338970d 100644 --- a/fusion_accounting_followup/services/__init__.py +++ b/fusion_accounting_followup/services/__init__.py @@ -1,3 +1,4 @@ from . import overdue_aging from . import level_resolver from . import risk_scorer +from . import tone_selector diff --git a/fusion_accounting_followup/services/tone_selector.py b/fusion_accounting_followup/services/tone_selector.py new file mode 100644 index 00000000..e77ecd3d --- /dev/null +++ b/fusion_accounting_followup/services/tone_selector.py @@ -0,0 +1,18 @@ +"""Tone selector: pick gentle/firm/legal based on follow-up level + risk score.""" + +TONE_BY_LEVEL = { + 1: 'gentle', + 2: 'firm', + 3: 'legal', + 4: 'legal', +} + + +def select_tone(*, level_sequence: int, risk_score: int = 0) -> str: + """Default tone follows level sequence; high risk can escalate.""" + base_tone = TONE_BY_LEVEL.get(level_sequence, 'gentle') + if risk_score >= 80 and base_tone == 'gentle': + return 'firm' + if risk_score >= 90 and base_tone == 'firm': + return 'legal' + return base_tone diff --git a/fusion_accounting_followup/tests/__init__.py b/fusion_accounting_followup/tests/__init__.py index 5ad09c02..d50dde45 100644 --- a/fusion_accounting_followup/tests/__init__.py +++ b/fusion_accounting_followup/tests/__init__.py @@ -1,3 +1,4 @@ from . import test_overdue_aging from . import test_level_resolver from . import test_risk_scorer +from . import test_tone_selector diff --git a/fusion_accounting_followup/tests/test_tone_selector.py b/fusion_accounting_followup/tests/test_tone_selector.py new file mode 100644 index 00000000..f7df4f63 --- /dev/null +++ b/fusion_accounting_followup/tests/test_tone_selector.py @@ -0,0 +1,25 @@ +from odoo.tests.common import TransactionCase +from odoo.tests import tagged +from odoo.addons.fusion_accounting_followup.services.tone_selector import select_tone + + +@tagged('post_install', '-at_install') +class TestToneSelector(TransactionCase): + + def test_level_1_default_gentle(self): + self.assertEqual(select_tone(level_sequence=1), 'gentle') + + def test_level_2_default_firm(self): + self.assertEqual(select_tone(level_sequence=2), 'firm') + + def test_level_3_default_legal(self): + self.assertEqual(select_tone(level_sequence=3), 'legal') + + def test_critical_risk_escalates_gentle_to_firm(self): + self.assertEqual(select_tone(level_sequence=1, risk_score=85), 'firm') + + def test_extreme_risk_escalates_firm_to_legal(self): + self.assertEqual(select_tone(level_sequence=2, risk_score=95), 'legal') + + def test_unknown_level_defaults_gentle(self): + self.assertEqual(select_tone(level_sequence=99), 'gentle')