fix(fusion_accounting_reports): expose dynamic OWL reports as menu items

User reported that after Enterprise uninstall, clicking 'Reports' opened
PDF statements instead of the dynamic Fusion report viewer. Root cause:
the OWL ReportViewer (registered as view_type='fusion_reports') was only
reachable via the period-picker WIZARD; no menu items used the OWL view
directly. Plus the JS service ignored report_code, so even within the
viewer, all PnL-typed reports rendered the canonical P&L line_specs.

Changes:

JS layer
- reports_service.js: runReport now accepts and forwards reportCode;
  state tracks currentReportCode so re-runs after period/comparison
  changes preserve the variant.
- report_viewer.js: reads default_report_code (and default_comparison)
  from the action context.
- period_filter.js: passes the cached reportCode on date changes;
  clears it when the user picks a different report_type.

Backend
- New fusion_accounting_reports/views/report_actions.xml with 11
  dedicated ir.actions.act_window records, one per built-in report
  (P&L, Balance Sheet, Trial Balance, GL, Cash Flow, Executive Summary,
  Annual Statements, Aged Receivable, Aged Payable, Partner Ledger,
  Tax Summary). Each opens view_mode='fusion_reports' with the
  appropriate default_report_type + default_report_code context.
- views/menu_views.xml: each report now gets its own menu item
  directly under Accounting > Reporting (sequence 10-40), matching
  Enterprise's flat structure. Custom Period wizard, XLSX export and
  Anomaly browser collected under a 'Tools' sub-group at the bottom.
- fusion_accounting_l10n_ca: adds menu items for 'Profit and Loss
  (Canada)' and 'Balance Sheet (Canada)' as siblings, plus a 'Tax
  Returns (CA)' configuration menu.

Verified live on westin-v19:
- pnl rendering 3 rows, cash_flow 9, executive_summary 7,
  annual_statements 5, ca_profit_loss 9 \u2014 each report now renders
  its own line_specs correctly.
- Reporting menu shows 14 Fusion report entries + Tools group.
- 136/136 reports + l10n_ca tests pass.

Version bumps: reports 19.0.1.1.1, l10n_ca 19.0.1.1.0.

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-04-20 01:11:48 -04:00
parent 867b5f71a1
commit 8217bb0ff6
8 changed files with 277 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
{ {
'name': 'Fusion Accounting — Canadian Reports', 'name': 'Fusion Accounting — Canadian Reports',
'version': '19.0.1.0.0', 'version': '19.0.1.1.0',
'category': 'Accounting/Localizations/Reporting', 'category': 'Accounting/Localizations/Reporting',
'summary': 'Canadian-specific report definitions and tax return templates for Fusion Accounting.', 'summary': 'Canadian-specific report definitions and tax return templates for Fusion Accounting.',
'description': """ 'description': """
@@ -21,6 +21,7 @@ Auto-installs when l10n_ca + fusion_accounting_reports are both present.
'data/fusion_tax_return_data.xml', 'data/fusion_tax_return_data.xml',
'data/report_ca_balance_sheet.xml', 'data/report_ca_balance_sheet.xml',
'data/report_ca_profit_loss.xml', 'data/report_ca_profit_loss.xml',
'views/menu_views.xml',
], ],
'auto_install': ['l10n_ca', 'fusion_accounting_reports'], 'auto_install': ['l10n_ca', 'fusion_accounting_reports'],
'installable': True, 'installable': True,

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
Canadian-flavored P&L + BS menus. Live alongside the rest of the
Fusion reports under Accounting > Reporting. Open the OWL
ReportViewer with the Canadian report_code so the line_specs
come from the Canadian definitions seeded in this module.
-->
<record id="action_fusion_report_ca_pnl" model="ir.actions.act_window">
<field name="name">Profit and Loss (Canada)</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'pnl', 'default_report_code': 'ca_profit_loss', 'default_comparison': 'previous_year'}</field>
</record>
<record id="action_fusion_report_ca_bs" model="ir.actions.act_window">
<field name="name">Balance Sheet (Canada)</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'balance_sheet', 'default_report_code': 'ca_balance_sheet', 'default_comparison': 'previous_period'}</field>
</record>
<menuitem id="menu_fusion_report_ca_pnl"
name="Profit and Loss (Canada)"
parent="account.menu_finance_reports"
action="action_fusion_report_ca_pnl"
sequence="15"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_report_ca_bs"
name="Balance Sheet (Canada)"
parent="account.menu_finance_reports"
action="action_fusion_report_ca_bs"
sequence="16"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<!-- Tax-return tracking list -->
<record id="action_fusion_tax_return" model="ir.actions.act_window">
<field name="name">Tax Returns</field>
<field name="res_model">fusion.tax.return</field>
<field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">No tax returns recorded yet</p>
<p>Track GST/HST/PST/T4/T5018/payroll-remittance filings.
Each return covers a (date_from, date_to) window and moves
from draft \u2192 to-file \u2192 filed as you submit it.</p>
</field>
</record>
<menuitem id="menu_fusion_tax_return"
name="Tax Returns (CA)"
parent="account.menu_finance_configuration"
action="action_fusion_tax_return"
sequence="100"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
</odoo>

