Skip to main content
iVentureTeam

task_with_hours

On timesheet lines, the task dropdown shows each task's remaining allocated hours while you search, then hides them once you pick. That is the task_with_hours widget from hr_timesheet.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 24, 2026Updated August 24, 20266 min read
Technical nametask_with_hours
Field typesmany2one (project.task)
Viewsform, list
Modulehr_timesheet (Timesheets app)
Used in core3 occurrences in hr_timesheet, on task_id of timesheet lines
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo. Applied by the Timesheets views; reuse needs XML
Alternativesmany2one, many2one_avatar_user, timesheet_uom

What the task picker widget does

Timesheet entry lives or dies on how fast people can find the right task. task_with_hours is the hr_timesheet module's answer: a many2one for task_id whose dropdown labels every candidate task with how much of its allocated time is left, so the person logging hours can see at a glance which bucket still has room.

The mechanism is a clean client-server split, read directly from the source. The widget injects one context key, hr_timesheet_display_remaining_hours, into the dropdown's search. Server side, project.task reacts to that key in _compute_display_name by appending a suffix after a non-breaking space: (08:00 remaining) in hour-and-minute form, a minus sign in front when the task is overdrawn, or (1.5 days remaining) when the company's timesheet encoding unit is days. The suffix only exists for tasks where timesheets are allowed and allocated hours are set above zero.

The same widget then hides its own suffix once a value is picked: the display name is cut at the first non-breaking space, so the selected chip reads just the task name. The dropdown informs, the field stays clean.

What this means for your team

The remaining-hours suffix turns timesheet entry into soft budget control. When a consultant sees (02:15 remaining) next to a task, the message lands before any report is run: this bucket is nearly spent, either wrap up or flag it. Teams that bill fixed-price projects get their early-warning system inside the field where hours are logged, which beats a variance report read two weeks too late.

The creation gate is equally deliberate. Letting people invent tasks from a timesheet line is how project structures rot, so the widget only allows creating a task when the line already carries a project, and the new task lands in that project. No project selected, no create option at all. That single rule keeps orphan tasks out of the database while still letting a project lead add a legitimate missing task mid-entry.

For the suffix to mean anything, allocated hours must be filled on tasks. Projects that skip allocated_hours get plain dropdowns, no warning, no budget signal, which is a data discipline point to bake into your project templates rather than a widget setting.

Supported options in Odoo 19

The widget declares no options of its own: the descriptor is built with buildM2OFieldDescription, so the entire many2one option set applies unchanged. The rows below are the inherited options most relevant on timesheet lines, verified in many2one_field.js on the 19.0 branch; the widget's own behavior, context injection, suffix trimming and the creation gate, has no option surface.

OptionTypeWhat it does
no_createbooleanInherited from many2one. Removes all creation routes. Note that creation is already blocked whenever the context has no default_project_id.
no_quick_createbooleanInherited from many2one. Removes the inline create entry only.
no_create_editbooleanInherited from many2one. Removes the create-and-edit dialog entry only.
no_openbooleanInherited from many2one. Prevents navigating to the task from the field in readonly.
search_thresholdnumberInherited from many2one, new in 19. Minimum typed characters before the dropdown searches, useful on databases with thousands of tasks.(since Odoo 19.0)
placeholder_fieldfield nameInherited from many2one, new in 19. Char field on the record supplying a dynamic placeholder.(since Odoo 19.0)

The creation gate wins over creation options. The component ANDs its project check into canCreate, canCreateEdit and canQuickCreate after the normal options are computed. Adding no_create tightens behavior as usual, but nothing you set in XML can enable creation while the context lacks default_project_id; the gate is code, not configuration.

Working examples

The core pattern, from timesheet line views

<field name="task_id" widget="task_with_hours"
       context="{'default_project_id': project_id}"/>

The context line is doing double duty: it presets the project on any task created from the dropdown, and its presence is what unlocks creation at all.

Read-only style task reference with hours in the dropdown

<field name="task_id" widget="task_with_hours"
       options="{'no_create': True, 'no_open': True}"/>

The model side that feeds the suffix

allocated_hours = fields.Float("Allocated Time")
remaining_hours = fields.Float(compute="_compute_remaining_hours")

Both are standard project.task fields; fill allocated time and the dropdown starts talking.

The non-breaking space contract

Two source details explain most surprises with this widget.

The suffix lives in the display name, not in the widget. The client never computes hours; it just passes the context key and trims what comes back at the first non-breaking space (\u00A0). That character is the entire contract between server and client. A custom _compute_display_name override that drops the NBSP, or replaces it with a regular space, silently breaks the trimming and the selected chip starts showing the suffix too.

Negative remaining time renders with a minus. The server formats overdrawn tasks as (-03:30 remaining), so the dropdown flags budget overruns, not just budgets running low. With day encoding it converts through the company UoM instead.

