Enable Dark Mode!
how-to-customize-headers-and-footers-for-all-reports-in-odoo-20.jpg
By: Sonu S

How to Customize Headers and Footers for All Reports in Odoo 20

Technical Odoo 20 Reporting

Odoo is regarded as a fully-fledged ERP system that offers a variety of modules, including sales, CRM, product inventory, and finance. Each module provides users with report templates available in PDF format that include quotes, invoices, purchase orders, delivery slips, etc., forming the list of documents sent to clients. The 20th version of Odoo provides several types of templates, like external_layout_standard, external_layout_striped, external_layout_bold, external_layout_bubble, and external_layout_wave, powered by the QWeb reporting engine. While several units of the company may find it useful to apply the templates, there are many customers who need the titles and headers to be designed and adjusted to their specific needs; as a result, the design process requires them to provide their logos, company details, and numbers. Odoo allows using the existing templates, so there is no need to create the report layout starting from scratch.

The blog post explains the different ways in which the header and footer of reports in Odoo 20 can be modified by using the inherited standard external layout. For any business report generation, the web.external_layout template is used, which calls any required layout for any company. By changing the layout, all the reports can be changed simultaneously.

Inheriting the Report Layout

The web module indicates that the standard layout is web.external_layout_standard. To modify this layout, we create a template that extends it with the help of inherit_id and utilize XPath expressions to replace only the header and footer while keeping the body section of the report intact.

Xml:

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <!-- Custom Header & Footer: Inherited from the standard external layout -->
    <template id="custom_standard_layout" inherit_id="web.external_layout_standard">
        <!-- Customize the Header -->
        <xpath expr="//div[contains(@t-attf-class, '_layout header')]" position="replace">
            <div t-attf-class="o_company_#{company.id}_layout header" style="padding: 15px 10px;">
                <table class="table-borderless w-100 mb-2">
                    <tr>
                        <!-- Company Logo -->
                        <td t-if="company.logo" class="align-middle pe-3" style="width: 1%;">
                            <img class="o_company_logo_small" t-att-src="image_data_uri(company.logo)"
                                 alt="Company Logo" style="max-height: 70px;"/>
                        </td>
                        <!-- Company Details -->
                        <td class="align-middle text-start">
                            <h2 t-if="company.name" style="font-weight: bold; margin: 0 0 5px 0;"
                                t-out="company.name"/>
                            <p t-if="company.street or company.city or company.zip" style="margin: 0; font-size: 12px;">
                                <strong>Address:</strong>
                                <t t-out="company.street or ''"/>
                                <t t-if="company.city">, <t t-out="company.city"/></t>
                                <t t-if="company.zip">, <t t-out="company.zip"/></t>
                            </p>
                            <p t-if="company.phone or company.email or company.website" style="margin: 0; font-size: 12px;">
                                <t t-if="company.phone">Phone: <t t-out="company.phone"/> </t>
                                <t t-if="company.email">Email: <t t-out="company.email"/> </t>
                                <t t-if="company.website">Website: <t t-out="company.website"/></t>
                            </p>
                        </td>
                    </tr>
                </table>
                <!-- Divider -->
                <div style="border-bottom: 1px solid #bbb;"/>
            </div>
        </xpath>
        <!-- Customize the Footer -->
        <xpath expr="//div[contains(@t-attf-class, 'footer o_company_')]" position="replace">
            <div t-attf-class="footer o_company_#{company.id}_layout {{report_type != 'pdf' and 'mt-auto'}}">
                <div class="custom_footer" style="border-top: 1px solid #333; padding: 10px 0; font-size: 11px;">
                    <table class="table-borderless w-100">
                        <!-- Company Name and Page Number -->
                        <tr>
                            <td class="text-start">
                                <strong t-out="company.name"/>
                            </td>
                            <td class="text-end">
                                <span t-if="report_type == 'pdf'">Page: <span class="page"/> / <span class="topage"/></span>
                                <span t-elif="is_preview">Page: 1 / 1</span>
                            </td>
                        </tr>
                        <!-- Company Address -->
                        <tr>
                            <td colspan="2" class="text-center">
                                <t t-out="company.street or ''"/>
                                <t t-if="company.city">, <t t-out="company.city"/></t>
                                <t t-if="company.country_id">, <t t-out="company.country_id.name"/></t>
                            </td>
                        </tr>
                    </table>
                </div>
            </div>
        </xpath>
    </template>
</odoo>

The template comes from web.external_layout_standard, so any report that utilizes the standard layout will also take the changes. The first XPath looks for the header div. Beginning from Odoo 20, the header div also has a class called order_o_company__layout_header. Therefore, to find it, the expression contains(@t-attf-class, ‘layout header’) is used. The default header is replaced with one that has the company logo on the left, the company name on the left, and the address and contact information (i.e., phone number, email, and website) below the logo, and a line beneath the header. The second XPath is used to find the footer div and replace it with the footer that has the company name on the left, the page number on the right, and the address of the company in the center. The page and topage spans are generated by wkhtmltopdf, while the is_preview condition displays a static page number in the HTML preview. The report's functionality remains unaffected, as the handling of the same in Odoo 20 takes place via web.external_layout_body. The company variable provides access to the current company's details, while the information present in the printed document can still be utilized for the report body via o or docs.

After you have completed the installation of the module, it is important to make sure that the company has set the Default Layout. Access Settings > General Settings > Document Layout > Configure Document Layout and select Layout Light. Next, open any sales order report and press Print.

Following the default settings of Odoo 20, here is the preview of a Sales Order report's header and footer:

How to Customize Headers and Footers for All Reports in Odoo 20-cybrosys

Following the update, the report displays the new header that contains the company's logo, name, address, and contact details, and the new footer that contains the company's name, page numbers, and address.

How to Customize Headers and Footers for All Reports in Odoo 20-cybrosys

With the same header and footer applied to other external documents such as an invoice, a purchase order, and a delivery slip, there is no need to make modifications to each document template. The customised invoice report is shown below:

How to Customize Headers and Footers for All Reports in Odoo 20-cybrosys

In conclusion, there exists an efficient method for the employment of an external layout, which involves the usage of the capabilities given by Odoo 20. This method provides opportunities for companies to maintain a uniform branding of their documents and reports. XPath expressions help keep the basic templates in their original form.

To read more about How to Customize Headers and Footers for All Reports in Odoo 19, refer to our blog, How to Customize Headers and Footers for All Reports in Odoo 19.


Frequently Asked Questions

Will this change affect the reports in Odoo 20?

Yes, indeed, it is applicable to every report which is using web.external_layout." For example, we can name quotations, invoice orders, and delivery slips. The modification applies as long as the layout used by the company is standard (Light). In case another layout is used, such as Bold, the layout has to be inherited separately.

Why are the page numbers hidden in the HTML preview?

Page numbers and page counts are filled solely by wkhtmltopdf when the report is being printed in PDF. In the HTML preview, Odoo shows the static “Page: 1 / 1”, so the footer contains the report_type == ‘pdf’ condition.

Which template controls the header and footer of PDF reports in Odoo 20?

Every external business document calls web.external_layout. This template loads the layout the company selected in Document Layout settings, such as web.external_layout_standard for the Light layout. Changing the header and footer in that layout template updates every report that uses it.

Can I customize the header and footer for only one report?

Yes. You can add a condition inside the layout using the record being printed, for example t-if="o and o._name == 'account.move'", to show a different header for invoices. Another option is to create a separate layout template and call it with t-call only in that report.

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



0
Comments



Leave a comment



WhatsApp