Skip to main content
iVentureTeam

many2many_tags_banks

Partner bank accounts rendered as tags, each showing whether outgoing payments are allowed. It also saves the partner behind your back, and there is a good reason for that.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated August 27, 20266 min read
Odoo 19 contact form showing bank accounts rendered as tags with an outgoing payment indicator.
Technical namemany2many_tags_banks
Field typesmany2many, one2many
Viewsform
Moduleaccount, present wherever Invoicing or Accounting is installed
Used in core1 occurrence, the bank accounts field on the contact form in account
VersionsOdoo 20.0, Odoo 19.0
No-code setupNo. The option names a companion field, which Studio cannot configure
Alternativesmany2many_tags, many2many_tags_avatar, one2many, res_partner_many2one

What the bank tags field does

A contact can have several bank accounts, and on an accounting-enabled database they matter twice over: for receiving payments and, more sensitively, for sending them. A supplier's account number being wrong or unapproved is the kind of mistake that moves real money to the wrong place.

This widget renders those accounts as tags on the contact form, which suits a short list far better than an embedded table. Each tag shows the account and an indicator for whether outgoing payments are allowed on it, taken from a boolean field you nominate through an option.

It also adds two behaviors around that display. Clicking through opens the full bank account list for the partner, and when the widget mounts it saves the contact if there are unsaved changes, so that a partner being created inline from an invoice exists as a record before anyone tries to attach a bank account to it.

What this means for your team

The outgoing payment flag is a control, not a decoration. Marking an account as not allowed for outgoing payments is how a finance team stops a supplier bank change from being used before it has been verified, which is the shape of most invoice fraud. Putting that state on the tag itself means the person choosing an account sees it without opening anything.

The inline save is the detail worth explaining to a team. Creating a supplier from a bill and adding their bank account in the same breath is common, and it only works if the partner has been written first. The widget does that automatically, which means the contact is committed earlier than a user might expect. On a form people treat as a draft, that is a surprise; on this particular form it is the only way the flow works.

Supported options in Odoo 19

One option is declared by the widget, and the rest are inherited from the color-editable tags widget it extends. The declared option is worth reading closely, because its declared type does not describe what it holds. Read from the widget's source and the tags field, Odoo 19.0.

OptionTypeWhat it does
allow_out_payment_fieldstring (field name)Names a boolean field on the bank account model. The widget injects it into the related fields it loads, marked writable, and passes its value to each tag as the outgoing payment indicator. Declared in the source as a boolean, which describes the field it points at rather than the option itself.(since Odoo 19.0)
color_fieldfieldInherited from the color-editable tags widget. Names the integer field holding each tag's color. Core sets it here.
edit_tagsbooleanInherited. Makes clicking a tag open the bank account record. Core sets it here.(default: false)
no_createbooleanInherited. Removes creation from the tag input entirely.(default: false)
no_quick_createbooleanInherited. Removes creation from the typed text while keeping the popup form path.(default: false)
create_name_fieldfieldInherited. Field the typed text is written into when quick-creating a tag.

The declared type of the new option is misleading. It is announced as a boolean, but the value it takes is the technical name of a boolean field, which the widget then injects into the related fields it loads and reads per tag. Passing an actual boolean will not work; pass the field name, as the core contact form does.

Working examples

The core usage

<field name="bank_ids" string="Bank accounts"
       widget="many2many_tags_banks"
       context="{'default_partner_id': id}"
       domain="[('partner_id','=', id)]"
       options="{'color_field': 'color',
                  'allow_out_payment_field': 'allow_out_payment',
                  'edit_tags': True}"/>

The domain and the default in context are what tie new accounts to this contact. The three options are the full configuration.

Without the outgoing payment indicator

<field name="bank_ids" widget="many2many_tags_banks"
       options="{'color_field': 'color'}"/>

Legal, and the tags render normally. The indicator simply has no field to read.

Locking creation from the tag input

<field name="bank_ids" widget="many2many_tags_banks"
       options="{'allow_out_payment_field': 'allow_out_payment',
                  'no_quick_create': True}"/>

Inherited from the tags widget. Useful where bank accounts should only be created through their own form, with validation.

How the indicator field gets loaded, and why the partner saves

The class extends the color-editable tags field and replaces the component that renders the tag row with its own subclass, so the template can show the outgoing payment indicator. The value for that indicator is added in the method that builds each tag's props, read from the related record's data under the field name you supplied.

Getting that field loaded is the job of the descriptor's related fields function. It takes whatever the base widget already injects and appends an entry for the named field, declared writable. That is what makes the value available on each tag without the view having to know anything about the bank account model.

