diff --git a/fusion_accounting_followup/__manifest__.py b/fusion_accounting_followup/__manifest__.py index b29628b8..0ffeb741 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.8', + 'version': '19.0.1.0.9', 'category': 'Accounting/Accounting', 'summary': 'AI-augmented customer follow-ups (dunning) for unpaid invoices.', 'description': """ diff --git a/fusion_accounting_followup/models/__init__.py b/fusion_accounting_followup/models/__init__.py index 6679c497..76c956a5 100644 --- a/fusion_accounting_followup/models/__init__.py +++ b/fusion_accounting_followup/models/__init__.py @@ -1,3 +1,4 @@ from . import fusion_followup_level from . import fusion_followup_run from . import fusion_followup_text_cache +from . import res_partner diff --git a/fusion_accounting_followup/models/res_partner.py b/fusion_accounting_followup/models/res_partner.py new file mode 100644 index 00000000..2aaa5936 --- /dev/null +++ b/fusion_accounting_followup/models/res_partner.py @@ -0,0 +1,52 @@ +"""Inherit res.partner: add follow-up state fields.""" + +from odoo import _, api, fields, models + + +FOLLOWUP_STATUS = [ + ('no_action', 'No Action Needed'), + ('action_due', 'Action Due'), + ('paused', 'Paused'), + ('blocked', 'Blocked'), + ('with_credit_team', 'With Credit Team'), +] + + +class ResPartner(models.Model): + _inherit = "res.partner" + + fusion_followup_status = fields.Selection( + FOLLOWUP_STATUS, default='no_action', tracking=True, + help="Current follow-up status as computed by the engine.") + fusion_followup_paused_until = fields.Date( + tracking=True, + help="Pause follow-ups for this partner until this date.") + fusion_followup_last_level_id = fields.Many2one( + 'fusion.followup.level', + help="The most-recent follow-up level this partner has been contacted at.") + fusion_followup_last_run_date = fields.Datetime(readonly=True) + fusion_followup_run_ids = fields.One2many( + 'fusion.followup.run', 'partner_id', string='Follow-up History') + fusion_followup_run_count = fields.Integer( + compute='_compute_fusion_followup_run_count') + fusion_followup_risk_score = fields.Integer( + readonly=True, default=0, + help="Latest computed payment risk (0-100). Updated by cron.") + fusion_followup_risk_band = fields.Selection([ + ('low', 'Low'), ('medium', 'Medium'), + ('high', 'High'), ('critical', 'Critical'), + ], default='low', readonly=True) + + def _compute_fusion_followup_run_count(self): + for partner in self: + partner.fusion_followup_run_count = len(partner.fusion_followup_run_ids) + + def action_view_followup_history(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'fusion.followup.run', + 'view_mode': 'list,form', + 'domain': [('partner_id', '=', self.id)], + 'context': {'default_partner_id': self.id}, + } diff --git a/fusion_accounting_followup/tests/__init__.py b/fusion_accounting_followup/tests/__init__.py index 595ca547..803fa97a 100644 --- a/fusion_accounting_followup/tests/__init__.py +++ b/fusion_accounting_followup/tests/__init__.py @@ -6,3 +6,4 @@ from . import test_followup_text_generator from . import test_fusion_followup_level from . import test_fusion_followup_run from . import test_fusion_followup_text_cache +from . import test_res_partner_inherit diff --git a/fusion_accounting_followup/tests/test_res_partner_inherit.py b/fusion_accounting_followup/tests/test_res_partner_inherit.py new file mode 100644 index 00000000..fa77651e --- /dev/null +++ b/fusion_accounting_followup/tests/test_res_partner_inherit.py @@ -0,0 +1,27 @@ +from odoo.tests.common import TransactionCase +from odoo.tests import tagged + + +@tagged('post_install', '-at_install') +class TestResPartnerFollowup(TransactionCase): + + def test_default_status_no_action(self): + partner = self.env['res.partner'].create({'name': 'Default Status'}) + self.assertEqual(partner.fusion_followup_status, 'no_action') + self.assertEqual(partner.fusion_followup_risk_band, 'low') + self.assertEqual(partner.fusion_followup_risk_score, 0) + + def test_run_count_reflects_history(self): + partner = self.env['res.partner'].create({'name': 'History Partner'}) + self.assertEqual(partner.fusion_followup_run_count, 0) + for _ in range(3): + self.env['fusion.followup.run'].create({'partner_id': partner.id}) + partner.invalidate_recordset(['fusion_followup_run_count', 'fusion_followup_run_ids']) + self.assertEqual(partner.fusion_followup_run_count, 3) + + def test_action_view_followup_history_returns_action(self): + partner = self.env['res.partner'].create({'name': 'Action Partner'}) + action = partner.action_view_followup_history() + self.assertEqual(action['res_model'], 'fusion.followup.run') + self.assertEqual(action['domain'], [('partner_id', '=', partner.id)]) + self.assertEqual(action['context']['default_partner_id'], partner.id)