> ## Content Index
> Fetch the complete content index at: https://carbone.io/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Beyond QWeb: generating your Odoo documents with Carbone
- URL: https://carbone.io/blog/beyond-qweb-generating-your-odoo-documents-with-carbone/
- Published: 2026-07-09T12:54:53.000Z
- Updated: 2026-10-30T11:31:15.000Z
- Author: Guillaume Paquereau
- Tags: Tutorials, EN, Odoo

Odoo natively generates its documents (quotations, invoices, delivery notes, and so on) with **QWeb**, its own HTML/XML-based templating engine that outputs PDF. That works fine for standard use, but any real customization stays in developer territory:

|                        | QWeb (native)                                                            | Carbone                                                 |
| ---------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------- |
| Output format          | PDF only (Excel/Word require a third-party module)                       | DOCX, XLSX, PDF, ODT, PPTX from a single template       |
| Editing the template   | XML/HTML, developer mode or Odoo Studio (paid)                           | A Word/Excel file, editable by a non-technical user     |
| QR code / barcode      | Possible via an internal controller, called in XML with a hand-built URL | A single tag (:barcode), a hundred-plus formats, no XML |
| Subtotals / aggregates | Computed field in Python on the model                                    | Right in the tag (:aggSum, :aggCount...)                |
| Deploying a change     | Update the module, redeploy                                              | Replace the Word/Excel file in the report config        |

