Skip to main content
iVentureTeam

line_open_move_widget

A many2one field whose internal-link arrow does not open the record you expect, on purpose: line_open_move_widget routes analytic lines to the journal item's real business document.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 25, 2026Updated August 25, 20266 min read
Technical nameline_open_move_widget
Field typesmany2one
Viewsform, list
Moduleaccount, installed with Invoicing or Accounting
Used in core2 occurrences in 1 module: move_line_id on the analytic line form and list
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo. Applied in view XML; Studio has no toggle for it
Alternativesopen_move_widget, many2one, so_line_field

What the line open move widget does

Analytic lines carry a move_line_id pointing at the journal item they were generated from. With a plain many2one widget, the internal-link arrow next to that field opens the raw account.move.line form, a screen almost nobody wants, since a journal item out of context answers no business question.

line_open_move_widget keeps everything else about the many2one, the autocomplete, the domain handling, the option set, and swaps the arrow's destination. Its openRecordAction override calls the server method action_open_business_doc on the related record, which resolves the most meaningful representation: the customer invoice if the item belongs to one, the payment if it is a payment line, the journal entry otherwise.

Editing is untouched. Users can still search, pick or clear the journal item like any relation, and core wraps the field with no_create so analytic screens cannot spawn journal items by accident.

What this means for your team

Analytic accounting is where controllers reconcile operational truth with the books, and the loop is always the same: this cost line looks off, show me the document behind it. This widget makes that loop one click long. From an analytic item you land directly on the vendor bill or payroll entry that produced it, with no detour through a journal-item form that lacks every piece of context.

The detail worth teaching your team: the arrow deliberately skips the record it technically points to. Users trained on standard many2one behavior sometimes report it as a bug, expecting the move line form. It is the opposite of a bug; the routing is the feature.

For custom analytic or costing models that also reference journal items, reusing this widget gives your screens the same drill-down for free. That, plus a no_create to keep data entry clean, is a pattern we apply in most analytic accounting setups we deliver.

Supported options in Odoo 19

The widget declares nothing of its own: its registration is buildM2OFieldDescription(LineOpenMoveWidget), so the option surface is exactly the standard many2one's, verified in Odoo 19.0. The rows below are the declared set; the deeper behavior of each is documented on the many2one page.

OptionTypeWhat it does
no_createbooleanInherited from many2one. Blocks every creation path in the dropdown. Core sets it on both analytic usages so journal items cannot be created from analytic screens.
no_quick_createbooleanInherited from many2one. Removes only the inline Create entry.
no_create_editbooleanInherited from many2one. Removes only the Create and Edit dialog entry.
no_openbooleanInherited from many2one. Hides the internal-link arrow entirely, which also disables this widget's business-doc routing.
search_thresholdnumberInherited from many2one. Minimum typed characters before the journal item search fires.
placeholder_fieldfield nameInherited from many2one. A char field on the record supplying a dynamic placeholder.

no_open does not just hide the arrow, it removes the widget's whole point. With no_open the override never fires and you are left with a plain, arrow-less many2one. If you want to block navigation, use the standard widget instead and keep intent obvious in the XML.

Working examples

How core uses it (analytic line form)

<field name="move_line_id"
       options="{'no_create': True}"
       widget="line_open_move_widget"/>

In the analytic items list, hidden by default

<field name="move_line_id"
       widget="line_open_move_widget"
       optional="hide"/>

Both usages relate to account.move.line, which matters: the override calls action_open_business_doc with resModel: "account.move.line" baked in and the related record's id. Reuse the widget only on fields whose comodel is account.move.line; on any other relation the id would be sent to the wrong model and the click would error or open something unrelated.

Three lines of override, three eras of API

The override is three lines; the routing is server side. openRecordAction replaces the m2o's standard open with a doActionButton of type object calling action_open_business_doc, passing this.props.record.data[this.props.name].id, the id of the linked journal item. All if-this-then-invoice logic lives in the Python method, so widget behavior automatically tracks any server-side improvement to document resolution.

The 19 rewrite changed the shape, not the behavior. Through 16, 17 and 18 the widget was a Many2OneField subclass; 17 and 18 are byte-identical descriptor-style files. Odoo 19 rebuilt it on the composition API, a template rendering the shared Many2One component with computeM2OProps plus the one overridden callback, part of the framework-wide m2o decomposition. Anyone who patched the old class needs to re-target the new component, the classic post-migration JavaScript fix.

