← All articles

Blog

Custom Odoo Reports: QWeb, Spreadsheets and SQL Views Compared

Which Odoo reporting method fits which job? A practical comparison of QWeb PDFs, Spreadsheet dashboards and SQL views, with costs, limits and a decision guide.

6 min read
  • mid
  • odoo

If you are asking which tool to use for a custom Odoo report, the short answer is this. Use QWeb when the output is a printed or emailed document, such as an invoice, a delivery slip or a statement. Use Spreadsheet dashboards when people need live, filterable numbers they can click through. Use a SQL view model when the numbers need joins or aggregations that Odoo’s standard models do not give you. Many real projects use all three.

Most reporting pain comes from picking the wrong one. A QWeb PDF forced to act as a dashboard is slow and rigid. A SQL view built to produce a pretty customer document is the wrong tool entirely.

The three approaches in one table

QWeb PDFSpreadsheet dashboardSQL view model
Best forDocuments: invoices, quotes, labels, certificatesLive KPIs, pivots, charts for managersCross-model analysis, custom aggregates
Built byDeveloper (XML/QWeb)Power user or analyst, no codeDeveloper (Python + SQL)
OutputPDF or HTML, one record or a batchInteractive dashboard in the browserA new list, pivot and graph view
Main riskLayout fragility, slow on large batchesAccess-rights and plan surprisesMaintenance on every upgrade

QWeb PDF reports

Odoo’s reporting engine is built on QWeb templates, which are HTML with control flow, rendered to PDF by wkhtmltopdf. According to the Odoo developer documentation, a report needs a report action and a template, and can optionally define a paper format. Templates receive the selected records as docs, and you wrap the content in the standard web.external_layout to get your company header and footer.

A minimal template looks like this:

<template id="report_delivery_note_custom">
  <t t-call="web.html_container">
    <t t-foreach="docs" t-as="doc">
      <t t-call="web.external_layout">
        <div class="page">
          <h2 t-field="doc.name"/>
        </div>
      </t>
    </t>
  </t>
</template>

Where it works well

  • Branded documents customers and auditors see.
  • Layouts that must match a legal or customer-mandated format.
  • Multi-language output. Odoo’s docs describe rendering a document in a chosen language, for example the partner’s language.
  • Barcodes and QR codes on labels, via the built-in barcode controller.

Where it hurts

  • It is not an analysis tool. You get a static document, not something to slice.
  • Pixel-level layout is fiddly. wkhtmltopdf is an older rendering engine, so modern CSS such as flexbox and grid does not always behave.
  • Reports needing data beyond the selected records need a custom report model, which means Python.
  • Large batches (thousands of pages) are slow. Generate those in the background, not on screen.

Typical effort: a modest layout change to a standard document is small work. A fully custom document with new data is a few days. Treat both as estimates and confirm against your own scope.

Spreadsheet dashboards

Odoo’s dashboards are built on its spreadsheet engine. The official dashboards documentation says that spreadsheets are the foundation: a spreadsheet is converted into a dashboard, with data sources linked to your database, so the latest data is retrieved each time the dashboard is opened or refreshed. Global filters (period, salesperson, customer) apply across the whole page, and clicking a figure or chart point opens the underlying records.

Where it works well

  • Managers who want a sales, stock or finance view without waiting for a developer.
  • Fast iteration. An analyst can change a pivot in minutes.
  • Drill-down from a number to the records behind it, which builds trust in the figures.

Where it hurts

  • Access rights apply. The docs state that data is filtered by each user’s model access rights and record rules, so a viewer may see less than the dashboard’s author did. Test dashboards with a real non-admin user before launch.
  • Standard dashboards are reinstalled on version upgrades. The docs recommend duplicating them before customizing, or your changes can be overwritten.
  • Check your plan. Confirm that the Dashboards app is included in your Odoo edition and plan before you design around it.
  • You are limited to the data sources and measures the engine can query. Complex joins or custom calculations often push you to option three.

SQL view models

When Odoo’s built-in models cannot answer your question, the developer approach is a model backed by a PostgreSQL view. Odoo’s own how-to on customized reports describes the pattern: declare a model with _auto = False so no regular table is created, mark fields read-only, drop any existing view with tools.drop_view_if_exists and then create the view in init(). The docs suggest splitting the query into methods such as _select and _from for readability.

class SaleMarginReport(models.Model):
    _name = "x.sale.margin.report"
    _auto = False
    _description = "Sales margin by product and month"

    product_id = fields.Many2one("product.product", readonly=True)
    margin = fields.Float(readonly=True)

    def init(self):
        tools.drop_view_if_exists(self.env.cr, self._table)
        self.env.cr.execute("CREATE OR REPLACE VIEW %s AS (...)" % self._table)

The result behaves like any other Odoo model. It gets list, pivot and graph views, filters, grouping and export, and it can feed a spreadsheet dashboard as a data source.

Where it works well

  • Margin, aging, utilization or stock-valuation views joining several models.
  • Big data volumes, because the database does the aggregation.
  • A single agreed definition of a metric that every dashboard reuses.

Where it hurts

  • It is code. Someone must own it, test it and update it for each major Odoo upgrade when underlying tables change.
  • Odoo’s documentation describes two ways to populate the view: overriding init(), which it presents as the standard option, or setting _table_query. Views that depend on context, such as multi-company or multi-currency conversions, are easy to get wrong, so test them carefully.
  • Raw SQL bypasses some ORM behavior. Check that record rules and multi-company restrictions are still enforced for your users.

How to choose

  1. Is the output a document someone prints, signs or emails? QWeb.
  2. Do people need to explore numbers and filter them? Start with a Spreadsheet dashboard on standard models.
  3. Does the question need data the standard models cannot join or aggregate? Build a SQL view, then put a dashboard on top of it.
  4. Is it a one-off question? Export a pivot view to a spreadsheet. Not everything deserves a build.

A common healthy pattern is SQL view for the metric definition, dashboard for the daily view, and QWeb only for the monthly pack that goes to the board or bank.

Mistakes we see

  • Building a QWeb report to answer “what happened last quarter?” and then adding filters one request at a time.
  • Customizing a standard dashboard in place and losing the edits at the next upgrade.
  • Skipping the non-admin test, then discovering that the sales team sees empty charts.
  • Writing SQL views with no owner and no upgrade test.

Next step

If you are weighing these options for your own Odoo setup, we are happy to talk it through. A no-charge conversation with Teknologia Solutions can help you map which reports belong in which tool and what each is likely to cost to build and maintain. No obligation, and if the answer is “use the standard report,” we will say so.


Sources: Odoo developer docs, QWeb reports; Odoo docs, Dashboards; Odoo developer docs, Create customized reports. Features, editions and pricing change between Odoo versions. Verify against Odoo’s current documentation and your own plan before deciding.