From ORM to Excel: Building Odoo Reports with report_xlsx by OCA

January 12, 2026 by
From ORM to Excel: Building Odoo Reports with report_xlsx by OCA
Fazri Muhammad Yazid
| No comments yet

Overview

report_xlsx is a base reporting module maintained by the Odoo Community Association (OCA). It provides a clean, Python-only way to generate XLSX (Excel) reports in Odoo, without QWeb, HTML, or PDF rendering.

The module is officially available on Odoo Apps and can be downloaded from:
​https://apps.odoo.com/apps/modules/17.0/report_xlsx

This module acts as an abstract engine. It does not generate reports by itself. Instead, it exposes a base class that developers extend to build custom Excel reports using xlsxwriter.

Tutorial


1. Ensure system dependency

Make you already installed these library in your pip list:

  • xlsxwriter
  • xlrd


2. Directory Standart

A typical module that implements an XLSX report

my_module/
├─ __manifest__.py
├─ reports/
│  ├─ __init__.py
│  └─ my_model_report_xlsx.py
└─ views/
   └─ report_action.xml


3. Implementing the XLSX Report Class

Every XLSX report must inherit from report.report_xlsx.abstract.

from odoo import models

class MyModelReportXlsx(models.AbstractModel):
    _name = 'report.my_module.my_model_report_xlsx'
    _inherit = 'report.report_xlsx.abstract'

    def generate_xlsx_report(self, workbook, data, records):
        sheet = workbook.add_worksheet('Summary')

        header = workbook.add_format({
            'bold': True,
            'border': 1,
            'align': 'center'
        })

        sheet.write(0, 0, 'Name', header)
        sheet.write(0, 1, 'Amount', header)

        row = 1
        for rec in records:
            sheet.write(row, 0, rec.name)
            sheet.write(row, 1, rec.amount)
            row += 1

Parameter:

  1. workbook: xlsxwriter.Workbook
  2. data: optional payload (wizard input)
  3. records: active recordset passed by the report action


4. Create the Report Action

To expose an XLSX report in Odoo, you must register an ir.actions.report record.
This action binds your Python report_xlsx class to a model and makes it callable from the UI or code.

<odoo>
    <record id="action_my_model_xlsx" model="ir.actions.report">
        <field name="name">My Model Excel Report</field>
        <field name="model">my.model</field>
        <field name="report_type">xlsx</field>
        <field name="report_name">my_module.my_model_report_xlsx</field>
        <field name="report_file">my_model_report_xlsx</field>
        <field name="binding_type">report</field>
    </record>
</odoo>


5. Calling XLSX Report from a Function

In Odoo, an XLSX report can be executed from a button by calling report_action() inside a model method.
The button uses type="object" and calls a Python function.

from odoo import models

class MyModel(models.Model):
    _name = 'my.model'
    _description = 'My Model'

    def create_pdf_report(self):
        return self.env.ref(
            'my_module.action_my_model_xlsx'
        ).report_action(self)


XLSX Formatting

Formatting in report_xlsx is handled entirely through xlsxwriter formats.
There is no template layer. Every visual decision is explicit and deterministic.


Basic Format Definitions

header_format = workbook.add_format({
    'bold': True,
    'border': 1,
    'align': 'center',
    'valign': 'vcenter',
    'bg_color': '#D9D9D9'
})

text_format = workbook.add_format({
    'border': 1,
    'align': 'left'
})

number_format = workbook.add_format({
    'border': 1,
    'align': 'right',
    'num_format': '#,##0.00'
})

date_format = workbook.add_format({
    'border': 1,
    'num_format': 'yyyy-mm-dd'
})


Writing Data Rows with Format

row = 1
for rec in records:
    sheet.write(row, 0, rec.partner_id.name or '', text_format)
    sheet.write(row, 1, rec.amount_total, number_format)
    sheet.write(row, 2, rec.invoice_date_due, date_format)
    row += 1


Column Width and Row Height

Always set column width explicitly.

sheet.set_column('A:A', 30)
sheet.set_column('B:B', 18)
sheet.set_column('C:C', 15)


Merging Cells

title_format = workbook.add_format({
    'bold': True,
    'font_size': 14,
    'align': 'center',
    'valign': 'vcenter'
})

sheet.merge_range('A1:C1', 'Aged Payable Report', title_format)
From ORM to Excel: Building Odoo Reports with report_xlsx by OCA
Fazri Muhammad Yazid January 12, 2026
Share this post
Archive
Sign in to leave a comment