feat(fusion_accounting_ai): add ReportsAdapter with trial_balance

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-04-18 23:14:41 -04:00
parent d331dc5fa6
commit 086b24ab36
3 changed files with 74 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
from .base import DataAdapter, AdapterMode
from ._registry import get_adapter, register_adapter
# Side-effect imports: each adapter module calls register_adapter at module load.
from . import bank_rec # noqa: F401
from . import bank_rec # noqa: F401
from . import reports # noqa: F401
__all__ = ['DataAdapter', 'AdapterMode', 'get_adapter', 'register_adapter']

View File

@@ -0,0 +1,56 @@
"""Reports data adapter.
Routes report-data lookups across:
- FUSION: fusion.account.report (added by fusion_accounting_reports, Phase 2)
- ENTERPRISE: account.report from account_reports
- COMMUNITY: raw aggregations on account.move.line
"""
from .base import DataAdapter
from ._registry import register_adapter
class ReportsAdapter(DataAdapter):
FUSION_MODEL = 'fusion.account.report'
ENTERPRISE_MODULE = 'account_reports'
def trial_balance(self, date_to=None, company_ids=None):
return self._dispatch('trial_balance', date_to=date_to, company_ids=company_ids)
def trial_balance_via_fusion(self, date_to=None, company_ids=None):
# Phase 2 will implement; for now defer to community.
return self.trial_balance_via_community(date_to=date_to, company_ids=company_ids)
def trial_balance_via_enterprise(self, date_to=None, company_ids=None):
# Enterprise account_reports has rich filters; for AI-tool consumption,
# the community shape suffices and avoids brittle coupling to Odoo's
# report-line internals.
return self.trial_balance_via_community(date_to=date_to, company_ids=company_ids)
def trial_balance_via_community(self, date_to=None, company_ids=None):
domain = [('parent_state', '=', 'posted')]
if date_to:
domain.append(('date', '<=', date_to))
if company_ids:
domain.append(('company_id', 'in', list(company_ids)))
Line = self.env['account.move.line'].sudo()
groups = Line._read_group(
domain=domain,
groupby=['account_id'],
aggregates=['debit:sum', 'credit:sum'],
)
return [
{
'account_id': account.id,
'account_code': account.code,
'account_name': account.name,
'debit': debit_sum,
'credit': credit_sum,
'balance': debit_sum - credit_sum,
}
for account, debit_sum, credit_sum in groups
]
register_adapter('reports', ReportsAdapter)

View File

@@ -58,3 +58,19 @@ class TestBankRecAdapter(TransactionCase):
ids = [r['id'] for r in rows]
self.assertIn(self.line.id, ids,
f"Expected line {self.line.id} in unreconciled list, got: {ids}")
@tagged('post_install', '-at_install')
class TestReportsAdapter(TransactionCase):
"""Verify the reports adapter computes a trial-balance-shaped result."""
def test_trial_balance_returns_rows_in_pure_community(self):
adapter = get_adapter(self.env, 'reports')
# Compute an empty-filter trial balance for the current company. Should
# return a list (possibly empty in a fresh test DB) without errors.
result = adapter.trial_balance()
self.assertIsInstance(result, list)
# Each row should have account_id and balance keys
for row in result:
self.assertIn('account_id', row)
self.assertIn('balance', row)