Skip to main content
iVentureTeam

mail_attachments_selector

The upload control on Odoo's document send wizard. It routes files through the messaging layer rather than creating attachments directly, and every file it adds is marked so it can be cleaned up later.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated September 8, 20266 min read
Technical namemail_attachments_selector
Field typesjson
Viewsform
Moduleaccount, present wherever Invoicing or Accounting is installed
Used in core1 occurrence, the document send wizard in account
VersionsOdoo 20.0, Odoo 19.0
No-code setupNo. The field holds a structured value the server builds
Alternativesmail_attachments, many2many_binary, account_file_uploader, binary

What the attachment selector does

Odoo's document send wizard has two attachment widgets that work as a pair. One renders the list of files that will go out, including the invoice PDF that does not exist yet. This one is the control that lets a user add something of their own to that list.

It is more than a file input. Rather than creating an attachment record directly, it uploads through Odoo's messaging attachment service, against the thread of the document being sent. That means the upload goes through the same path as an attachment dropped on a chatter, with the same handling and the same server-side checks.

Once the upload completes, the widget appends an entry to the same structured field the list widget reads, carrying the new attachment's id, name and type, and marked as a manual upload. From that moment the list widget owns it, including the cleanup if the wizard is abandoned.

What this means for your team

The reason to care about the routing is that it determines where the file ends up. Because the upload is made against the document's own thread, the attachment is attached to the invoice, not orphaned on a wizard record that disappears. Anything the customer receives is therefore also on the document afterwards, which is what an audit trail needs.

The manual flag is the other half of that. Files a user uploaded and then abandoned are deleted when the wizard closes unsaved, so a hesitant user does not leave debris behind. Files that survive a completed send stay attached, which is the correct asymmetry.

For anyone customizing the send flow, the pairing is the thing to preserve. Splitting the upload control from the list is what allows the wizard to place them in different parts of the form, but they share one field and one set of conventions, and replacing only one of them breaks the cleanup.

Supported options in Odoo 19

There is nothing to configure. The registration provides a component and nothing else: no options, no supported types, no extractor. What it needs comes from two fields on the wizard, read by literal name. Read from mail_attachments_selector.js, Odoo 19.0.

OptionTypeWhat it does
modelcompanion fieldRead by literal name on the wizard. The technical model of the document being sent, used to resolve the thread the upload targets.
res_idscompanion fieldRead by literal name and parsed as JSON. Only the first id is used to resolve the thread, so a batch send uploads against the first document.

Two fields are read literally. The widget takes the target model from one field and the record ids from another, parsing the second as JSON, in order to resolve the thread it uploads against. Neither is declared as a dependency, so both have to be present on the wizard; without them the upload cannot resolve a target.

Working examples

The core usage

<field name="mail_attachments_widget"
       widget="mail_attachments_selector"
       invisible="not display_attachments_widget"/>

Note that it points at the same field as the list widget. The two are two views of one value.

The fields it resolves the target from

# both read by literal name on the wizard
model     # the document's model
res_ids   # a JSON list; the first id is used

Only the first id is used to resolve the thread, which is why a batch send uploads against the first document.

The display half

<field name="mail_attachments_widget"
       widget="mail_attachments"/>

The paired widget that lists the files and handles removal and cleanup.

Uploading through the chatter's own path

The upload path is worth following. When a file is chosen, the widget parses the record ids field, inserts a thread for the target model and the first id into the messaging store, wraps the uploaded data back into a file object, and hands it to the attachment upload service against that thread's composer. The service does the actual creation and returns the attachment.

The widget then builds a small entry with the attachment's id, name and mime type, marks it as not a placeholder and as a manual upload, and appends it to the field. Everything after that, including removal and the cleanup of abandoned uploads, belongs to the list widget reading the same field.

Two details are worth flagging. Only the first record id is used to resolve the thread, so on a batch send the upload is attached to the first document rather than to all of them. And the widget borrows the mail composer's own attachment selector template rather than defining its own, which keeps the control visually identical to the one in the chatter composer.

