This commit is contained in:
gsinghpal
2026-05-16 13:18:52 -04:00
parent 191a9c82be
commit 9ebf89bde2
1080 changed files with 0 additions and 1197 deletions

View File

@@ -0,0 +1,2 @@
from . import xlsx_export_wizard
from . import period_picker_wizard

View File

@@ -0,0 +1,77 @@
"""Period selection + comparison wizard.
Pre-fills date ranges for common report periods (current month, YTD, etc.)."""
from datetime import timedelta
from odoo import api, fields, models
from ..services.date_periods import (
fiscal_year_bounds, month_bounds, quarter_bounds,
)
class FusionPeriodPickerWizard(models.TransientModel):
_name = "fusion.period.picker.wizard"
_description = "Period Selection Wizard"
report_type = fields.Selection([
('pnl', 'P&L'),
('balance_sheet', 'Balance Sheet'),
('trial_balance', 'Trial Balance'),
('general_ledger', 'General Ledger'),
], required=True, default='pnl')
period_preset = fields.Selection([
('this_month', 'This Month'),
('last_month', 'Last Month'),
('this_quarter', 'This Quarter'),
('last_quarter', 'Last Quarter'),
('this_year', 'This Year (YTD)'),
('last_year', 'Last Year'),
('custom', 'Custom Range'),
], default='this_month', required=True)
date_from = fields.Date()
date_to = fields.Date()
comparison = fields.Selection([
('none', 'No Comparison'),
('previous_period', 'Previous Period'),
('previous_year', 'Previous Year'),
], default='none')
@api.onchange('period_preset')
def _onchange_period_preset(self):
today = fields.Date.today()
if self.period_preset == 'this_month':
p = month_bounds(today)
self.date_from, self.date_to = p.date_from, p.date_to
elif self.period_preset == 'last_month':
p = month_bounds(today.replace(day=1) - timedelta(days=1))
self.date_from, self.date_to = p.date_from, p.date_to
elif self.period_preset == 'this_quarter':
p = quarter_bounds(today)
self.date_from, self.date_to = p.date_from, p.date_to
elif self.period_preset == 'last_quarter':
this_q = quarter_bounds(today)
p = quarter_bounds(this_q.date_from - timedelta(days=1))
self.date_from, self.date_to = p.date_from, p.date_to
elif self.period_preset == 'this_year':
p = fiscal_year_bounds(today)
self.date_from, self.date_to = p.date_from, today
elif self.period_preset == 'last_year':
last_year = today.replace(year=today.year - 1)
p = fiscal_year_bounds(last_year)
self.date_from, self.date_to = p.date_from, p.date_to
def action_open_report(self):
"""Open the fusion reports viewer pre-filled with selected period."""
self.ensure_one()
return {
'type': 'ir.actions.client',
'tag': 'fusion_reports',
'context': {
'default_report_type': self.report_type,
'default_date_from': str(self.date_from),
'default_date_to': str(self.date_to),
'default_comparison': self.comparison,
},
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_fusion_period_picker_wizard_form" model="ir.ui.view">
<field name="name">fusion.period.picker.wizard.form</field>
<field name="model">fusion.period.picker.wizard</field>
<field name="arch" type="xml">
<form string="Pick Reporting Period">
<group>
<field name="report_type"/>
<field name="period_preset"/>
<field name="date_from" invisible="period_preset != 'custom'"
required="period_preset == 'custom'"/>
<field name="date_to" invisible="period_preset != 'custom'"
required="period_preset == 'custom'"/>
<field name="comparison"/>
</group>
<footer>
<button name="action_open_report" type="object" string="Open Report"
class="btn-primary"/>
<button special="cancel" string="Cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_fusion_period_picker_wizard" model="ir.actions.act_window">
<field name="name">Open Financial Report</field>
<field name="res_model">fusion.period.picker.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -0,0 +1,105 @@
"""XLSX export wizard for fusion financial reports."""
import base64
import io
from odoo import _, fields, models
from odoo.exceptions import UserError
from ..services.date_periods import Period
class FusionXlsxExportWizard(models.TransientModel):
_name = "fusion.xlsx.export.wizard"
_description = "Export Financial Report to XLSX"
report_type = fields.Selection([
('pnl', 'P&L'),
('balance_sheet', 'Balance Sheet'),
('trial_balance', 'Trial Balance'),
('general_ledger', 'General Ledger'),
], required=True, default='pnl')
date_from = fields.Date(required=True, default=fields.Date.today)
date_to = fields.Date(required=True, default=fields.Date.today)
comparison = fields.Selection([
('none', 'No Comparison'),
('previous_period', 'Previous Period'),
('previous_year', 'Previous Year'),
], default='none')
xlsx_file = fields.Binary(readonly=True)
xlsx_filename = fields.Char(readonly=True)
state = fields.Selection([('draft', 'Draft'), ('done', 'Done')], default='draft')
def action_export(self):
self.ensure_one()
company_id = self.env.company.id
engine = self.env['fusion.report.engine']
if self.report_type == 'pnl':
period = Period(self.date_from, self.date_to, f"{self.date_from} - {self.date_to}")
result = engine.compute_pnl(period, comparison=self.comparison, company_id=company_id)
elif self.report_type == 'balance_sheet':
result = engine.compute_balance_sheet(self.date_to, comparison=self.comparison, company_id=company_id)
elif self.report_type == 'trial_balance':
period = Period(self.date_from, self.date_to, f"{self.date_from} - {self.date_to}")
result = engine.compute_trial_balance(period, company_id=company_id)
else:
period = Period(self.date_from, self.date_to, f"{self.date_from} - {self.date_to}")
result = engine.compute_gl(period, company_id=company_id)
try:
import xlsxwriter
except ImportError:
raise UserError(_(
"xlsxwriter Python package is required for XLSX export. "
"Install with: pip install xlsxwriter"))
buf = io.BytesIO()
wb = xlsxwriter.Workbook(buf, {'in_memory': True})
ws = wb.add_worksheet(self.report_type[:30])
bold = wb.add_format({'bold': True})
money = wb.add_format({'num_format': '#,##0.00'})
money_bold = wb.add_format({'num_format': '#,##0.00', 'bold': True})
ws.write(0, 0, result.get('report_name', 'Report'), bold)
ws.write(1, 0, f"Period: {result.get('period', {}).get('label', '')}")
if result.get('comparison_period'):
ws.write(2, 0, f"Comparison: {result['comparison_period']['label']}")
row_idx = 4
ws.write(row_idx, 0, 'Line', bold)
ws.write(row_idx, 1, 'Amount', bold)
if result.get('comparison_period'):
ws.write(row_idx, 2, 'Comparison', bold)
ws.write(row_idx, 3, 'Variance %', bold)
for row in result.get('rows', []):
row_idx += 1
label = (' ' * (row.get('level', 0) or 0)) + (row.get('label', '') or '')
fmt = bold if row.get('is_subtotal') else None
money_fmt = money_bold if row.get('is_subtotal') else money
ws.write(row_idx, 0, label, fmt)
ws.write(row_idx, 1, row.get('amount', 0), money_fmt)
if result.get('comparison_period'):
if row.get('amount_comparison') is not None:
ws.write(row_idx, 2, row['amount_comparison'], money_fmt)
if row.get('variance_pct') is not None:
ws.write(row_idx, 3, row['variance_pct'] / 100,
wb.add_format({'num_format': '+0.0%;-0.0%;0.0%'}))
ws.set_column(0, 0, 40)
ws.set_column(1, 3, 16)
wb.close()
self.write({
'xlsx_file': base64.b64encode(buf.getvalue()),
'xlsx_filename': f'{self.report_type}_{self.date_from}_{self.date_to}.xlsx',
'state': 'done',
})
return {
'type': 'ir.actions.act_window',
'res_model': self._name,
'res_id': self.id,
'view_mode': 'form',
'target': 'new',
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_fusion_xlsx_export_wizard_form" model="ir.ui.view">
<field name="name">fusion.xlsx.export.wizard.form</field>
<field name="model">fusion.xlsx.export.wizard</field>
<field name="arch" type="xml">
<form string="Export to XLSX">
<group invisible="state == 'done'">
<field name="report_type"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="comparison"/>
</group>
<group invisible="state != 'done'">
<field name="xlsx_file" filename="xlsx_filename" readonly="1"/>
<field name="xlsx_filename" invisible="1"/>
</group>
<field name="state" invisible="1"/>
<footer>
<button name="action_export" type="object" string="Export"
class="btn-primary" invisible="state == 'done'"/>
<button special="cancel" string="Close"/>
</footer>
</form>
</field>
</record>
<record id="action_fusion_xlsx_export_wizard" model="ir.actions.act_window">
<field name="name">Export Report (XLSX)</field>
<field name="res_model">fusion.xlsx.export.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>