stock.forced_placeholder
On Odoo's replenishment screen, an empty Route cell still tells you which route would apply. That is stock.forced_placeholder: a many2one whose placeholder text survives where the standard widget would show nothing.
| Technical name | stock.forced_placeholder |
|---|---|
| Field types | many2one |
| Views | list (the replenishment view), form |
| Module | stock (Inventory) |
| Used in core | 5 occurrences across 3 modules, including stock, mrp, purchase_stock, on replenishment views |
| Versions | Odoo 20.0, Odoo 19.0 |
| No-code setup | No: it is applied in XML on the replenishment views |
| Alternatives | many2one, package_m2o, many2one_uom |
What the Forced Placeholder widget does
What this means for your team
Supported options in Odoo 19
| Option | Type | What it does |
|---|---|---|
placeholder_field | field name (char) | Inherited from many2one, but essential here: names the char field whose per-record value renders as the placeholder, in the input when editing and in place of the empty value in readonly. Core points it at computed fields like route_id_placeholder.(since Odoo 19.0) |
no_create | boolean | Inherited from many2one: removes both create paths from the dropdown. Core sets it on the replenishment route cell. |
no_open | boolean | Inherited from many2one: removes the link to the selected record. Independent of this widget's automatic link removal in readonly mode. |
search_threshold | number | Inherited from many2one: minimum typed characters before the autocomplete searches.(since Odoo 19.0) |
Without placeholder_field (or a static placeholder attribute) this widget is pointless. The whole difference from the base many2one is showing placeholder text in the empty state; give it no placeholder and you get the base behavior with extra steps. Core always pairs it with a computed placeholder field.
Working examples
Fifteen lines, two overrides
Version compatibility
| Version | Status | Notes |
|---|---|---|
| Odoo 20.0 | In development | Not released. Behavior unchanged on the development branch; props-schema conversion only. |
| Odoo 19.0 | Verified | Introduced in 19.0; verified against the shipped source. |
| Odoo 18.0 | Not available | Does not exist. |
| Odoo 17.0 | Not available | Does not exist. |
| Odoo 16.0 | Not available | Does not exist. |
Upgrade note. The widget does not exist before Odoo 19. Views referencing it on 18 or earlier fall back to a missing-widget warning; backports need the widget file plus its template copied along, adjusted for the pre-19 many2one internals, which differ substantially.
What is changing in Odoo 20
Common problems and fixes
| Symptom | Cause and fix |
|---|---|
| The empty cell shows nothing at all | No placeholder is configured: the widget only renders what placeholder_field (or the placeholder attribute) provides. Point placeholder_field at a char field on the record, computed to hold the effective default. |
| The placeholder text is wrong or stale | The computed placeholder field's logic, not the widget; it prints whatever that field holds. Debug the compute (route selection rules, warehouse config); the widget is display plumbing only. |
| Users think the muted text is a saved value | Placeholder text can read like data to untrained eyes. Keep the decoration-muted pairing from core so explicit values render solid and defaults render gray, and mention it in user training. |
| Missing widget warning outside Inventory | The widget is registered by the stock module. Add stock as a dependency, or copy the fifteen-line widget and template into your own module. |
| In readonly the value is not clickable anymore | Intended: the widget forces canOpen off in readonly to avoid a link cursor on placeholder-only cells. Use the plain many2one where readonly navigation matters more than the placeholder. |
Forced Placeholder widget vs the alternatives
| Widget | Best for | Key difference |
|---|---|---|
stock.forced_placeholder | Many2one cells where an empty value means a computed default applies | Keeps the placeholder visible in the empty state and drops the link in readonly |
| many2one | The general record link | Shows nothing when empty and readonly; the base this widget wraps |
| package_m2o | Stock package fields with context-driven display names | Alters how values display, not how emptiness displays |
| many2one_uom | Unit of measure links tied to a quantity | Pairs the m2o with product and quantity fields; no placeholder logic |
Frequently asked questions
What does stock.forced_placeholder do that many2one does not?+
Why is the Route column on the replenishment screen gray?+
Can I use it on my own many2one field?+
Does it support the normal many2one options?+
Why does it only exist since Odoo 19?+
Replenishment settings your planners can trust?
Routes, orderpoints, lead times and the defaults behind every muted placeholder decide whether Odoo buys, makes or moves. We configure Inventory replenishment end to end and make the screen tell your planners the truth about what will happen next.
Tune our replenishment setup