There is also a piece of dead code in the Odoo 19 file: the setup builds a set of x2many operations against an attachment relation and then never uses it. It does no harm, but it is a leftover from an earlier approach, and it disappears with the rename described below.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. The development branch renames the file and the registered name to a send-specific name and adds its own stylesheet.
Odoo 19.0VerifiedFirst version. Verified against the shipped source.
Odoo 18.0Not availableWidget does not exist.
Odoo 17.0Not availableWidget does not exist.
Odoo 16.0Not availableWidget does not exist.

Upgrade note. The widget is new in Odoo 19, so there is nothing to migrate from earlier versions. Going forward is the direction that needs attention, because the name changes on the development branch. See the Odoo 20 section.

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.

The widget is renamed. On the development branch the file and the registered name both change to a send-specific name, and the wizard view is updated to match. A dedicated stylesheet appears alongside it, which suggests the control is being given its own presentation rather than borrowing the composer's.

A view still carrying the current name will not resolve on Odoo 20, so the upload control would silently disappear from a customized send wizard. Search custom views for this name before upgrading.

Common problems and fixes

SymptomCause and fix
Uploading does nothingThe wizard does not carry the model or record ids fields the widget reads by literal name. Add both fields to the wizard model and the view.
The uploaded file is attached to the wrong documentOnly the first record id is used to resolve the thread on a batch send. Expected. Attach per document rather than through a batch wizard if it matters.
Uploaded files vanishThe wizard was closed without being saved, and the list widget deletes manual uploads in that case. Complete the send, or re-upload after saving.
The file does not appear in the listThe list widget is pointed at a different field. Both widgets must reference the same field; they are two views of one value.
Options are ignoredThe registration has no extractor. Nothing to configure here.
The upload control disappeared after upgradingThe registered name changes on the Odoo 20 development branch. Update custom views to the new widget name during the upgrade.

Attachment selector vs the alternatives

WidgetBest forKey difference
mail_attachments_selectorAdding files to a send wizard whose attachment list is a structured valueUploads through the messaging attachment service against the document's own thread, then appends a flagged entry
mail_attachmentsDisplaying and removing the same listThe paired widget; it renders and cleans up, this one uploads
many2many_binaryA plain list of attached files on a real relationBacked by a relation, with its own upload button and no thread routing
account_file_uploaderCreating documents from uploaded filesCreates records from the upload rather than attaching to an existing one
binaryA single file on a recordOne field, one file, no list

Use the standard file attachment widgets wherever a real relation is available; they are simpler and need no companion. This one exists because the send wizard's attachment list is a structured value rather than a relation, and because the upload has to be routed through the document's own thread. Keep it paired with the list widget rather than mixing it with a generic uploader.

Frequently asked questions

How is this different from mail_attachments?+
They are a pair on the same field. This one is the upload control; the other renders the list, handles removal and deletes abandoned uploads when the wizard closes unsaved.
Where do uploaded files end up?+
Attached to the document being sent. The widget uploads through Odoo's messaging attachment service against that document's thread, rather than creating an attachment record directly.
Why does a batch send attach to only one document?+
The widget parses the record ids field and uses the first id to resolve the thread. That is a limitation of how the target is resolved, not a setting.
Does it support any options?+
None. The registration provides a component only, with no options, no extractor and no supported types. It reads two fields on the wizard by literal name.
What happens to it in Odoo 20?+
It is renamed on the development branch, along with its file, and the wizard view is updated. A custom view still using the old name would lose its upload control silently.

Invoices that arrive with everything attached

Terms, delivery notes, electronic invoicing files and the odd one-off document all have to reach the customer with the invoice. We configure and test Odoo document sending end to end, on versions 16 through 19.

Book a free consultation

How this page was produced

The upload routing through the messaging attachment service, the entry shape it appends, the two fields read by literal name and the unused operations set were read from mail_attachments_selector.js on the Odoo 19.0 branch, with the paired behavior read from the list widget in the same folder. The usage comes from account/wizard/account_move_send_wizard.xml. The rename was verified by locating the replacement file and the updated view on the public development branch. Spotted an error? Tell us and we will correct the page.