Odoo 16 is a powerful open-source business management software that offers a wide range of features to streamline various business operations. One of its key functionalities is the ability to generate PDF reports, which can be customized and tailored to meet specific requirements. In this blog post, we will walk you through a step-by-step process on how to create PDF reports in Odoo 16, enabling you to efficiently generate professional-looking reports for your business needs.
Before diving into the customization process, it's essential to familiarize yourself with the Odoo reporting system. Odoo uses QWeb, a versatile templating engine, to generate reports. QWeb is a templating engine used in the Odoo ERP framework. It allows developers to define the structure and layout of web pages, reports, and documents using XML-based templates. With QWeb, developers can combine static content with dynamic data, enabling the creation of dynamic and interactive web pages. QWeb templates support features such as conditional statements, loops, variables, and filters, providing flexibility in manipulating and presenting data. Templates can be customized easily by modifying XML elements and attributes. In the context of generating custom PDF reports in Odoo 16, QWeb is used to define the structure and content of the report template, incorporating dynamic data using Python code. Overall, QWeb plays a crucial role in Odoo's templating system, facilitating the creation of customized and professional web content within the Odoo framework.
Report Actions (ir.actions.report):
In Odoo 16, an example of a report action is shown in the code below. Make this XML file, place it in the module's reports directory, and add it to a manifest.
<record id="action_report_truck_booking_form" model="ir.actions.report">
<field name="name">Truck Booking</field>
<field name="model">truck.booking</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">
packers_and_movers_management.form_truck_booking_report
</field>
<field name="report_file">
packers_and_movers_management.form_truck_booking_report
</field>
<field name="binding_model_id" ref="model_truck_booking"/>
<field name="binding_type">report</field>
</record>
The descriptions of each field in the Report actions are provided below:
* Name: This field defines the name of the action or report, which is "Truck Booking" in this case.
* model: The model field specifies the model on which this report is based, which is "truck.booking". It indicates that this report is associated with the "truck.booking" model.
* report_type: The report_type field defines the type of the report, which is "qweb-pdf" in this case. It indicates that the report will be generated in PDF format using the QWeb templating engine.
* report_name: The report_name field specifies the technical name of the report. It is the identifier used to locate the actual report template file.
* report_file: The report_file field specifies the actual file path of the report template. It indicates the location of the QWeb template file that will be used to generate the report.
* binding_model_id: The binding_model_id field defines the model to which this action or report is bound. It refers to the model_truck_booking model, which is defined elsewhere in the code.
* binding_type: The binding_type field specifies the type of binding for this action or report, which is "report" in this case. It indicates that this action or report is bound to a report.
Report Template:
Templates are generated in HTML format. The <template> tags will contain the layout's text. Using t-call properties, you can combine many templates into a single template. The related serves as an illustration of a model report template.
<template id="form_truck_booking_report">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.external_layout">
<div class="page">
<div class="text-center">
<h1>Packers And Movers Management</h1>
</div>
<h3>Customer:
<span t-field="o.name"/>
</h3>
</div>
</t>
</t>
</t>
</template>
The "web.html_container" template, which serves as a container for the full report content, is the first template that the template calls. Each object is assigned to the variable "o" within a loop that iterates across the "docs" variable. The "web.external_layout" template is called within this loop to specify the layout hierarchy. The report's material is organised into a page div with the header "Packers And Movers Management" in the centre. A subheading with the words "Customer:" and the value of the "name" field from each item in the "docs" variable appears beneath the heading. For every object in the loop, this structure is repeated.
How to print a PDF report using the wizard or button?
Under specific circumstances, we must print the report using a wizard or button. In that case, we execute the report action from the earlier example using the print button inside the wizard or form. The print function is demonstrated in the code below.
def action_report_truck_booking(self):
query = """select pr.name,fv.name as truck,gt.name as goods,tb.from_location,tb.to_location,tb.distance,
tb.weight,tb.unit,amount,tb.date,tb.state from truck_booking as tb
inner join res_partner as pr on pr.id = tb.partner_id
inner join fleet_vehicle_model as fv on fv.id = tb.truck_id
inner join goods_type as gt on gt.id = tb.goods_type_id """
if self.from_date:
query += """ where tb.date >= '%s' and tb.date <= '%s'""" % self.from_date, % self.to_date
self.env.cr.execute(query)
report = self.env.cr.dictfetchall()
data = {'date': self.read()[0],'report': report}
return self.env.ref('module_name.action_report_booking').report_action(None, data=data)
This generates a report by executing a custom SQL query to retrieve truck booking data based on specific criteria. The query joins multiple tables such as truck_booking, res_partner, fleet_vehicle_model, and goods_type to fetch fields like partner name, truck name, goods type, locations, distance, weight, unit, amount, date, and state. The code dynamically modifies the query based on the provided from_date and to_date attributes, appending conditions to filter the results accordingly. The modified query is executed using self.env.cr.execute(), and the results are fetched using self.env.cr.dictfetchall() and stored in the report variable. A dictionary named data is created, containing the date attribute from the current instance and the report data obtained from the query. Finally, the method triggers the report action using self.env.ref('module_name.action_report_booking').report_action(None, data=data), passing the relevant data. This allows the generation of a report that includes the retrieved truck booking information based on the specified date range.
The next step is to create an Abstract model and a function called _get_report_values(). It should read "report.module_name.report_name" in the model name. Here's an illustration of the same:
from odoo import models,api
class TruckFormReport(models.AbstractModel):
_name = 'report.packers_and_movers_management.form_truck_booking_report’
@api.model
def _get_report_values(self, docids, data=None):
docs = self.env[truck.booking].browse(docids)
return {
'doc_ids': docids,
'doc_model': 'truck.booking',
'docs': docs,
'data': data,
}
This code establishes an abstract model class for Odoo report generation. Based on the supplied document IDs and extra data, the _get_report_values method collects the relevant data and returns it as a dictionary. The report template makes use of this information to fill the report with the necessary details.