refactor(fusion_accounting_ai): route reporting tools through ReportsAdapter
Task 13 Step 9 of phase-0 plan.
All Enterprise account.report entry points now go through ReportsAdapter:
- get_profit_loss → ReportsAdapter.run_report(account_reports.profit_and_loss)
- get_balance_sheet → ReportsAdapter.run_report(account_reports.balance_sheet)
- get_trial_balance → ReportsAdapter.run_report(...) with Community fallback
to the existing trial_balance() account.move.line aggregation
- get_cash_flow → ReportsAdapter.run_report(account_reports.cash_flow_statement)
- compare_periods → two run_report() calls
- export_report → ReportsAdapter.export_report() (PDF/XLSX via Enterprise)
ReportsAdapter extended with:
- run_report(ref_id, date_from, date_to, limit) — generic Enterprise
account.report wrapper. Enterprise mode returns {report_name, lines};
Community mode returns a graceful error dict pointing users at the
raw trial_balance() aggregation tool.
- export_report(ref_id, fmt, date_from, date_to) — Enterprise-only PDF/XLSX
export; Community mode returns an error dict.
Pure-Community tools in reporting.py (get_invoicing_summary, get_billing_summary,
get_collections_summary) unchanged — they aggregate account.move /
account.payment directly which is tri-mode safe.
3 new data-adapter tests added for run_report happy/error paths and
export_report shape. Total: 12 tests, all passing on westin-v19.
Made-with: Cursor
This commit is contained in:
@@ -6,14 +6,22 @@ Routes report-data lookups across:
|
||||
- COMMUNITY: raw aggregations on account.move.line
|
||||
"""
|
||||
|
||||
import base64
|
||||
import logging
|
||||
|
||||
from .base import DataAdapter
|
||||
from ._registry import register_adapter
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ReportsAdapter(DataAdapter):
|
||||
FUSION_MODEL = 'fusion.account.report'
|
||||
ENTERPRISE_MODULE = 'account_reports'
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# trial_balance (Community-computable from account.move.line)
|
||||
# ------------------------------------------------------------------
|
||||
def trial_balance(self, date_to=None, company_ids=None):
|
||||
return self._dispatch('trial_balance', date_to=date_to, company_ids=company_ids)
|
||||
|
||||
@@ -52,5 +60,111 @@ class ReportsAdapter(DataAdapter):
|
||||
for account, debit_sum, credit_sum in groups
|
||||
]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# run_report — generic Enterprise account.report wrapper
|
||||
#
|
||||
# Returns either {'report_name', 'lines'} or {'error': ...}.
|
||||
# Used by profit_loss / balance_sheet / cash_flow / trial_balance_lines
|
||||
# tool wrappers that want Enterprise's hierarchical report shape when
|
||||
# available.
|
||||
# ------------------------------------------------------------------
|
||||
def run_report(self, ref_id, date_from=None, date_to=None, limit=100):
|
||||
return self._dispatch(
|
||||
'run_report',
|
||||
ref_id=ref_id, date_from=date_from, date_to=date_to, limit=limit,
|
||||
)
|
||||
|
||||
def run_report_via_fusion(self, ref_id, date_from=None, date_to=None, limit=100):
|
||||
# Phase 2: fusion.account.report will implement equivalent rendering.
|
||||
return self.run_report_via_community(
|
||||
ref_id=ref_id, date_from=date_from, date_to=date_to, limit=limit,
|
||||
)
|
||||
|
||||
def run_report_via_enterprise(self, ref_id, date_from=None, date_to=None, limit=100):
|
||||
try:
|
||||
report = self.env.ref(ref_id, raise_if_not_found=False)
|
||||
except Exception:
|
||||
report = None
|
||||
if not report:
|
||||
return {'error': f'Report {ref_id} not found'}
|
||||
date_opts = {}
|
||||
if date_from:
|
||||
date_opts['date_from'] = date_from
|
||||
if date_to:
|
||||
date_opts['date_to'] = date_to
|
||||
options = report.get_options({'date': date_opts} if date_opts else {})
|
||||
lines = report._get_lines(options)
|
||||
return {
|
||||
'report_name': report.name,
|
||||
'lines': [{
|
||||
'name': line.get('name', ''),
|
||||
'level': line.get('level', 0),
|
||||
'columns': [c.get('no_format', c.get('name', '')) for c in line.get('columns', [])],
|
||||
} for line in lines[:limit]],
|
||||
}
|
||||
|
||||
def run_report_via_community(self, ref_id, date_from=None, date_to=None, limit=100):
|
||||
return {
|
||||
'error': (
|
||||
f'Report {ref_id!r} is only available when account_reports (Enterprise) '
|
||||
'or a fusion reports module is installed. For pure Community installs, '
|
||||
'use the raw trial_balance() adapter method or the tools that aggregate '
|
||||
'account.move.line directly.'
|
||||
),
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# export_report — Enterprise-only PDF/XLSX export
|
||||
# ------------------------------------------------------------------
|
||||
def export_report(self, ref_id, fmt='pdf', date_from=None, date_to=None):
|
||||
return self._dispatch(
|
||||
'export_report',
|
||||
ref_id=ref_id, fmt=fmt, date_from=date_from, date_to=date_to,
|
||||
)
|
||||
|
||||
def export_report_via_fusion(self, ref_id, fmt='pdf', date_from=None, date_to=None):
|
||||
return self.export_report_via_community(
|
||||
ref_id=ref_id, fmt=fmt, date_from=date_from, date_to=date_to,
|
||||
)
|
||||
|
||||
def export_report_via_enterprise(self, ref_id, fmt='pdf', date_from=None, date_to=None):
|
||||
try:
|
||||
report = self.env.ref(ref_id, raise_if_not_found=False)
|
||||
except Exception:
|
||||
report = None
|
||||
if not report:
|
||||
return {'error': f'Report {ref_id} not found'}
|
||||
date_opts = {}
|
||||
if date_from:
|
||||
date_opts['date_from'] = date_from
|
||||
if date_to:
|
||||
date_opts['date_to'] = date_to
|
||||
options = report.get_options({'date': date_opts} if date_opts else {})
|
||||
try:
|
||||
if fmt == 'xlsx':
|
||||
result = report.dispatch_report_action(options, 'export_to_xlsx')
|
||||
else:
|
||||
result = report.dispatch_report_action(options, 'export_to_pdf')
|
||||
if isinstance(result, dict) and result.get('file_content'):
|
||||
return {
|
||||
'file_name': result.get('file_name', f'report.{fmt}'),
|
||||
'file_type': result.get('file_type', fmt),
|
||||
'file_content_b64': base64.b64encode(result['file_content']).decode(),
|
||||
}
|
||||
return {
|
||||
'status': 'generated',
|
||||
'message': f'Report exported as {fmt}. Use the Odoo UI to download.',
|
||||
}
|
||||
except Exception as e:
|
||||
return {'error': f'Export failed: {str(e)}'}
|
||||
|
||||
def export_report_via_community(self, ref_id, fmt='pdf', date_from=None, date_to=None):
|
||||
return {
|
||||
'error': (
|
||||
f'Exporting report {ref_id!r} is only available with Enterprise '
|
||||
'account_reports installed.'
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
register_adapter('reports', ReportsAdapter)
|
||||
|
||||
Reference in New Issue
Block a user