Skip to main content
iVentureTeam

customContentKanbanLikeWidget

The card grid for building a quotation PDF: pick headers, per-line documents and footers, and fill the form fields inside them.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated August 27, 20264 min read
Technical namecustomContentKanbanLikeWidget
Viewsform (view widget, <widget> element)
Modulesale_pdf_quote_builder, the Sales app
Used in coreThe quotation form in sale_pdf_quote_builder
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0
No-code setupNo. It is set in view XML and depends on module-specific data
Alternativesquotation_document_many2many, many2many_binary, pdf_viewer

What the quote PDF picker does

A quotation that goes out as a PDF is often more than the order lines: a cover page, terms, a product datasheet per line, a signature page. The PDF quote builder assembles those from documents attached to templates and products.

This widget is the picker for that assembly. It shows three groups of cards, headers, per-line documents and footers, each selectable, and each able to carry form fields whose values you fill in here.

All of that, the selections and the field values, is serialized into one JSON field on the quotation, which is what the PDF generation later reads.

What this means for your team

Quote presentation is a real differentiator in considered purchases, and the practical obstacle is that assembling a good-looking PDF by hand does not scale past a few quotes a week. Making it a card grid does.

The thing to invest in is the document library. The widget only offers what the template and the products provide, so the quality of the output is decided before anyone opens a quotation.

Supported options in Odoo 19

The widget declares no options and takes the standard view widget props. What it reads, writes and suppresses is listed below. Read from the custom content source, Odoo 19.0.

OptionTypeWhat it does
customizable_pdf_form_fieldsfield written by the widgetReceives the serialized selections and form field values as a single JSON string.
sale_order_template_idfield watched by an effectChanging it refetches the available documents, because the template decides which apply.
statefield watched by an effectOn confirmation the widget forces itself read-only and saves, freezing the composition.
product_document_idsrelation written per lineLinked or unlinked with parent updates and change handlers deliberately suppressed.

It saves before it fetches. The refresh saves the quotation first and only fetches when that succeeded, which is what makes documents on newly added order lines available without a manual save.

Working examples

Placing it

<widget name="customContentKanbanLikeWidget"/>

No attributes. It reads the quotation and its lines.

What is stored

{ header: {...}, line: {...}, footer: {...} }
# serialized into one field on the quotation

Document names and their form field values.

When it refetches

# whenever the quotation template changes

Because that changes which documents apply.

Three suppressions, and a prop written from inside

Two behaviors here are unusual enough to be worth calling out.

The first is how a line document selection is written. The widget finds the order line record, sets a flag on it that prevents the parent from being updated, then links or unlinks the document with an immediate save. The source explains both: the flag stops a network call to save the parent, and the immediate save stops a change handler from firing. The line relation is then updated once, explicitly without change handlers.

That is three separate suppressions in one method, and the reason is volume: a quotation with twenty lines and a document each would otherwise trigger a cascade of server round trips.

The second is the confirmation behavior. An effect watches the quotation's state, and when it becomes confirmed the widget sets its own read-only prop and saves the record to force a refresh. Writing to a prop is unconventional; the effect is that a confirmed order's PDF composition freezes.

The serialization itself is mechanical: selected headers and footers, and per line the selected documents, each reduced to a name and a map of form field values, all merged into one object and written to the quotation's JSON field.

Version compatibility

VersionStatusNotes
Odoo 20.0Partial / changedNot released. Reworked alongside the newer state conventions.
Odoo 19.0VerifiedVerified against the shipped source.
Odoo 18.0VerifiedFirst version with this card grid.
Odoo 17.0Not availableWidget does not exist.
Odoo 16.0Not availableWidget does not exist.

Upgrade note. Present since Odoo 18 in this form. Odoo 17 had an earlier PDF quote builder without this card grid, so an upgrade changes how documents are chosen.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The development branch is unstable and this may still change; we re-verify this page after the release.

Reworked. The development branch changes the component alongside the newer state conventions. The three card groups and the single JSON field are what to expect to survive.

Common problems and fixes

SymptomCause and fix
No documents are offeredThe quotation template and the products carry none. Attach documents to the template or the products first.
The cards go read-onlyThe quotation was confirmed, which freezes the composition deliberately. Expected. Amend before confirming.
Documents change when I switch templateAn effect refetches on template change. Expected; the template decides which documents apply.
Selecting a line document does not trigger pricing changesChange handlers are deliberately suppressed on that write. Expected, and intentional for performance.
Form field values are lostThey live in the serialized JSON field, written when a selection changes. Change a selection to force a rewrite, then save.
The widget is missingThe PDF quote builder module is not installed. Install it.

Quote PDF picker vs the alternatives

WidgetBest forKey difference
customContentKanbanLikeWidgetAssembling a quotation PDF from selectable header, line and footer documentsSerializes every selection and form value into one JSON field, suppressing change handlers as it writes
quotation_document_many2manyThe document relation itselfA relation field rather than a card grid
many2many_binaryAttaching files to a quotationSeparate files rather than one assembled PDF
pdf_viewerReading a PDF inlineViewing rather than composing

Attaching documents to the quotation directly is the simple alternative and produces a set of separate files rather than one assembled PDF. Which is right depends on whether the client should receive one document or several.

Frequently asked questions

Where are the selections stored?+
In a single JSON field on the quotation, holding the selected headers, per-line documents and footers along with their form field values.
Why does it save before showing documents?+
So that documents attached to newly added order lines are offered. It only fetches when the save succeeded.
Why do line document changes not trigger recalculation?+
The write deliberately suppresses parent updates and change handlers, because a quotation with many lines would otherwise cause a cascade of server round trips.
Why do the cards lock after confirmation?+
An effect watching the quotation state forces the widget read-only and saves, so a confirmed order's PDF composition cannot drift.
Which versions have it?+
Odoo 18 onwards. The development branch reworks it alongside the newer state conventions.

Quotes that look like the deal is worth it

Assembled PDFs, dynamic pricing and templates turn quoting from a document exercise into a sales asset. We implement Odoo Sales and the quote builder, on versions 16 through 19.

Book a free consultation

How this page was produced

The save before fetch, the refetch effect keyed on the quotation template, the parent update flag and immediate save when writing a line document, the explicit relation update without change handlers, the read-only forcing on confirmation and the JSON serialization shape were read from the custom content source on the Odoo 19.0 branch. Version coverage comes from comparing the file across the 18.0 branch and its absence on 16.0 and 17.0, plus a comparison against the public development branch. Spotted an error? Tell us and we will correct the page.