Customise invoice printout - Odoo Qweb 101

Technically, we are not going to alter the existing invoice print, but to add another option alongside it. Do you recall the small button we have above the invoice form view and tree view? (NB: For the print button to appear in tree view you have to select at least one invoice)


Now the first thing we would like to do, is to add another choice in the drop-down menu. To do that, we need to inherit the account module: account.account (NB: We can of course do the same thing for other prints such as sales and purchase order.)

 

Step 1: Define the __init__.py and __openerp__.py in your newly created module, let’s say report_customisation. Same old stuff just like what you would do for other module

 

Step 2:  Create invoice_report_extension.py and invoice_report_extension.xml files. Add the following code:

from openerp import models,api,fields

from openerp.tools import amount_to_text_en

class res_company(models.Model):

_inherit=‘res.company’

logo_report= fields.Binary(“Header Image”,

help=“This field holds the image used for the logo on the prints, limited to 1024x1024px”)

class account_invoive_extension(models.Model):

_inherit=‘account.invoice’

<?xml version=“1.0” encoding=“utf-8”?>

<openerp>

<data>

        <!– form view for company –>

<record id=“view_company_form_ce” model=“ir.ui.view”>

<field name=“name”>report.customisation.company.form</field>

<field name=“model”>res.company</field>

<field name=“inherit_id” ref=“base.view_company_form”/>

<field name=“arch” type=“xml”>

<field name=“custom_footer” position=“before”>

<field name=“logo_report” widget=“image”/>

</field>

</field>

</record>

</data>

</openerp>

Explanation: This inherits two modules: it extends company to add an icon (for header logo) and it extends invoice obviously to change its interface and grab its data for report generation. The XML only added an option in company edit view to upload the aforementioned logo.

 

Step 3: To create a button within print menu, we just need to specify a report element somewhere in the module. We shall create a new xml for this: report_sales_invoice.xml to include the following elements:

<template id=“report_sales_invoice_template”>

<t t-call=“report.html_container”>

<t t-foreach=“doc_ids” t-as=“doc_id”>

<t t-raw=“translate_doc(doc_id, doc_model, ‘partner_id.lang’, ‘report_customisation.report_sales_invoice_document’)”/>

</t>

</t>

</template>

<report

id=“report_sales_invoices”

model=“account.invoice”

string=“Sales Invoice Report”

report_type=“qweb-pdf”

name=“report_customisation.report_sales_invoice_template”

file=“report_customisation.report_sales_invoice_template”

attachment_use=“True”

attachment=“(object.state in (‘open’,’paid’)) and (‘INV’+(object.number or ”).replace(‘/’,”)+’.pdf’)”

/>

The report element defines the nature of the button: which report it will generate and the styles. We can see that its name is “Sales Invoice Report” and report_type is obligatorily “qweb-pdf” As for the other attributes, there is sufficient documentation on Odoo websites. Its model is account.invoice, meaning the button will appear for any view of this model.

The template, however, is the important bridge between web interface and underlying logic. Coded in Qweb syntaxes, the attributes begin uniformly with ‘t-‘. the t-call here is fixed, but t-foreach has more story behind. It fetches the relevant docs (called doc_ids). Admittedly if you pressed the button in form view, there is only gonna be one doc, but if you selected multiple invoices in the tree/list view, there is going to be many of them, therefore it uses a foreach to tackle them one by one.

The embedded line is executed for each document, in our case the sales invoice. The t-raw calls a function translation which is recommended (You never know just how fast your business extends to French or Spanish speakers:), in which it includes the document id (for fetching info), doc_model, language_id, and finally the main template that defines how the invoice will look.

Tentatively, we shall just include this line to make sure it gets fed with an empty invoice.

<template id=“report_sales_invoice_document”/>

Now install the module (Don’t forget to add the xml files in __openerp__.py) and we shall be able to see the button appearing in the print menu:


  1. But if you try to print anything, it’s gonna give you a blank page, because we haven’t coded the main template! Now let’s change that step by step. First, include the following verse inside the template we left blank above:

<t t-if =”o and ‘company_id’ in o” >