View File

@@ -1,6 +1,6 @@
{ {
'name': 'Fusion Accounting Reports', 'name': 'Fusion Accounting Reports',
'version': '19.0.1.1.0', 'version': '19.0.1.1.1',
'category': 'Accounting/Accounting', 'category': 'Accounting/Accounting',
'summary': 'AI-augmented financial reports (P&L, balance sheet, trial balance, GL).', 'summary': 'AI-augmented financial reports (P&L, balance sheet, trial balance, GL).',
'description': """ 'description': """
@@ -47,6 +47,7 @@ menu hides; the engine and AI tools remain available for the chat.
'reports/report_pdf_template.xml', 'reports/report_pdf_template.xml',
'wizards/xlsx_export_wizard_views.xml', 'wizards/xlsx_export_wizard_views.xml',
'wizards/period_picker_wizard_views.xml', 'wizards/period_picker_wizard_views.xml',
'views/report_actions.xml',
'views/menu_views.xml', 'views/menu_views.xml',
], ],
'external_dependencies': { 'external_dependencies': {

View File

@@ -15,9 +15,11 @@ export class PeriodFilter extends Component {
async onReportTypeChange(ev) { async onReportTypeChange(ev) {
const reportType = ev.target.value; const reportType = ev.target.value;
if (reportType && this.state.dateFrom && this.state.dateTo) { if (reportType && this.state.dateFrom && this.state.dateTo) {
// Switching report type clears the report_code (user is picking
// a different category, not a variant).
await this.reports.runReport( await this.reports.runReport(
reportType, this.state.dateFrom, this.state.dateTo, reportType, this.state.dateFrom, this.state.dateTo,
this.state.comparison); this.state.comparison, null);
} }
} }
@@ -27,7 +29,8 @@ export class PeriodFilter extends Component {
await this.reports.runReport( await this.reports.runReport(
this.state.currentReportType, this.state.currentReportType,
this.state.dateFrom, this.state.dateTo, this.state.dateFrom, this.state.dateTo,
this.state.comparison); this.state.comparison,
this.state.currentReportCode);
} }
} }

View File

