so_line_field
The Sales Order Item column on Odoo timesheets is rendered by so_line_field, a many2one variant that quietly records whether a human picked the item. Here is what it actually does.
| Technical name | so_line_field |
|---|---|
| Field types | many2one |
| Views | list, form, kanban (timesheet views) |
| Module | sale_timesheet, installed with Sales + Timesheets |
| Used in core | 8 occurrences in sale_timesheet, on the timesheet list, form and grid views of account.analytic.line |
| Versions | Odoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0 |
| No-code setup | No. This widget is set in view XML; Studio has no equivalent |
| Alternatives | many2one, timesheet_uom |
What the SO Item field does
What this means for your team
Supported options in Odoo 19
| Option | Type | What it does |
|---|---|---|
no_open | boolean | Disables opening the selected sales order item in a dialog or breadcrumb from the field. Core sets it on the timesheet list view. |
no_create | boolean | Removes every creation path from the dropdown. Core sets it everywhere this widget appears, since order lines should come from quotations, not timesheets. |
no_quick_create | boolean | Removes only the inline Create "typed text" entry; the Create and Edit dialog remains available. |
no_create_edit | boolean | Removes only the Create and Edit dialog entry; inline quick-create remains available. |
search_threshold | number | Minimum characters typed before the dropdown searches. Without it, the search fires on focus, which is wasteful on large order line tables. |
placeholder_field | field name | A char field on the timesheet line supplying a dynamic placeholder. Core uses the static placeholder attribute ("Non-billable") instead. |
can_scan_barcode | boolean | Enables barcode scanning for the relation on mobile. Read in extractM2OFieldProps only; it appears in no options panel or documentation. |
create_name_field | field name | Which field on the related model receives the typed text on quick-create. Also read only in extractM2OFieldProps; irrelevant in core usage since creation is disabled.(default: name) |
The last two entries are undocumented. can_scan_barcode and create_name_field are read in the many2one's extractProps but declared nowhere, so they inherit into every many2one-based widget, this one included. Also note the two permission switches that are attributes, not options: can_create and can_write accept Python expressions on the field element and gate creation and editing before any option is considered.
Working examples
How the is_so_line_edited flag really works
Version compatibility
| Version | Status | Notes |
|---|---|---|
| Odoo 20.0 | In development | Not released. Internal rewrite only on the development branch; behavior unchanged. See below. |
| Odoo 19.0 | Verified | Rebuilt on buildM2OFieldDescription; adds the readonly falsy value label display. All options verified against the shipped source. |
| Odoo 18.0 | Verified | Same behavior; implemented as a many2OneField spread with an update wrapper. |
| Odoo 17.0 | Verified | Same behavior and flag logic; the file also registered a so_line_one2many companion used by the old gantt view. |
| Odoo 16.0 | Verified | Widget present with the same name; registered directly as a component class in the pre-descriptor style. |
Upgrade note. The registration name has been stable since 16.0, so view XML carries over. JavaScript patches do not: 16.0 registered the component class directly, 17.0 and 18.0 spread many2OneField, and 19.0 rebuilds it on buildM2OFieldDescription with the new Many2One component, so any patch targeting the old class structure needs rewriting after an Odoo migration.
What is changing in Odoo 20
Common problems and fixes
| Symptom | Cause and fix |
|---|---|
| Odoo keeps overwriting the sales order item a user picked | A custom view renders so_line with the plain many2one widget, so is_so_line_edited is never set and recomputation treats the line as untouched. Use widget="so_line_field" and include is_so_line_edited invisible in the view. |
| Changing the item throws an error about is_so_line_edited | The widget updates that field on the record, but the view does not load it. Add <field name="is_so_line_edited" column_invisible="True"/> next to the so_line field, as core does. |
| The Sales Order Item column is missing entirely | The line's project is not billable; core hides the field when allow_billable is false. Enable billing on the project; the column returns automatically. |
| Users create stray sales order items from timesheets | The widget was reused without no_create, so the inherited many2one creation paths are active. Add options="{'no_create': True}" as every core usage does. |
| Empty cells show "Non-billable" and users think it is a value | That is the placeholder, plus the 19.0 readonly fallback that prints a label for empty values in muted text. No fix needed; train users that muted text means no linked order item. |
SO Item field vs the alternatives
| Widget | Best for | Key difference |
|---|---|---|
so_line_field | The sales order item on billable timesheet lines | Flags manual edits via is_so_line_edited so recomputes keep them |
many2one | Any other relation without edit tracking | Identical UI, but never writes the manual-edit flag |
| timesheet_uom | The duration column on the same timesheet lines | Dispatches to the company's encoding unit widget instead of a relation |
Frequently asked questions
What does the so_line_field widget do in Odoo?+
Why does my custom timesheet view lose manual sales order item choices?+
Which options does so_line_field support?+
Can I clear the is_so_line_edited flag from the form?+
Does so_line_field work outside timesheets?+
Is the widget available in Odoo 16, 17 and 18?+
What is the Non-billable text on empty lines?+
Billing hours that never reach an invoice?
Timesheet-to-sales-order mapping is the plumbing your services revenue flows through. We tune billable project setups, protect manual assignments, and build custom timesheet views that keep the invoicing logic intact, across Odoo 16 to 19.
Book a free consultation