The save on mount is short and deliberate. When the component mounts it asks whether the model's root record is dirty and, if so, saves it. The source comment explains the case exactly: creating a partner from a move, where the partner has to exist before it can be an account holder. It is worth knowing that this fires on mount, not on interaction, so simply opening a form with unsaved changes and this widget on it will commit the record.

One curiosity in the Odoo 19 file, harmless but odd: the class spreads its own static components list inside its own class body. Because static inheritance resolves that before the field is assigned, it evaluates to the parent's components, which is what was intended. The development branch rewrites it to the conventional form that says so directly.

The list action is plain: it builds a window action for the relation with a list and a form view, applies the field's own domain, and opens it in the current breadcrumb rather than a dialog, so a user reviewing bank accounts gets the full model with its own filters.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. The development branch rebases it onto the plain tags widget and rebuilds the tag as a dedicated badge component.
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; the contact form used a plain relational field before. Going forward, the base it extends changes on the development branch, so any custom module patching this widget should expect the color-editing layer to be gone.

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 can still change; we re-verify the page after release.

The base changes. On the development branch the widget extends the plain tags field rather than the color-editable one, which means the color-picking behavior that comes with that base is no longer part of this widget.

The tag rendering is rebuilt. Instead of subclassing the tag row component, the development branch defines a small dedicated bank tag component built on the shared badge tag, with an explicit prop list including the outgoing payment flag. The indicator itself and the option that feeds it are unchanged, and so are the save on mount and the list action.

Common problems and fixes

SymptomCause and fix
No outgoing payment indicator on the tagsThe allow_out_payment_field option is missing, so the widget has no field to read. Add options="{'allow_out_payment_field': 'allow_out_payment'}" or your own boolean field name.
The contact saves itself when I open the formThe widget saves the root record on mount when it is dirty, so an inline-created partner exists before accounts are attached. Expected. Finish the contact before adding bank accounts if the timing matters.
New bank accounts are not linked to the contactThe field's context does not set the partner default. Add context="{'default_partner_id': id}" as the core view does.
Tags cannot be recoloredThe color_field option is missing, or the database is on a version where the base no longer offers color editing. Set color_field to the color integer on the bank account model.
Passing True to the indicator option does nothingThe option takes a field name, despite being declared as a boolean. Pass the technical name of the boolean field instead.
The tag list shows accounts of other partnersThe field has no domain restricting it to the current contact. Add domain="[('partner_id','=', id)]" as the core view does.

Bank tags field vs the alternatives

WidgetBest forKey difference
many2many_tags_banksPartner bank accounts shown compactly with their outgoing payment statusTags carrying an indicator from a nominated boolean field, plus an automatic save of the parent record on mount
many2many_tagsAny ordinary many2many rendered as tagsNo indicator, no injected related field and no automatic save
many2many_tags_avatarTags representing peopleRenders avatars rather than a status indicator
one2manyEditing bank accounts in place with their full field setAn embedded table rather than a selection control
res_partner_many2oneChoosing the partner itselfA single relation picker, not a tag list

Use the plain tags widget wherever the extra indicator and the automatic save are not wanted; both are specific to the accounting flow this widget was written for. If bank accounts need to be edited in place with their full field set rather than picked, an embedded list is the honest choice, because tags are for selection rather than for data entry.

Frequently asked questions

What does the indicator on each bank tag mean?+
Whether outgoing payments are allowed on that account. The value comes from a boolean field you name with the allow_out_payment_field option, which the widget loads as a related field on the tag's record.
Why does the contact save itself?+
The widget saves the root record on mount if it has unsaved changes. The source comment gives the reason: a partner created inline from a move has to exist before it can be an account holder.
The option is declared as a boolean. Do I pass True?+
No. Despite the declared type, the value is the technical name of a boolean field. The core contact form passes the field name.
Can I still recolor the tags?+
In Odoo 19 yes, because the widget extends the color-editable tags field and the core view passes a color field. On the development branch the base changes and that layer is gone.
Which versions have it?+
Odoo 19 onwards. Before that the contact form used a plain relational field for bank accounts.

Supplier bank changes are how invoice fraud gets paid

Approval flows on bank accounts, outgoing payment controls and the audit trail behind them are configuration, not luck. We set up payment controls in Odoo Accounting on versions 16 through 19, and document who can change what.

Book a free consultation

How this page was produced

The declared option, the related-field injection, the per-tag prop, the save on mount and the list action were read from many2many_tags_banks.js on the Odoo 19.0 branch, with the inherited behavior read from the tags field in web. The usage and its three options come from account/views/partner_view.xml. Version coverage comes from the absence of the file on 16.0, 17.0 and 18.0, and from a full comparison against the public development branch. Spotted an error? Tell us and we will correct the page.