Skip to main content
iVentureTeam

quotation_document_many2many

The upload area for the PDF pages Odoo wraps around a quotation. It borrows the product document kanban wholesale and changes three things: where uploads go, what they attach to, and that only PDFs are accepted.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated September 2, 20265 min read
Technical namequotation_document_many2many
Field typesmany2many, one2many
Viewsform, with an embedded kanban
Modulesale_pdf_quote_builder, the quotation PDF builder
Used in core1 occurrence, the quotation template form in sale_pdf_quote_builder
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0
No-code setupNo. It posts to a module-specific upload route
Alternativesmany2many_binary, binary, x2_many_image, one2many

What the quotation documents field does

Odoo's quote builder assembles a customer-facing PDF by wrapping the generated quotation in pages you supply: a cover, a company introduction, terms, a closing page. Those pages are uploaded PDFs attached to a quotation template.

This widget is the area where they are uploaded and shown. It does not invent an interface: it borrows the kanban renderer and upload button that product documents already use, so the cards, the drag and drop area and the upload flow are the ones people already know from products.

What it supplies is the context. Uploads are posted to the quote builder's own route, tagged with the quotation template they belong to, and restricted to PDF files, which is the only format that can be stitched into the output.

What this means for your team

Branded quotations are a sales asset, and the reason they often do not exist is that producing them is a manual assembly job. Attaching the pages to a template once and having every quotation come out wrapped in them turns that into a setup task rather than a per-quote task.

The PDF restriction is worth stating to whoever uploads. A cover designed in a presentation tool has to be exported first; the upload area will not accept it otherwise, and the restriction is deliberate because the output is assembled by merging PDFs.

The other operational note is that the template has to be saved before documents can be attached. The widget captures the template's identity when it renders, so a brand new unsaved template has nothing to attach uploads to.

Supported options in Odoo 19

The widget declares no options of its own, and neither does the x2many descriptor it spreads. What it configures instead are three values set in its own initialization, listed below. Read from quotation_document_kanban_widget.js, Odoo 19.0.

OptionTypeWhat it does
uploadRoutefixed in the sourceWhere uploaded files are posted. Not configurable from the view.(default: the quote builder's upload route)(since Odoo 18.0)
allowedMIMETypesfixed in the sourcePassed to the upload control, so the file dialog itself filters to PDFs. Not configurable from the view.(default: application/pdf)(since Odoo 18.0)
sale_order_template_idform data keyCaptured from the record when the component starts and sent with every upload, so the file is attached to the right quotation template.(since Odoo 18.0)

All three settings are fixed in the source. The upload route, the accepted file type and the key identifying the template are written into the widget, so there is no way to accept another format or post elsewhere without replacing it.

Working examples

The core usage

<field name="quotation_document_ids"
       widget="quotation_document_many2many"/>

No options. The embedded kanban comes from the product document renderer the widget reuses.

What it posts

route: /sale_pdf_quote_builder/quotation_document/upload
form data: { sale_order_template_id: <the template> }
accepted: application/pdf

All three are set when the component starts and cannot be changed from the view.

The product equivalent

<field name="product_document_ids"
       widget="product_document_kanban"/>

The same interface for documents attached to a product, which is where this widget's renderer and upload button come from.

Borrowed wholesale, configured in three lines

The widget is twenty lines, and almost all of its behavior is inherited. It supplies its own template, adds the upload button and the product document kanban renderer to its component list, and sets three instance values in its initialization.

The one behavior change in its history is where the template identity is captured. The Odoo 18 version recomputed it before every render, which meant it followed the record but did work repeatedly for a value that does not change during an edit session. Odoo 19 captures it once when the component starts.

That is fine on the core form, where the field appears on a saved template, and it is the thing to know if you reuse the widget: the identity is read at startup, so a field rendered before the record is saved will carry no template.

The accepted type is passed to the upload control rather than validated afterwards, so the file dialog itself filters to PDFs. A file dropped from outside that dialog would still be rejected server side, but the interface makes the constraint visible before the upload starts.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Byte-identical to Odoo 19 on the development branch.
Odoo 19.0VerifiedVerified against the shipped source. The template identity is captured once at startup.
Odoo 18.0VerifiedFirst version. The identity was recomputed before every render.
Odoo 17.0Not availableWidget does not exist.
Odoo 16.0Not availableWidget does not exist.

Upgrade note. The widget arrived in Odoo 18 and needs no view change. The Odoo 19 difference is internal: the template identity is captured once at startup rather than before every render, which matters only if you reuse the widget on an unsaved record.

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 the page after release.

Byte-identical. The file on the development branch matches Odoo 19 exactly, including the upload route, the accepted type and the startup capture.

Common problems and fixes

SymptomCause and fix
Uploads are rejectedThe file is not a PDF; the accepted type is fixed in the source. Export the page to PDF before uploading.
Uploads do not attach to the templateThe template identity was captured before the record was saved. Save the quotation template first, then upload.
The upload area does not appearThe embedded view is not a kanban, so the renderer the widget substitutes is not used. Use the field with its kanban subview, as the core form does.
Options are ignoredThe widget declares none, and the x2many descriptor forwards the dictionary to the embedded view. Nothing to configure here.
Files upload but the PDF is unchangedThe documents are attached but not enabled in the quote builder configuration. Check the builder settings on the template.
Missing widget errorThe quote builder module is not installed in that database. Install the PDF quote builder.

Quotation documents field vs the alternatives

WidgetBest forKey difference
quotation_document_many2manyUploading the PDF pages that wrap a generated quotationReuses the product document kanban with a fixed route, template key and PDF-only restriction
many2many_binaryA plain list of attached filesNo kanban cards, no drop area and no route
binaryA single file on the recordOne field, one file
x2_many_imageImage galleries on a relationImages with a media dialog rather than PDF uploads
one2manyEditing the document records themselvesA table rather than an upload area

For documents attached to a product, use the product document kanban widget directly; it is the same interface without the quote builder's route. For an ordinary list of attachments with no upload area of this kind, the binary attachments widget is lighter and needs no module-specific route.

Frequently asked questions

Why are only PDFs accepted?+
The output is assembled by merging PDFs, so anything else could not be stitched in. The accepted type is passed to the upload control, so the file dialog filters before the upload starts.
Why do my uploads not attach?+
The widget captures the quotation template's identity when the component starts. On an unsaved template there is nothing to attach to, so save first.
Can I change the upload route?+
No. The route, the accepted type and the form data key are all written into the widget's initialization.
Is it the same interface as product documents?+
Yes, deliberately. It reuses the product document kanban renderer and upload button, so the cards and the drop area behave identically.
What changed in Odoo 19?+
Only where the template identity is captured: once at startup rather than before every render. It matters only if you reuse the widget on a record that is not yet saved.

Quotations that look like your company sent them

Branded covers, terms and closing pages wrapped around every quote are a setup job once, not a design job per deal. We configure Odoo's quote builder and the sales documents around it, on versions 16 through 19.

Book a free consultation

How this page was produced

The three configured values, the reused renderer and upload button and the startup capture were read from quotation_document_kanban_widget.js on the Odoo 19.0 branch, with the inherited behavior read from the product document kanban components in the product module. The usage comes from sale_pdf_quote_builder/views/sale_order_template_views.xml. Version coverage comes from the absence of the file on 16.0 and 17.0, a comparison with 18.0, and a byte comparison against the public development branch. Spotted an error? Tell us and we will correct the page.