From 22b277c6b88f6c44db816d8be20557c33d5d88e6 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sun, 19 Apr 2026 15:31:22 -0400 Subject: [PATCH] feat(fusion_accounting_reports): fusion.report.commentary cache model Made-with: Cursor --- fusion_accounting_reports/__manifest__.py | 2 +- fusion_accounting_reports/models/__init__.py | 1 + .../models/fusion_report_commentary.py | 43 +++++++++++++++ .../security/ir.model.access.csv | 1 + fusion_accounting_reports/tests/__init__.py | 1 + .../tests/test_fusion_report_commentary.py | 53 +++++++++++++++++++ 6 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 fusion_accounting_reports/models/fusion_report_commentary.py create mode 100644 fusion_accounting_reports/tests/test_fusion_report_commentary.py diff --git a/fusion_accounting_reports/__manifest__.py b/fusion_accounting_reports/__manifest__.py index 50a6cab9..b0d633b0 100644 --- a/fusion_accounting_reports/__manifest__.py +++ b/fusion_accounting_reports/__manifest__.py @@ -1,6 +1,6 @@ { 'name': 'Fusion Accounting Reports', - 'version': '19.0.1.0.11', + 'version': '19.0.1.0.12', 'category': 'Accounting/Accounting', 'summary': 'AI-augmented financial reports (P&L, balance sheet, trial balance, GL).', 'description': """ diff --git a/fusion_accounting_reports/models/__init__.py b/fusion_accounting_reports/models/__init__.py index 4a3eb8fd..0e9fa527 100644 --- a/fusion_accounting_reports/models/__init__.py +++ b/fusion_accounting_reports/models/__init__.py @@ -1,2 +1,3 @@ from . import fusion_report from . import fusion_report_engine +from . import fusion_report_commentary diff --git a/fusion_accounting_reports/models/fusion_report_commentary.py b/fusion_accounting_reports/models/fusion_report_commentary.py new file mode 100644 index 00000000..36278bfb --- /dev/null +++ b/fusion_accounting_reports/models/fusion_report_commentary.py @@ -0,0 +1,43 @@ +"""Cached AI-generated commentary for a report run. + +One row per (report, period_from, period_to, comparison_mode, company). +Refreshed on demand or via cron when the underlying data has changed.""" + +from odoo import _, api, fields, models + + +class FusionReportCommentary(models.Model): + _name = "fusion.report.commentary" + _description = "AI-Generated Report Commentary Cache" + _order = "generated_at desc" + + report_id = fields.Many2one('fusion.report', required=True, ondelete='cascade') + company_id = fields.Many2one('res.company', required=True, + default=lambda self: self.env.company) + period_from = fields.Date(required=True) + period_to = fields.Date(required=True) + comparison_mode = fields.Selection([ + ('none', 'None'), + ('previous_period', 'Previous Period'), + ('previous_year', 'Previous Year'), + ], default='none', required=True) + + summary = fields.Text() + highlights = fields.Json() # list of strings + concerns = fields.Json() # list of strings + next_actions = fields.Json() # list of strings + + generated_at = fields.Datetime(default=fields.Datetime.now, required=True) + generated_by = fields.Selection([ + ('on_demand', 'On Demand'), + ('cron', 'Cron'), + ('templated', 'Templated Fallback'), + ], default='on_demand', required=True) + + provider = fields.Char(help="LLM provider used (e.g. 'openai', 'claude', 'local'). " + "Empty for templated fallback.") + + _unique_period = models.Constraint( + 'UNIQUE(report_id, company_id, period_from, period_to, comparison_mode)', + 'Only one commentary cache row per report+period+mode.', + ) diff --git a/fusion_accounting_reports/security/ir.model.access.csv b/fusion_accounting_reports/security/ir.model.access.csv index 2cdf3a4b..e5ffcdcb 100644 --- a/fusion_accounting_reports/security/ir.model.access.csv +++ b/fusion_accounting_reports/security/ir.model.access.csv @@ -1,3 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_fusion_report_user,fusion.report.user,model_fusion_report,base.group_user,1,0,0,0 access_fusion_report_admin,fusion.report.admin,model_fusion_report,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1 +access_fusion_report_commentary,fusion.report.commentary,model_fusion_report_commentary,base.group_user,1,1,1,0 diff --git a/fusion_accounting_reports/tests/__init__.py b/fusion_accounting_reports/tests/__init__.py index 1d9a597b..decddc27 100644 --- a/fusion_accounting_reports/tests/__init__.py +++ b/fusion_accounting_reports/tests/__init__.py @@ -8,3 +8,4 @@ from . import test_seeded_reports from . import test_anomaly_detection from . import test_commentary_prompt from . import test_commentary_generator +from . import test_fusion_report_commentary diff --git a/fusion_accounting_reports/tests/test_fusion_report_commentary.py b/fusion_accounting_reports/tests/test_fusion_report_commentary.py new file mode 100644 index 00000000..7cc81e68 --- /dev/null +++ b/fusion_accounting_reports/tests/test_fusion_report_commentary.py @@ -0,0 +1,53 @@ +"""Tests for fusion.report.commentary cache model.""" + +from datetime import date +from odoo.tests.common import TransactionCase, tagged + + +@tagged('post_install', '-at_install') +class TestFusionReportCommentary(TransactionCase): + + def setUp(self): + super().setUp() + self.report = self.env.ref('fusion_accounting_reports.report_pnl') + + def test_create_minimal(self): + c = self.env['fusion.report.commentary'].create({ + 'report_id': self.report.id, + 'period_from': date(2026, 4, 1), + 'period_to': date(2026, 4, 30), + 'summary': 'Test summary.', + 'highlights': ['point 1', 'point 2'], + }) + self.assertEqual(c.summary, 'Test summary.') + self.assertEqual(c.highlights, ['point 1', 'point 2']) + self.assertEqual(c.generated_by, 'on_demand') + + def test_uniqueness_per_period(self): + self.env['fusion.report.commentary'].create({ + 'report_id': self.report.id, + 'period_from': date(2026, 4, 1), + 'period_to': date(2026, 4, 30), + 'comparison_mode': 'none', + }) + with self.assertRaises(Exception): + self.env['fusion.report.commentary'].create({ + 'report_id': self.report.id, + 'period_from': date(2026, 4, 1), + 'period_to': date(2026, 4, 30), + 'comparison_mode': 'none', + }) + + def test_different_comparison_modes_can_coexist(self): + for mode in ['none', 'previous_period', 'previous_year']: + self.env['fusion.report.commentary'].create({ + 'report_id': self.report.id, + 'period_from': date(2026, 5, 1), + 'period_to': date(2026, 5, 31), + 'comparison_mode': mode, + }) + count = self.env['fusion.report.commentary'].search_count([ + ('report_id', '=', self.report.id), + ('period_from', '=', date(2026, 5, 1)), + ]) + self.assertEqual(count, 3)