Value access drifted with the record API. 16 read props.value[0], 17 and 18 read record.data[name][0] off the id-name tuple, 19 reads record.data[name].id off the record-like value object. Any copy-pasted derivative widget carries the era of its source.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Only prop-system modernization on the development branch; behavior unchanged. Re-verified after launch.
Odoo 19.0VerifiedComposition-API rewrite of the same behavior; verified against the shipped source.
Odoo 18.0VerifiedDescriptor-style Many2OneField subclass, byte-identical to 17.
Odoo 17.0VerifiedSame behavior on the id-name tuple record API.
Odoo 16.0VerifiedOriginal class-registered version reading props.value; same click routing.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The notes below come from the public development branch, unstable until feature freeze, and this page is re-verified after release.

The widget's file shows no behavioral change: the class, the hardcoded account.move.line model, the registration and both core usages survive. The only diff is prop plumbing, useProps with the shared many2OneFieldProps replacing the static props declaration, the same modernization sweeping every m2o-family widget on the branch.

The practical migration note is inherited instead: base many2one behavior keeps evolving on master, and since this widget spreads the m2o descriptor, whatever lands there lands here. Include it in the widgets you smoke-test during an Odoo 20 migration, but expect it to just work.

Common problems and fixes

SymptomCause and fix
Arrow opens an invoice instead of the journal itemaction_open_business_doc resolves the most specific document for the line. Expected; that is the widget's purpose. The raw item is reachable from the entry itself.
Click errors or opens nonsense on a custom fieldThe override sends the related id to a hardcoded account.move.line model; your field relates to something else. Only use the widget on account.move.line relations; otherwise use the plain many2one.
No arrow visible at allno_open is set, or the field is empty, or the record is in a state where the m2o hides the link. Remove no_open and check the field has a value.
Users can create journal items from the analytic screenA custom view dropped the no_create option core passes. Restore options="{'no_create': True}".
A JavaScript patch on the widget stopped working after upgrading to 19The class was rebuilt on the composition API; the old Many2OneField subclass no longer exists. Re-target the patch at the new component or its m2oProps getter.

Line open move widget vs the alternatives

WidgetBest forKey difference
line_open_move_widgetmove_line_id fields on analytic and costing recordsStandard editable many2one whose arrow opens the item's business document via a hardcoded account.move.line call
open_move_widgetRead-only entry numbers in item listsRenders a char value as a link; no editing, routes through the current record instead of the related one
many2oneAny relation where the standard form link is correctIdentical editor, arrow opens the raw related form
so_line_fieldSale order line references outside sales screensAnother single-purpose m2o wrapper, tuned for sale line naming instead of navigation

The distinction to keep straight: this widget is an editable relation whose arrow is smarter; open_move_widget is a read-only text link. Pick by whether users should be able to change the value.

Frequently asked questions

What does line_open_move_widget change compared to a normal many2one?+
Exactly one thing: the internal-link arrow. Instead of opening the related account.move.line form, it calls action_open_business_doc on that line, landing on the invoice, payment or journal entry it belongs to. Searching, selecting and clearing the value behave like any many2one.
Where is it used in standard Odoo?+
Twice, both on move_line_id of analytic lines in the account module: once on the form with no_create, once as a hidden-by-default column in the analytic items list.
Can I put line_open_move_widget on any many2one field?+
Only on fields relating to account.move.line. The override passes the related id with a hardcoded account.move.line model name, so any other comodel receives a foreign id and the action misfires. For other relations, use the standard widget or subclass with your own model.
Which options does it support?+
The full standard many2one set, since the registration is buildM2OFieldDescription: no_create, no_quick_create, no_create_edit, no_open, search_threshold and placeholder_field, plus the undocumented extractProps extras the m2o family carries. It adds no options of its own.
Did the widget change in Odoo 19?+
Behavior no, implementation yes. 16 through 18 subclassed Many2OneField; 19 rebuilt it as a small component rendering the shared Many2One with one overridden callback. JavaScript patches written against the old class need re-targeting after migration.
Anything to prepare for Odoo 20?+
Nothing specific: the development branch only modernizes prop declarations. Because the widget inherits the m2o descriptor wholesale, it silently picks up whatever the base many2one gains or loses in 20, so include it in post-migration smoke tests. We re-verify this page after the September 2026 release.

Analytic accounting that answers questions in one click?

Cost lines are only useful when the document behind them is one click away. We set up analytic plans, distribution models and drill-down-friendly screens on Odoo 16 through 19, tuned to how your controllers actually investigate numbers.

Upgrade my analytic setup

How this page was produced

Verified by reading open_move_line_move_widget.js and its template in the Odoo 19.0 account module, tracing buildM2OFieldDescription for the inherited option set, and confirming both usages on move_line_id in the analytic line views. Version rows come from diffing the file across the 16.0, 17.0 and 18.0 branches and the public development branch; 17 and 18 were confirmed byte-identical. Click routing was exercised on a clean Odoo 19 database, where the screenshot was captured. Corrections welcome via our contact page.