<t t-if=”o.date_invoice“>

<t t-set=”pdate” t-value=”time.strftime(‘%d %B %Y’,time.strptime(o.date_invoice,’%Y-%m-%d’))” />

</t>

</t>

This basically verifies that our invoice exists and its company_id exists as well. Then if the date doesn’t exist in the invoice it will create a variable ‘pdate’ to store the current date. Note that it uses o as a short form for object (doc_id in the previous section), accepted by Qweb. From now on we can use the data in invoice by referencing the object o, isn’t it convenient?

Now we need to define the header. We can either write it inside the main template, or a new template and call it from the main template using t-call. We take the latter strategy here because the header might be shared with other documents and we certainly wouldn’t want to rewrite our code!

<template id=”custom_layout_header“>

<t t-if =“o and ‘company_id’ in o” >

<t t-set=“company” t-value=”o.company_id”/>

<t t-set=“customer” t-value=”o.partner_id”/>

</t>

<div class=”header“>

<div class=”row” style=“font-size:11px; line-height=80%”>

<div class=“col-xs-6”>

<img  t-if=“company.logo_report”

t-att-src=“‘data:image/png;base64,%s’ %                                                                                                                            “company.logo_report”

style=“max-height: 140px;” />

</div>

<div class=“col-xs-4 col-xs-offset-2″ style=”margin-top:10px”>

             <span t-field=“company.name” style=“color:blue”/><br/>

             <span t-field=“company.street”/><br/>

<span t-field=“company.street2″/><br/>

             <span t-field=“company.country_id”/><span t-field=“company.zip“/><br/>

             Phone: <span t-field=“company.phone”/><br/>

Fax: <span t-field=“company.fax”/><br/>

Email: <span t-field=“company.email”/><br/>

<span t-field=“company.website”/><br/>

</div>

</div>

<hr style=“height:3px;border:none;color:#FF0000;background-color:#FF0000;margin-top:0px;margin-bottom:12px”/>

</div>

</div>

</template>

The Qweb has added plenty of classes and styles to be used just like any other html elements. Here I shall explain the most used ones so you can decipher these lines of code:

<t t-set=”company” t-value=“o.company_id”/>

<t t-set=”customer t-value=“o.partner_id”/>

 

Here we set some variables to be evaluated by Qweb at runtime. This is for convenience’s sake here so we can use company and customer as alias for o.company_id and o.partner_id

style=“font-size:11px; line-height=80%”

This is just the classical css style definition. It specifies the font and line spacing for all info included in this element

t-if=“company.logo_report”

t-att-src=“‘data:image/png;base64,%s’ %                                                                                                                            “company.logo_report”

style=“max-height: 140px;”

Here we extract the image (remember the company inherit where we included another logo?) and put it nicely in our report. The t-if asserts that it exists and t-att-src loads it as an image (stored as a binary field in odoo), then the style determines its size

<div class=”col-xs-4 col-xs-offset-2” style=”margin-top:10px“>

Here the most import structuring class, col-xs-## where # stands for a number between 1 to 12. Qweb divides the page width into 12 columns and col-xs-## defines how many columns the element will occupy. Note that the previous image has taken 6 columns, so only 6 remains available in the shared div element. (Outside the div we can re-employ the same trick to arrange the layour 

There are also col-xs-offset-## modifier that keeps certain spacing between elements, then  pull-left and pull-right if you would like to put data in a column on the extreme left or right

<span t-field=”company.name” style=”color:blue“/><br/>

This is a field referring directly a field in the object. Remember the alias defined above? Here we print its name. If your company is called GoodYear, it will appear as GoodYear in the invoice document. If you change company or company name, it will be automatically updated here.

After defining the header, we can simply include it in the main template by using a t-call:

<t t-call=“report_customisation.custom_layout_header”/>

Now if you print your invoice, you can see the top become something like this:

 

Of course you need to enter the details of your company and include you company logo, or else you won’t have much information. Now a little challenge: in the above header there is too much blank between logo and company info. How would you plan to fix it by changing the layout?



Understand odoo Tax code (Singapore)