"""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', }