@@ -16,6 +16,7 @@ export class ReportsService {
this.state = reactive({ this.state = reactive({
availableReports: [], availableReports: [],
currentReportType: null, currentReportType: null,
currentReportCode: null,
currentResult: null, currentResult: null,
currentAnomalies: [], currentAnomalies: [],
currentCommentary: null, currentCommentary: null,
@@ -41,15 +42,17 @@ export class ReportsService {
} }
} }
async runReport(reportType, dateFrom, dateTo, comparison = 'none') { async runReport(reportType, dateFrom, dateTo, comparison = 'none', reportCode = null) {
this.state.isLoading = true; this.state.isLoading = true;
this.state.currentReportType = reportType; this.state.currentReportType = reportType;
this.state.currentReportCode = reportCode;
this.state.dateFrom = dateFrom; this.state.dateFrom = dateFrom;
this.state.dateTo = dateTo; this.state.dateTo = dateTo;
this.state.comparison = comparison; this.state.comparison = comparison;
try { try {
this.state.currentResult = await this.rpc(`${ENDPOINT_BASE}/run`, { this.state.currentResult = await this.rpc(`${ENDPOINT_BASE}/run`, {
report_type: reportType, report_type: reportType,
report_code: reportCode,
date_from: dateFrom, date_from: dateFrom,
date_to: dateTo, date_to: dateTo,
comparison: comparison, comparison: comparison,
@@ -136,7 +139,8 @@ export class ReportsService {
this.state.comparison = mode; this.state.comparison = mode;
if (this.state.currentReportType) { if (this.state.currentReportType) {
return this.runReport(this.state.currentReportType, return this.runReport(this.state.currentReportType,
this.state.dateFrom, this.state.dateTo, mode); this.state.dateFrom, this.state.dateTo, mode,
this.state.currentReportCode);
} }
} }
} }

View File

@@ -22,6 +22,11 @@ export class ReportViewer extends Component {
const ctx = this.props.action?.context || {}; const ctx = this.props.action?.context || {};
const reportType = ctx.default_report_type || 'pnl'; const reportType = ctx.default_report_type || 'pnl';
// default_report_code lets multiple reports of the same type
// (e.g. pnl, cash_flow, executive_summary, annual_statements all
// type='pnl') resolve to their own line_specs.
const reportCode = ctx.default_report_code || null;
const comparison = ctx.default_comparison || 'none';
const companyId = this.env.services.user?.context?.allowed_company_ids?.[0]; const companyId = this.env.services.user?.context?.allowed_company_ids?.[0];
onWillStart(async () => { onWillStart(async () => {
@@ -29,7 +34,8 @@ export class ReportViewer extends Component {
const today = new Date(); const today = new Date();
const year = today.getFullYear(); const year = today.getFullYear();
await this.reports.runReport( await this.reports.runReport(
reportType, `${year}-01-01`, `${year}-12-31`, 'none'); reportType, `${year}-01-01`, `${year}-12-31`,
comparison, reportCode);
}); });
} }

View File

@@ -1,23 +1,111 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<!-- Lives under Community Accounting "Reporting" sub-menu. Only visible <!--
when Enterprise's account_reports is absent. --> Fusion dynamic financial reports live as direct children of the
<menuitem id="menu_fusion_reports_root" Community "Reporting" sub-menu (account.menu_finance_reports),
name="Financial Reports" sitting alongside Community's PDF-based "Statement Reports" /
"Partner Reports" / "Taxes & Fiscal" / "Management" wrappers.
Each menu opens an act_window with view_mode='fusion_reports'
(the OWL ReportViewer). report_actions.xml defines the actions.
All gated to the coexistence group so they only appear when
Enterprise's account_reports is uninstalled.
-->
<!-- Top of the list \u2014 daily-driver statements -->
<menuitem id="menu_fusion_reports_pnl"
name="Profit and Loss"
parent="account.menu_finance_reports" parent="account.menu_finance_reports"
sequence="2" action="action_fusion_report_pnl"
sequence="10"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_bs"
name="Balance Sheet"
parent="account.menu_finance_reports"
action="action_fusion_report_balance_sheet"
sequence="11"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_executive_summary"
name="Executive Summary"
parent="account.menu_finance_reports"
action="action_fusion_report_executive_summary"
sequence="12"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_cash_flow"
name="Cash Flow Statement"
parent="account.menu_finance_reports"
action="action_fusion_report_cash_flow"
sequence="13"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_annual_statements"
name="Annual Statements"
parent="account.menu_finance_reports"
action="action_fusion_report_annual_statements"
sequence="14"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/> groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<!-- Audit / drill-down statements -->
<menuitem id="menu_fusion_reports_tb"
name="Trial Balance"
parent="account.menu_finance_reports"
action="action_fusion_report_trial_balance"
sequence="20"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_gl"
name="General Ledger"
parent="account.menu_finance_reports"
action="action_fusion_report_general_ledger"
sequence="21"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<!-- Partner-grouped -->
<menuitem id="menu_fusion_reports_aged_receivable"
name="Aged Receivable"
parent="account.menu_finance_reports"
action="action_fusion_report_aged_receivable"
sequence="30"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_aged_payable"
name="Aged Payable"
parent="account.menu_finance_reports"
action="action_fusion_report_aged_payable"
sequence="31"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_partner_ledger"
name="Partner Ledger"
parent="account.menu_finance_reports"
action="action_fusion_report_partner_ledger"
sequence="32"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<!-- Tax -->
<menuitem id="menu_fusion_reports_tax"
name="Tax Summary"
parent="account.menu_finance_reports"
action="action_fusion_report_tax_summary"
sequence="40"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<!--
Tools group at the bottom: custom-period picker, XLSX export wizard,
anomaly browser. Less-frequently used; bundled together so they
don't clutter the report list.
-->
<menuitem id="menu_fusion_reports_tools_group"
name="Tools"
parent="account.menu_finance_reports"
sequence="90"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_open" <menuitem id="menu_fusion_reports_open"
name="Open Report..." name="Custom Period..."
parent="menu_fusion_reports_root" parent="menu_fusion_reports_tools_group"
action="action_fusion_period_picker_wizard" action="action_fusion_period_picker_wizard"
sequence="10" sequence="10"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/> groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
<menuitem id="menu_fusion_reports_xlsx" <menuitem id="menu_fusion_reports_xlsx"
name="Export to XLSX..." name="Export to XLSX..."
parent="menu_fusion_reports_root" parent="menu_fusion_reports_tools_group"
action="action_fusion_xlsx_export_wizard" action="action_fusion_xlsx_export_wizard"
sequence="20" sequence="20"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/> groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>
@@ -30,7 +118,7 @@
<menuitem id="menu_fusion_reports_anomalies" <menuitem id="menu_fusion_reports_anomalies"
name="Anomalies" name="Anomalies"
parent="menu_fusion_reports_root" parent="menu_fusion_reports_tools_group"
action="action_fusion_report_anomaly_list" action="action_fusion_report_anomaly_list"
sequence="30" sequence="30"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/> groups="fusion_accounting_core.group_fusion_show_when_enterprise_absent"/>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
One ir.actions.act_window per built-in report, each opens the OWL
ReportViewer (view_mode='fusion_reports'). The viewer reads
``default_report_type`` and ``default_report_code`` from the action
context to pick which fusion.report to render.
New menus per-report live below; users no longer need to go through
the period-picker wizard for the standard reports.
-->
<!-- ============================================================
CORE REPORTS (one per Fusion engine method)
============================================================ -->
<record id="action_fusion_report_pnl" model="ir.actions.act_window">
<field name="name">Profit and Loss</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'pnl', 'default_report_code': 'pnl'}</field>
</record>
<record id="action_fusion_report_balance_sheet" model="ir.actions.act_window">
<field name="name">Balance Sheet</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'balance_sheet', 'default_report_code': 'balance_sheet'}</field>
</record>
<record id="action_fusion_report_trial_balance" model="ir.actions.act_window">
<field name="name">Trial Balance</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'trial_balance', 'default_report_code': 'trial_balance'}</field>
</record>
<record id="action_fusion_report_general_ledger" model="ir.actions.act_window">
<field name="name">General Ledger</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'general_ledger', 'default_report_code': 'general_ledger'}</field>
</record>
<!-- ============================================================
SECONDARY PnL VARIANTS (engine.compute_pnl with code)
============================================================ -->
<record id="action_fusion_report_cash_flow" model="ir.actions.act_window">
<field name="name">Cash Flow Statement</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'pnl', 'default_report_code': 'cash_flow', 'default_comparison': 'previous_year'}</field>
</record>
<record id="action_fusion_report_executive_summary" model="ir.actions.act_window">
<field name="name">Executive Summary</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'pnl', 'default_report_code': 'executive_summary', 'default_comparison': 'previous_year'}</field>
</record>
<record id="action_fusion_report_annual_statements" model="ir.actions.act_window">
<field name="name">Annual Statements</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'pnl', 'default_report_code': 'annual_statements', 'default_comparison': 'previous_year'}</field>
</record>
<record id="action_fusion_report_tax_summary" model="ir.actions.act_window">
<field name="name">Tax Summary</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'trial_balance', 'default_report_code': 'tax_summary'}</field>
</record>
<!-- ============================================================
PARTNER-GROUPED REPORTS
============================================================ -->
<record id="action_fusion_report_aged_receivable" model="ir.actions.act_window">
<field name="name">Aged Receivable</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'aged_receivable', 'default_report_code': 'aged_receivable'}</field>
</record>
<record id="action_fusion_report_aged_payable" model="ir.actions.act_window">
<field name="name">Aged Payable</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'aged_payable', 'default_report_code': 'aged_payable'}</field>
</record>
<record id="action_fusion_report_partner_ledger" model="ir.actions.act_window">
<field name="name">Partner Ledger</field>
<field name="res_model">fusion.report</field>
<field name="view_mode">fusion_reports</field>
<field name="context">{'default_report_type': 'partner_ledger', 'default_report_code': 'partner_ledger'}</field>
</record>
</odoo>