The [**Carbone for Odoo**](https://apps.odoo.com/apps/modules/19.0/report%5Fcarbone?ref=carbone.io) module therefore shifts document customization out of a developer's hands and into those of the business teams. In this article, we start from a concrete case — a delivery note generated from a **batch transfer** grouping several delivery orders — and build the template from scratch, all the way to adding a QR code and a barcode.

## The scenario

We use an Odoo **batch transfer**: several individual delivery orders (one per warehouse or per shipment) grouped into a single dispatch. It's a native Odoo feature (`Inventory > Batch Transfers`), useful whenever a customer receives an order in several parcels.

On this document, we want to:

- List every product across all transfers, each with its source delivery order
- Automatically flag a **partial delivery** (backorder) with no formula to maintain
- Add a **tracking QR code** and a **barcode** that can be scanned in the warehouse

## 1\. Install the module

From Odoo's **Apps** menu, search for "Carbone" and click Install. Once active, a new configuration menu appears, letting you associate any Odoo report (delivery note, batch transfer, invoice...) with a Carbone template in DOCX or XLSX format — in place of the default QWeb report.

![Carbone module page (report_carbone) in the Odoo Apps menu](https://carbone.io/blog/content/images/2026/07/image-6.png)

Carbone module page (report\_carbone) in the Odoo Apps menu

## 2\. Prepare a test batch transfer

Before building the template, we set up a real case in Odoo:

1. `Inventory > Configuration > Settings` → check that **"Batch Transfers"** is enabled.
2. `Inventory > Transfers > Deliveries` → create two delivery orders for the same customer, each with its own product lines.
3. On one of them, set a **done** quantity lower than the **demand** quantity on a line (for example 7 instead of 10) — this is what will let us test automatic backorder detection.
4. `Inventory > Batch Transfers` → create a batch and add the two delivery orders to it.

![Odoo Delivery Note with an actual quantity less than the requested quantity](https://carbone.io/blog/content/images/2026/07/image-7.png)

Odoo Delivery Note with an actual quantity less than the requested quantity

## 3\. Link the Carbone template to the report

The full template used in this article is **available for download below:**

[bon-livraison-templatebon-livraison-template.docx23 KBdownload-circle](https://carbone.io/blog/content/files/2026/07/bon-livraison-template-1.docx "Download")

You can use it as-is to follow the tutorial, or as a starting point for your own document — it's an ordinary Word file, and every element (logo, columns, notes) is edited directly in Word.

On the configuration side, once the module is installed:

1. Enter your **Carbone API key** in the module settings (create an account on [carbone.io](https://carbone.io/?ref=carbone.io) to get one — or point to your On-Premise instance).
2. Open the Carbone reports configuration and create an entry for the **Batch Transfer** model (`stock.picking.batch`).
3. **Upload the `.docx` file** as the template linked to this report, and choose the desired output format (PDF for printing, or DOCX if the document should stay editable).
4. Click **"Add to the Print menu"**: this is what makes the report show up in the batch transfer's Print menu, alongside the native reports.

From there, every batch transfer printout will go through Carbone instead of the native QWeb rendering. To adjust the document later, you just replace the uploaded file — no restart, no module update.

![Configuring the Carbone Report in Odoo: Template, Output Format, and Print Menu](https://carbone.io/blog/content/images/2026/07/image-8.png)

Configuring the Carbone Report in Odoo: Template, Output Format, and Print Menu

## 4\. Understanding and building the Word template

The Carbone principle fits in one sentence: the template is an **ordinary Word document** in which you insert **tags between curly braces** (`{d.name}`, `{d.move_ids[i].product_id}`...) wherever data should appear. At generation time, Carbone merges the JSON sent by Odoo with the template: each tag is replaced by its value, lines marked `[i]` are repeated for every item in a list, and conditional blocks appear or disappear depending on the data. The formatting (fonts, tables, colors, headers) stays whatever the Word document defines — Carbone only touches the content.

![Word template for a delivery note with "Carbone" tags](https://carbone.io/blog/content/images/2026/07/image-9.png)

Word template for a delivery note with "Carbone" tags

Let's go through each part of the document.

### Company header + QR code

At the top of the document, a classic block (company name, address) and a tracking QR code. The QR code is a "placeholder" image inserted in Word, whose **alt text** contains the Carbone tag:

```
{d.name:prepend('https://tracking.example-carrier.com/do/'):barcode(qrcode,eclevel:M)}
```

![CarboneTag: barcode inserted into the alternative text of a placeholder image in Word](https://carbone.io/blog/content/images/2026/07/image-10.png)

Carbone Tag: barcode inserted into the alternative text of a placeholder image in Word

Carbone replaces the image with the generated QR code, sized to the placeholder defined in the template.

### A barcode for warehouse scanning

Same principle, with a Code128 barcode under the title, in the alt text of a second image:

```
{d.name:barcode(code128,includetext:true,textsize:10)}
```

### Document header

Batch number, date, operation type, and the list of included transfers — simple fields to insert, each matching a piece of information shown as-is on the transfer in Odoo:

```
Batch no.: {d.name}                          Date: {d.scheduled_date:formatD('MM/DD/YYYY')}
Operation type: {d.picking_type_id}          Transfers included: {d.picking_ids:arrayJoin(', ')}
```

`arrayJoin` turns the list of delivery orders in the batch directly into a readable comma-separated line — handy for avoiding a loop just for a simple summary.

### The product list, across all transfers

Each product line from a stock move (`stock.move`) is looped over, with its source delivery order, the demand quantity, and the available quantity:

```
{d.move_ids[i].picking_id}   {d.move_ids[i].product_id}   {d.move_ids[i].product_uom_qty}   {d.move_ids[i].quantity}   [status]
{d.move_ids[i+1]}
```

### Flagging a backorder, in a single tag

```
{d.move_ids[i].product_uom_qty:sub(.quantity):ifGT(0):show('⚠ backorder'):elseShow('OK')}
```

One chained formatter: subtract the available quantity from the demand quantity, then show the note only if the result is positive. No intermediate field and no logic needed on the Odoo side — this is exactly the case we set up in step 2.

### Totals

```
Number of transfers: {d.picking_ids:len}
Total available quantity: {d.move_ids[].quantity:aggSum:formatN(2)}
```

`:len` counts the number of delivery orders in the batch, and `:aggSum` aggregates a value across all lines, with no explicit loop needed for a total.

## 5\. Generate the document

Back on the batch transfer created in step 2: a click on **Print** now generates the delivery note as a PDF (or Word, if the document should stay editable), with:

- the backordered line flagged automatically,
- the QR code and barcode generated on the fly,
- a layout that business teams can edit entirely, with no ticket and no line of code.

![Launch of Delivery Note Generation with Carbone](https://carbone.io/blog/content/images/2026/07/image-11.png)

Launch of Delivery Note Generation with Carbone

## Going further

The delivery note is just one example: the same approach applies to **every document Odoo generates** — quotations, invoices, contracts, payslips, labels, inventory reports... — and to every output format (PDF, Word, Excel, PowerPoint, ODT). A single template per document type is enough, whatever format is requested at generation time.

- Full documentation of Carbone tags and formatters: [https://carbone.io/documentation/design/overview/getting-started.html](https://carbone.io/documentation/design/overview/getting-started.html?ref=carbone.io)
- Carbone module on the Odoo Apps Store: [https://apps.odoo.com/apps/modules/19.0/report\_carbone](https://apps.odoo.com/apps/modules/19.0/report%5Fcarbone?ref=carbone.io)

Other Carbone formatters are worth exploring for this kind of document:

- **`:color`**: apply a dynamic color to a cell or a row based on a condition — for example a backordered line highlighted in red, rather than just a text note.
- **`:html`**: if your delivery note includes a notes or instructions field entered as rich text in Odoo, this formatter injects it as-is (bold, lists...) into the generated document.

Finally, Carbone also comes in an **On-Premise** edition: the rendering engine runs directly in your own infrastructure rather than through the Cloud API — often a deciding factor for organizations bound by data residency or confidentiality requirements (public sector, healthcare, finance...). More information : [https://carbone.io/documentation/developer/on-premise-installation/introduction.html](https://carbone.io/documentation/developer/on-premise-installation/introduction.html?ref=carbone.io)