documentation/content/developer/tutorials/pdf_reports.md

299 lines
14 KiB
Markdown
Raw Permalink Normal View History

2025-02-27 18:56:07 +07:00
# Build PDF Reports
2025-02-27 18:56:07 +07:00
:::{important}
This tutorial is an extension of the {doc}`server_framework_101` tutorial. Make sure you have
completed it and use the `estate` module you have built as a base for the exercises in this
tutorial.
:::
2025-02-27 18:56:07 +07:00
We were previously {doc}`introduced to QWeb <server_framework_101/14_qwebintro>`
where it was used to build a kanban view. Now we will expand on one of QWeb's
other main uses: creating PDF reports. A common business requirement is the ability to create documents
to send to customers and to use internally. These reports can be used to summarize and display
information in an organized template to support the business in different ways. Odoo
can additionally add our company's header and footer to our reports with minimal extra effort.
2025-02-27 18:56:07 +07:00
The documentation related to this topic can be found in {ref}`reference/qweb`,
{ref}`reference/reports/report`, and the {ref}`reference/actions/report`
section of the Actions reference.
2025-02-27 18:56:07 +07:00
## File Structure
The bulk of a PDF report is its QWeb template. It also typically needs a corresponding
2025-02-27 18:56:07 +07:00
`ir.actions.report` to include the report within a module's business logic.
There is no strict rule for the file names or where they are located, but these two parts are
2025-02-27 18:56:07 +07:00
typically stored in 2 separate files within a `report` folder at the top level of your module's
directory. If a module has many or multiple long report templates, then they are often organized
logically across different files named after the report(s) they contain. All actions
2025-02-27 18:56:07 +07:00
for the reports are usually stored in the same file ending with `_reports.xml`, regardless of the
number of reports it contains.
Therefore, it is expected that your work tree will look something like this:
2025-02-27 18:56:07 +07:00
```bash
estate
├── models
│ ├── *.py
│ └── __init__.py
├── report
│ ├── estate_property_templates.xml
│ └── estate_property_reports.xml
├── security
│ └── ir.model.access.csv
├── views
│ └── *.xml
├── __init__.py
└── __manifest__.py
```
Don't forget to add whatever files your template and action view will be into to your `__manifest__.py`.
In this case, you will want to add the files to the `data` list and remember that the files listed in a manifest
are loaded sequentially!
2025-02-27 18:56:07 +07:00
## Basic Report
2025-02-27 18:56:07 +07:00
:::{note}
**Goal**: at the end of this section, we will be able to print a report that displays all offers for a
property.
2025-02-27 18:56:07 +07:00
```{image} pdf_reports/simple_report.png
:align: center
:alt: Simple PDF report
```
:::
In our real estate example there are many useful reports that we could create. One simple report we
can create is one that displays all of a property's offers.
2025-02-27 18:56:07 +07:00
### Report Data
Before we do anything we first need some data to populate our reports or else this tutorial
won't be very interesting. When creating reports, you will need some data to test your report code
and check that the resulting look is as expected. It is a good idea to test with data that will cover most
or all of your expected use cases. A good representation set for our simple report is:
2025-02-27 18:56:07 +07:00
- At least 3 properties where 1 is "sold", 1 is "offer received" and 1 is "new".
- At least 2-3 offers for our "sold" and "offer received" properties
If you don't have a set of data like this already, you can either:
2025-02-27 18:56:07 +07:00
- Complete the {doc}`define_module_data` tutorial (if you haven't done so already) and add the extra
cases to your demo data (you may need to create a new database to load in the demo data).
2025-02-27 18:56:07 +07:00
- Manually create the data in your database.
- Copy this [data file](https://github.com/odoo/technical-training-solutions/blob/{BRANCH}-J_reports/estate/data/estate_demo.xml)
into a new directory (data) in your estate module and copy [these lines](https://github.com/odoo/technical-training-solutions/blob/{BRANCH}-J_reports/estate/__manifest__.py#L21-L23)
into your \_\_manifest\_\_.py file (you may need to create a new database to load in the demo data).
Before continuing, click through your data in your database and make sure your data is as expected.
Of course you can add the data after you write your report code, but then you will not be able to
incrementally test portions of your code as you write it. This can make checking for mistakes and
debugging your code more difficult in the long run for complicated reports.
2025-02-27 18:56:07 +07:00
### Minimal Template
A minimal viable template is viewable under the "Minimal viable template" section of the
2025-02-27 18:56:07 +07:00
{ref}`reference/reports/templates` documentation. We can modify this example to build
our minimal property offers template file:
2025-02-27 18:56:07 +07:00
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="report_property_offers">
<t t-foreach="docs" t-as="property">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<div class="page">
<h2>
<span t-field="property.name"/>
</h2>
<div>
<strong>Expected Price: </strong>
<span t-field="property.expected_price"/>
</div>
2025-02-27 18:56:07 +07:00
<table class="table">
<thead>
<tr>
<th>Price</th>
</tr>
</thead>
<tbody>
<t t-set="offers" t-value="property.mapped('offer_ids')"/>
<tr t-foreach="offers" t-as="offer">
<td>
<span t-field="offer.price"/>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
2025-02-27 18:56:07 +07:00
</t>
</template>
</odoo>
```
Most of the Odoo specific (i.e. non-HTML) items in our file are explained in the minimal viable template section.
Some additional features in our template are:
2025-02-27 18:56:07 +07:00
- The use of the `class="table"` attribute, so our table has some nice formatting. Twitter Bootstrap
(we're using its table class in this case), and Font Awesome (useful for adding icons) classes can
be used in your report template.
2025-02-27 18:56:07 +07:00
- The use of `t-set`, `t-value`, `t-foreach`, and `t-as` so that we can loop over all the `offer_ids`.
If you are already familiar with website templating engines, then the QWeb directives (i.e. the `t-` commands)
2025-02-27 18:56:07 +07:00
probably don't need much explanation and you can just look at its {ref}`documentation <reference/qweb>` and
skip ahead to the next subsection.
Otherwise you are encouraged to read more about them (
2025-02-27 18:56:07 +07:00
[Wikipedia](https://en.wikipedia.org/wiki/Template_processor) has a good high level description), but
the general idea is that QWeb provides the ability to dynamically generate web code based on Odoo data and
simple commands. I.e. QWeb can access recordset data (and methods) and process simple programming operations
such as setting and accessing temporary variables. For example, in the above example:
2025-02-27 18:56:07 +07:00
- `t-set` creates a temporary variable called "offers" that has its value set by `t-value` to the current
`estate.property` recordset's `offer_ids`.
- The `t-foreach` and `t-as` usage is the equivalent to the Python:
2025-02-27 18:56:07 +07:00
```Python
for offer in offers:
```
2025-02-27 18:56:07 +07:00
### Report Action
2025-02-27 18:56:07 +07:00
Now that we have a template, we need to make it accessible in our app via a `ir.actions.report`.
A practical example of `ir.actions.report` is
[here](https://github.com/odoo/odoo/blob/0e12fa135882cd5095dbf15fe2f64231c6a84336/addons/event/report/event_event_reports.xml#L20-L30)
corresponding to
2025-02-27 18:56:07 +07:00
[this template](https://github.com/odoo/odoo/blob/0e12fa135882cd5095dbf15fe2f64231c6a84336/addons/event/report/event_event_templates.xml#L5).
Its contents are all explained in {ref}`the documentation <reference/actions/report>`.
2025-02-27 18:56:07 +07:00
An `ir.actions.report` is primarily used via the Print menu of a model's view. In the practical
example, the `binding_model_id` specifies which model's views the report should show, and Odoo
will auto-magically add it for you. Another common use case of the report action is to link it to
2025-02-27 18:56:07 +07:00
a button as we learned in {doc}`server_framework_101/09_actions`. This is handy for reports
that only make sense under specific conditions. For example, if we wanted to make a "Final Sale"
report, then we can link it to a "Print Sale Info" button that appears in the form view only when
the property is "Sold".
2025-02-27 18:56:07 +07:00
```{image} pdf_reports/print_menu.png
:align: center
:alt: Print Menu Button
```
You may have noticed or are wondered why our report template loops through a recordset. When our
template is passed more than one record, it can produce one PDF report for all the records.
Using the Print menu in the list view with multiple records selected will demonstrate this.
2025-02-27 18:56:07 +07:00
### Make a Report
Finally, you now know where to create your files and how the content of the files should look. Happy report making!
2025-02-27 18:56:07 +07:00
```{eval-rst}
.. exercise:: Make a report.
- Add the property offers report from the minimal template subsection to the Print menu of the Property views.
- Improve the report by adding more data. Refer to the **Goal** of this section to see what additional
data you can add and feel free to add even more.
- Bonus: Make an extra flexible report by adding in some logic so that when there are no offers on a property
then we don't create a table and instead write something about how there are no offers yet. Hint: you will
need to use ``t-if`` and ``t-else``.
Remember to check that your PDF reports match your data as expected.
2025-02-27 18:56:07 +07:00
```
2025-02-27 18:56:07 +07:00
## Sub-templates
2025-02-27 18:56:07 +07:00
:::{note}
**Goal**: at the end of this section, we will have a sub-template that we use in 2 reports.
2025-02-27 18:56:07 +07:00
```{image} pdf_reports/report_subtemplate.png
:align: center
:alt: Report using a subtemplate
```
:::
There are two main reasons for using sub-templates. One is to make the code easier to read when working with
extra-long or complicated templates. The other is to reuse code where possible. Our simple property offers
report is useful, but listing property offers information can be useful for more than just one report template.
One example is a report that lists all of a salesman's properties' offers.
See if you can understand how to call a sub-template by reading the
2025-02-27 18:56:07 +07:00
{ref}`documentation <reference/qweb/sub-templates>` on it and/or by looking at an
[example](https://github.com/odoo/odoo/blob/0e12fa135882cd5095dbf15fe2f64231c6a84336/addons/portal/static/src/xml/portal_chatter.xml#L147-L160)
(remember QWeb uses the same control flows regardless if it is for a report or a view in Odoo.)
2025-02-27 18:56:07 +07:00
```{eval-rst}
.. exercise:: Create and use a sub-template.
- Split the table portion of the offers into its own template. Remember to check that your
original report still prints correctly afterwards.
- Add a new report for ``res.users`` that allows you to print all of the Real Estate Properties
that are visible in their form view (i.e. in the "Settings" app). Include the offers for each
of those saleman's properties in the same report. Hint: since the ``binding_model_id`` in this
case will not be within the estate module, you will need to use ``ref="base.model_res_users"``.
Your end result should look similar to the image in the **Goal** of this section.
Remember to check that your reports match your data as expected!
2025-02-27 18:56:07 +07:00
```
2025-02-27 18:56:07 +07:00
## Report Inheritance
2025-02-27 18:56:07 +07:00
:::{note}
**Goal**: at the end of this section, we will inherit the property report in the `estate_account`
module.
2025-02-27 18:56:07 +07:00
```{image} pdf_reports/inherited_report.png
:align: center
:alt: An inherited report
```
:::
2025-02-27 18:56:07 +07:00
Inheritance in QWeb uses the same `xpath` elements as {ref}`views inheritance <reference/view_records/inheritance>`.
A QWeb template refers to its parent template in a different way though. It is even easier to do by just adding
2025-02-27 18:56:07 +07:00
the `inherit_id` attribute to the `template` element and setting it equal to the *module.parent_template_id*.
We didn't add any new fields to any of the estate models in `estate_account`, but we can still add information
to our existing property report. For example, we know that any "Sold" properties will already have an invoice
created for them, so we can add this information to our report.
2025-02-27 18:56:07 +07:00
```{eval-rst}
.. exercise:: Inherit a report.
- Extend the property report to include some information about the invoice. You can look at the **Goal** of this
section for inspiration (i.e. print a line when the property is Done, otherwise print nothing).
Again, remember to check that your reports match your data as expected!
2025-02-27 18:56:07 +07:00
```
2025-02-27 18:56:07 +07:00
## Additional Features
2025-02-27 18:56:07 +07:00
All the following extra features are described further in the {ref}`reference/reports/report`
documentation, including how to implement each of them.
2025-02-27 18:56:07 +07:00
### Translations
We all know Odoo is used in multiple languages thanks to automated and manual translating. QWeb reports are no
exception! Note that sometimes the translations do not work properly if there are unnecessary spaces in your
template's text content, so try to avoid them when possible (especially leading spaces).
2025-02-27 18:56:07 +07:00
### Reports are web pages
You probably are tired of hearing that QWeb creates HTML, but we're saying it again! One of the
neat features of reports being written in QWeb is they can be viewed within the web browser.
This can be useful if you want to embed a hyperlink that leads to a specific report. Note that
the usual security checks will still apply to prevent unauthorized users from accessing the reports.
2025-02-27 18:56:07 +07:00
### Barcodes
Odoo has a built-in barcode image creator that allows for barcodes to be embedded in your reports.
Check out the corresponding
2025-02-27 18:56:07 +07:00
[code](https://github.com/odoo/odoo/blob/0e12fa135882cd5095dbf15fe2f64231c6a84336/addons/web/controllers/main.py#L2044-L2046)
to see all the supported barcode types.
2025-02-27 18:56:07 +07:00