The version history is a study in Odoo's widget migrations. Odoo 16 shipped the behavior twice, once as an OWL component and once as a legacy widget for the old grid view. 17 consolidated on the component, subclassing Many2OneField, and 18 kept that file byte for byte. 19 rewrote it compositionally, wrapping the new Many2One building block and computing props instead of overriding getters, the same rewrite wave that hit every m2o-based widget. Behavior across all four: identical, which is why the version table below stays green.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Framework props migration only on the development branch; behavior identical. See below.
Odoo 19.0VerifiedCompositional rewrite on the new Many2One building block; behavior verified identical to 18.
Odoo 18.0VerifiedSame file as 17, byte for byte. No XML changes needed.
Odoo 17.0VerifiedConsolidated single component subclassing Many2OneField.
Odoo 16.0VerifiedBehavior present twice: OWL component plus a legacy widget for the old grid view.

Upgrade note. XML using this widget carries over unchanged from 16 through 19. JavaScript patches do not: 16 to 18 subclass Many2OneField with overridable getters, 19 is a composition around Many2One, so patches must be rewritten at the 19 boundary. If you patched the old grid-view widget in 16, that code has no successor at all.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026, and the notes below read the public development branch, which can still change before release.

No behavior changes are visible on master. The diff is limited to the framework props migration: useProps with the shared many2OneFieldProps instead of static props. The context key, the NBSP trim, and the project-gated creation are all untouched, and the server-side display name logic is unchanged.

Watch the base widget instead: whatever the many2one gains or loses in 20 flows straight through this descriptor, so read that page's Odoo 20 section alongside this one when planning an upgrade. We re-verify both after release.

Common problems and fixes

SymptomCause and fix
No remaining hours shown in the dropdownThe tasks have no allocated_hours set, or timesheets are disabled on them; the server only appends the suffix when both conditions hold. Set allocated time on the tasks and enable timesheets on the project.
Cannot create a task from the dropdownThe line has no project yet, so default_project_id is missing from the context and the widget disables all creation. Pick the project first; creation unlocks and the new task lands in it.
Hours show as days like (1.5 days remaining)The company's timesheet encoding unit is days, and the server formats accordingly. Expected; change the encoding unit in Timesheets settings if you want hours.
The selected task chip shows the remaining hours tooA custom display name override replaced the non-breaking space with a normal space, breaking the widget's trim. Keep the \u00A0 separator in any _compute_display_name override.
A minus sign appears in the suffixThe task's logged time exceeds its allocation; remaining hours are negative. That is the overrun warning working; rebalance the allocation or the logged time.
JavaScript patch stopped working after upgrading to 19The widget changed from a Many2OneField subclass to a composition around Many2One. Rewrite the patch against the 19 component; getter overrides no longer exist.

Task picker widget vs the alternatives

WidgetBest forKey difference
task_with_hoursTask selection on timesheet lines with hour budgetsRemaining-time suffix in the dropdown only, creation gated on the project
many2oneTask references outside timesheet contextNo hours suffix, normal creation rules
many2one_avatar_userAssignee fields next to the taskAvatar chip for users, no budget information
timesheet_uomThe duration column on the same lineFormats the hours value itself, dispatching per company encoding unit

The choice is contextual: on timesheet lines this widget is simply the right one. Elsewhere, a plain many2one avoids implying hours semantics that other forms do not have.

Frequently asked questions

What does the task_with_hours widget do?+
It is the many2one widget on timesheet task fields that shows each task's remaining allocated time in the dropdown, such as (12:30 remaining), hides that suffix once a task is picked, and only allows creating tasks when the line already has a project.
Why do some tasks show remaining hours and others do not?+
The server appends the suffix only for tasks with timesheets enabled and allocated_hours greater than zero. Tasks without an allocation list with their plain name, so a mixed dropdown is normal data, not a bug.
How is the remaining time formatted?+
As (HH:MM remaining), with a minus sign when the task is over budget. Companies that encode timesheets in days get (X days remaining) instead, converted through the timesheet UoM settings.
Why is there no Create option in my task dropdown?+
Creation is gated on the context: the widget checks for default_project_id and disables quick create and create-and-edit without it. Select the project on the line first, and creation returns, targeting that project.
Does task_with_hours support the usual many2one options?+
Yes, all of them: the descriptor is built from the many2one field, so no_open, no_create, search_threshold and the rest work unchanged. Its special behaviors are hardcoded and have no options.
Can I use task_with_hours outside the Timesheets app?+
Technically yes on any many2one to project.task, and the dropdown suffix will work since it is server-driven. Pass a default_project_id in the field context if you want creation available.
Does the widget change in Odoo 20?+
The development branch shows only the framework props migration, with identical behavior. Changes to the base many2one flow through automatically, so review that widget's Odoo 20 notes too. Final verification happens when Odoo 20 ships in late September 2026.

Timesheets that defend the budget while hours are logged

We configure task allocations, remaining-hours visibility and approval flows so project budgets surface inside daily timesheet entry, not in month-end reports. If your fixed-price projects leak hours quietly, the fix starts in this dropdown.

Make my timesheets budget-aware

How this page was produced

Verified by reading task_with_hours.js on the 16.0, 17.0, 18.0, 19.0 and development branches, including the 16-only legacy grid widget, plus the hr_timesheet override of project.task._compute_display_name for the suffix format, the NBSP separator and the days-encoding branch. The creation gate and context injection are read from the 19 component; inherited options were checked against many2one_field.js. Core usage was confirmed in the timesheet view archs. Found a mistake? Tell us.