Skip to main content
iVentureTeam

stock_picking_many2many

The transfers list on a wave. Adding one does not link a record directly: it opens a selection, creates a wizard, and lets the server attach them properly.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated August 28, 20264 min read
Technical namestock_picking_many2many
Field typesmany2many
Viewsform, with an embedded list
Modulestock_picking_batch, batch and wave transfers
Used in core1 occurrence, the transfers list on a wave
VersionsOdoo 20.0, Odoo 19.0
No-code setupNo. It is set in view XML and depends on module-specific data
Alternativesmany2many, sml_x2_many, stock_move_one2many, one2many

What the wave transfers field does

A wave groups work across several transfers so a picker walks the warehouse once. Adding a transfer to a wave is therefore not a link: move lines have to be split, reassigned and re-sequenced, and only the server can do that correctly.

This widget makes the interface match. Rather than the standard add action writing a relation, it opens a selection of transfers and, once chosen, creates the wizard Odoo already uses for this and calls its attach method.

It also filters the selection. Transfers already on the wave are excluded from the dialog, so the list never offers something that is already there.

What this means for your team

Wave picking only pays off if building a wave is quick, and the difference between a good and a bad implementation is usually how many wrong choices the interface allows. Excluding what is already attached removes one whole category of mistake.

The design point is that the server owns the operation. Anything that reshapes stock moves should be a server call, not a relation write, and this widget is a clean example of an interface deferring to that rather than trying to model it in the browser.

Supported options in Odoo 19

The widget declares no supported options and forwards the options dictionary to the embedded list. Its behavior is in the selection and the server call, described below. Read from the wave transfers source, Odoo 19.0.

OptionTypeWhat it does
the exclusion domainbuilt by the widgetTransfers already linked, filtered to those with a real database identifier, are excluded from the selection dialog.(since Odoo 19.0)
from_wave_formcontext key sent to the wizardTells the server the attach call came from the wave form rather than another entry point.(since Odoo 19.0)
editablestring (list attribute)Set on the embedded list. Governs inline editing of the linked rows.

Only saved transfers are excluded. The exclusion filters the relation's current identifiers down to real database ones before building the domain, so unsaved additions are not filtered. In practice a wave is saved before transfers are attached, so this rarely shows.

Working examples

The usual usage

<field name="picking_ids"
       widget="stock_picking_many2many">
  <list> ... </list>
</field>

No options. Adding opens the selection rather than a plain link action.

What happens on selection

# a wizard record is created, then
stock.add.to.wave.attach_pickings(...)
# and the returned action is opened

The server does the splitting and reassignment.

The plain relation

<field name="picking_ids" widget="many2many"/>

Writes the relation directly, which is not enough for a wave.

Deferring the whole operation to the server

The selection is built in two steps. First the widget wraps the standard select-and-create helper so that its own domain is appended before the dialog opens. That domain excludes the transfers already linked, filtered down to those with a real database identifier.

Second, the selection handler does not write anything to the relation. It creates a wizard record carrying the wave and the chosen transfers, calls the wizard's attach method with a context flag saying the call came from the wave form, and opens whatever action the server returns.

That context flag is worth noting: the same wizard is reachable from elsewhere, and the flag is how the server distinguishes the two entry points.

The last piece is a forced many-to-many semantic. The widget always reports the relation as linked rather than owned, so the framework offers linking and unlinking rather than creating and deleting, which is correct because a transfer exists independently of any wave.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Byte-identical to Odoo 19 on the development branch.
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. On earlier versions building a wave went through the wizard from a different entry point, so an upgrade improves the wave form with the standard views and nothing needs migrating.

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.

Byte-identical. The file on the development branch matches Odoo 19 exactly, including the exclusion domain and the wizard call.

Common problems and fixes

SymptomCause and fix
A transfer already on the wave is offered againIt has no database identifier yet, so the exclusion did not catch it. Save the wave first; the filter only excludes saved links.
Adding does nothing visibleThe server returned an action that closes without changing the view. Reload the wave; the attachment happens server side.
The dialog offers unrelated transfersThe field has no domain beyond the exclusion. Add a domain on the field to narrow the candidates.
Rows can be created from the listThe forwarded CRUD settings allow it. Pass no_create in the options; transfers should be linked, not created here.
The wizard behaves differently than expectedThe context flag tells the server the call came from the wave form. Expected; the same wizard behaves differently from other entry points.
Missing widget errorThe batch transfers module is not installed in that database. Install it, or use a plain relation widget.

Wave transfers field vs the alternatives

WidgetBest forKey difference
stock_picking_many2manyLinking transfers to a wave where the link reshapes stock movesOpens a selection, creates a wizard and lets the server attach, excluding what is already linked
many2manyLinks that are just a writeNo wizard, no exclusion and no server call
sml_x2_manyAdding move lines from real stockPicks quants rather than transfers
stock_move_one2manyOperations on a transferThe lines of one transfer rather than a set of transfers
one2manyOwned child recordsCreates and deletes rather than linking

A plain relation widget is right wherever linking really is just a write. Where the link triggers server-side reshaping, as it does for waves and batches, deferring to a wizard is the honest approach even though it costs a dialog.

Frequently asked questions

Why does adding a transfer open a dialog?+
Because attaching a transfer to a wave splits and reassigns move lines. The widget creates the wizard Odoo already uses for that and calls its attach method rather than writing the relation.
Why is a transfer I already added still offered?+
The exclusion filters the relation's identifiers down to real database ones, so an unsaved addition is not filtered. Saving the wave first avoids it.
Does it support options?+
None of its own. The options dictionary is forwarded to the embedded list as CRUD settings.
Why force many-to-many behavior?+
A transfer exists independently of any wave, so the framework should offer linking and unlinking rather than creating and deleting.
Which versions have it?+
Odoo 19 onwards, and byte-identical on the Odoo 20 development branch.

Wave picking that actually saves the walk

Batching, waves and zone picking each suit a different warehouse layout, and choosing wrong costs more steps than it saves. We design and implement Odoo picking strategies on versions 16 through 19.

Book a free consultation

How this page was produced

The wrapped selection helper, the exclusion domain filtered to real identifiers, the wizard creation, the attach call with its context flag and the forced many-to-many semantic were read from the wave transfers source on the Odoo 19.0 branch. Version coverage comes from the absence of the file on 16.0, 17.0 and 18.0, and a byte comparison against the public development branch. Spotted an error? Tell us and we will correct the page.