Reports are written in HTML/QWeb, like all regular views in Odoo. You can use the usual QWeb control flow tools. The PDF rendering itself is performed by wkhtmltopdf.
If you want to create a report on a certain model, you will need to define this Report and the Report template it will use. If you wish, you can also specify a specific Paper Format for this report. Finally, if you need access to more than your model, you can define a Custom Reports class that gives you access to more models and records in the template.
Every report must be declared by a report action.
For simplicity, a shortcut <report> element is available to define a report, rather than have to set up the action and its surroundings manually. That <report> can take the following attributes:
Warning
The paper format cannot currently be declared via the <report> shortcut, it must be added afterwards using a <record> extension on the report action itself:
<record id="<report_id>" model="ir.actions.report.xml">
<field name="paperformat_id" ref="<paperformat>"/>
</record>
Example:
<report
id="account_invoices"
model="account.invoice"
string="Invoices"
report_type="qweb-pdf"
name="account.report_invoice"
file="account.report_invoice"
attachment_use="True"
attachment="(object.state in ('open','paid')) and
('INV'+(object.number or '').replace('/','')+'.pdf')"
/>
A minimal template would look like:
<template id="report_invoice">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="report.external_layout">
<div class="page">
<h2>Report title</h2>
<p>This object's name is <span t-field="o.name"/></p>
</div>
</t>
</t>
</t>
</template>
Calling external_layout will add the default header and footer on your report. The PDF body will be the content inside the <div class="page">. The template’s id must be the name specified in the report declaration; for example account.report_invoice for the above report. Since this is a QWeb template, you can access all the fields of the docs objects received by the template.
There are some specific variables accessible in reports, mainly:
If you wish to access other records/models in the template, you will need a custom report.
If you wish to translate reports (to the language of a partner, for example), you need to define two templates:
You can then call the translatable document from your main template with the attribute t-lang set to a language code (for example fr or en_US) or to a record field. You will also need to re-browse the related records with the proper context if you use fields that are translatable (like country names, sales conditions, etc.)
Warning
If your report template does not use translatable record fields, re-browsing the record in another language is not necessary and will impact performances.
For example, let’s look at the Sale Order report from the Sale module:
<!-- Main template -->
<template id="report_saleorder">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="sale.report_saleorder_document" t-lang="doc.partner_id.lang"/>
</t>
</t>
</template>
<!-- Translatable template -->
<template id="report_saleorder_document">
<!-- Re-browse of the record with the partner lang -->
<t t-set="doc" t-value="doc.with_context({'lang':doc.partner_id.lang})" />
<t t-call="report.external_layout">
<div class="page">
<div class="oe_structure"/>
<div class="row">
<div class="col-xs-6">
<strong t-if="doc.partner_shipping_id == doc.partner_invoice_id">Invoice and shipping address:</strong>
<strong t-if="doc.partner_shipping_id != doc.partner_invoice_id">Invoice address:</strong>
<div t-field="doc.partner_invoice_id" t-field-options="{"no_marker": true}"/>
<...>
<div class="oe_structure"/>
</div>
</t>
</template>
The main template calls the translatable template with doc.partner_id.lang as a t-lang parameter, so it will be rendered in the language of the partner. This way, each Sale Order will be printed in the language of the corresponding customer. If you wish to translate only the body of the document, but keep the header and footer in a default language, you could call the report’s external layout this way:
<t t-call="report.external_layout" t-lang="en_US">
Tip
Please take note that this works only when calling external templates, you will not be able to translate part of a document by setting a t-lang attribute on an xml node other than t-call. If you wish to translate part of a template, you can create an external template with this partial template and call it from the main one with the t-lang attribute.
Barcodes are images returned by a controller and can easily be embedded in reports thanks to the QWeb syntax:
<img t-att-src="'/report/barcode/QR/%s' % 'My text in qr code'"/>
More parameters can be passed as a query string
<img t-att-src="'/report/barcode/?
type=%s&value=%s&width=%s&height=%s'%('QR', 'text', 200, 200)"/>
Twitter Bootstrap and FontAwesome classes can be used in your report template
Local CSS can be put directly in the template
Global CSS can be inserted in the main report layout by inheriting its template and inserting your CSS:
<template id="report_saleorder_style" inherit_id="report.layout">
<xpath expr="//style" position="after">
<style type="text/css">
.example-css-class {
background-color: red;
}
</style>
</xpath>
</template>
Paper formats are records of report.paperformat and can contain the following attributes:
Example:
<record id="paperformat_frenchcheck" model="report.paperformat">
<field name="name">French Bank Check</field>
<field name="default" eval="True"/>
<field name="format">custom</field>
<field name="page_height">80</field>
<field name="page_width">175</field>
<field name="orientation">Portrait</field>
<field name="margin_top">3</field>
<field name="margin_bottom">3</field>
<field name="margin_left">3</field>
<field name="margin_right">3</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">3</field>
<field name="dpi">80</field>
</record>
The report model has a default get_html function that looks for a model named report.module.report_name. If it exists, it will use it to call the QWeb engine; otherwise a generic function will be used. If you wish to customize your reports by including more things in the template (like records of others models, for example), you can define this model, overwrite the function render_html and pass objects in the docargs dictionnary:
from openerp import api, models
class ParticularReport(models.AbstractModel):
_name = 'report.module.report_name'
@api.multi
def render_html(self, data=None):
report_obj = self.env['report']
report = report_obj._get_report_from_name('module.report_name')
docargs = {
'doc_ids': self._ids,
'doc_model': report.model,
'docs': self,
}
return report_obj.render('module.report_name', docargs)
Reports are dynamically generated by the report module and can be accessed directly via URL:
For example, you can access a Sale Order report in html mode by going to http://<server-address>/report/html/sale.report_saleorder/38
Or you can access the pdf version at http://<server-address>/report/pdf/sale.report_saleorder/38
odoo docs theme based on Bootstrap 3.2 documentation adapted to Sphinx output with branding and color changes.