By: Anusha

How to Create Qweb Reports In Odoo 12

Technical Odoo 12

Reports play a vital role in business as it gives insight to crucial assessments and reflections of business operations carried every now and then. Reports can be called as the backbone of the business, as it underpins the success and failure nodes of business. Odoo offers an inquisitive platform to frame reports giving comprehensive analysis for business developments.
To create a report in Odoo, the end user need a report template and report action. 

Report action
In order to make a report action, we need an element named  <report>.report have the following attributes.

a) id
the generated record's `external id`
b) name (mandatory)
only useful as a mnemonic/description of the report when looking for one in a list of some sort
c) model (mandatory)
the model your report will be about
d) report_type(mandatory)
either ``qweb-pdf`` for PDF reports or ``qweb-html`` for HTML
e) print_report_name
the name of your report (which will be the name of the PDF output)
f) Groups
groups allowed to view/use the current report
g) Attachment_use
if set to True, the report will be stored as an attachment of the record using the name generated by the ``attachment`` expression; you can use this if you need your report to be generated only once (for legal reasons, for example)
h) attachment
python expression that defines the name of the report; the record is accessible as the variable ``object``
i) Paper format
 an external id of the paper format you wish to use

   string="PRO-FORMA Invoice"
   print_report_name="'PRO-FORMA - %s' % (object.name)"
Report template
Minimal viable template
 <template id="report_invoice">
   <t t-call="web.html_container">
       <t t-foreach="docs" t-as="o">
           <t t-call="web.external_layout">
               <div class="page">
                   <h2>Report title</h2>
                   <p>This object's name is <span t-field="o.name"/></p>

When we call the external_layout it will take the default header and footer. The contents inside the PDF will reside in the <div class="page">.The template id must be the same that we defined in the report declared.

There are some specific variables:

a) docs
Records for the current report
b) doc_ids
List of ids for the ``docs`` records
c) doc_model
Model for the docs records
d) time
A reference to :mod:`python:time` from the Python standard library
e) user
res.user record for the user printing the report
f) res_company
Record for the current user's company

Translatable Templates
There may be situations where we deal with foreign partners, in that case, we should take reports in their language, for that purpose we need to define mainly two templates.

a) The main report template
b) The translatable document

by using the “t-lang” attribute you can call the translatable document from your main template.

For example, let's look at the Sale Order report from the Sale module:

1)Main template 
   <template id="report_saleorder">
       <t t-call="web.html_container">
           <t t-foreach="docs" t-as="doc">
               <t t-call="sale.report_saleorder_document" t-lang="doc.partner_id.lang"/>

2) 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="web.external_layout">
           <div class="page">
               <div class="oe_structure"/>
               <div class="row">
                   <div class="col-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 class="oe_structure"/>
Paper Format
We can define a paper format for our report according to our convenience. 
<record id="paperformat_hrsummary" model="report.paperformat">
   <field name="name">Leaves Summary</field>
   <field name="default" eval="True"/>
   <field name="format">custom</field>
   <field name="page_height">297</field>
   <field name="page_width">210</field>
   <field name="orientation">Landscape</field>
   <field name="margin_top">30</field>
   <field name="margin_bottom">23</field>
   <field name="margin_left">5</field>
   <field name="margin_right">5</field>
   <field name="header_line" eval="False"/>
   <field name="header_spacing">20</field>
   <field name="dpi">90</field>

a) Name (mandatory)
b) Description
a small description of your format
c) Format
either a predefined format (A0 to A9, B0 to B10, Legal, Letter, Tabloid,…) or custom; A4 by default. 
d) dpi
Output DPI; 90 by default
e) margin_top, margin_bottom, margin_left, margin_right
margin sizes in mm
f) page_height, page_width
page dimensions in mm
g) Orientation
Landscape or Portrait
h) Header_line
boolean to display a header line
header spacing in mm

Custom Reports
In our day to day business activities, we need reports according to our business process. For that, we may customize the reports. In that situations, we need additional records, functions etc in order to make our report, for that purpose we may use Abstract Model class and overwrite the function _get_report_values and pass objects in the dictionary:

Here is an example from payroll

class Report Hr Salary Employee By month(models.AbstractModel):
   _name = 'report.l10n_in_hr_payroll.report_hrsalarybymonth'
   _description = "Indian Salary by Month Report"
def _get_report_values(self, docids, data=None):
   model = self.env.context.get('active_model')
   docs = self.env[model].browse(self.env.context.get('active_id'))
   get_periods, months, total_mnths = self.get_periods(data['form'])
   get_employee = self.get_employee(data['form'], months, total_mnths)
   get_months_tol = self.get_months_tol()
   get_total = self.get_total(get_months_tol)
   return {
       'doc_ids': docids,
       'doc_model': model,
       'data': data,
       'docs': docs,
       'get_periods': get_periods,
       'get_employee': get_employee,
       'get_months_tol': get_months_tol,
       'get_total': get_total,
       'month_len': len(total_mnths) + 1

In this, we can declare the functions that we need to use, You can refer our blog related to Qweb where the blog explains about the Advanced Qweb Operations in Odoo 13

If you need any assistance in odoo, we are online, please chat with us.

cybrosys youtube



melissa miller

Thanks for the detailed information about the Qweb, It was was very useful for people like me working as an Odoo consultant for the organizations. By referring your article, it is quite easy to understand the working principle of web




Leave a comment


Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635


Cybrosys Limited
Alpha House,
100 Borough High Street, London,
SE1 1LB, United Kingdom


Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.


Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message