Skip to main content
iVentureTeam

hr_org_chart

The organization chart on every Odoo employee form is one field widget: hr_org_chart. It ignores the field it sits on, calls its own controller, and caps the manager chain at five levels because of a one-line source comment.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 26, 2026Updated August 26, 20266 min read
Technical namehr_org_chart
Field typesany (the field's own value is ignored; core places it on a one2many)
Viewsform
Modulehr_org_chart (auto-installed with Employees)
Used in core2 occurrences: the hr.employee form and the hr.employee.public form
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo. The chart ships preinstalled on employee forms; Studio has no org chart component
Alternativesmany2one_avatar_employee, many2many_avatar_employee, hr_employee_chat

What the org chart widget does

Open any employee in Odoo's HR app and the form ends with an Organization Chart: the chain of managers above the person, the person, and their direct reports, each row showing an avatar, a name, a job title and a count of direct and total subordinates. All of that is a single field widget named hr_org_chart.

It is an unusual field widget. It declares no options and no supported field types, and it never reads the value of the field it is attached to. Core mounts it on the child_ids one2many with readonly="1" and nolabel="1", but the field is only an anchor point in the layout. The component watches the record itself: it takes the record id and the parent_id field, calls its own HTTP route /hr/get_org_chart, and renders whatever the controller returns.

What this means for your team

The org chart answers the questions HR and managers ask most while looking at a person: who does this employee report to, who reports to them, and how big is their team really. The direct and indirect counts are clickable, so a department head can jump from any employee to a kanban of that person's whole reporting line in one click, without building a filter.

Because the chart re-renders live as parent_id changes, it is also the fastest sanity check during reorganizations: change the manager field, and the chain above the employee redraws before you save. Companies that keep parent_id clean get an always-current org chart for free on every employee record, which is usually the moment teams stop maintaining a separate spreadsheet org chart.

Working examples

How core mounts the chart

<field name="child_ids" widget="hr_org_chart"
       readonly="1" nolabel="1"
       class="position-relative w-100"/>

From hr_org_chart/views/hr_views.xml. The field could be almost any field of the model; the widget only uses the record's id and parent_id.

Adding the chart to a custom employee-like model

<!-- Only works for models the /hr/get_org_chart controller understands -->
<field name="parent_id" invisible="1"/>
<field name="child_ids" widget="hr_org_chart" readonly="1" nolabel="1"/>

The controller resolves employees through hr.employee, so on anything that is not an employee model you must extend /hr/get_org_chart server side first.

The res.users regression and the refresh contract

Two behaviors in the 19.0 source are worth knowing before you customize anything.

The res.users regression. In Odoo 17 and 18 the component carried an explicit fallback, documented in a source comment: on a res.users form it read employee_id and employee_parent_id from the record; on employee forms it read resId and parent_id. Odoo 19 removed that fallback and now reads only record.resId and record.data.parent_id. Place the widget on a user form in 19 and it treats the user id as an employee id, which silently renders the wrong chart or none at all. If you had an 18 customization showing the chart on user preferences, that is the line that broke it.

The refresh contract. A useRecordObserver watches the record and refetches when either the employee id or the parent changes, resetting the level cap each time. Clicking the more managers link sets the cap to 100 and forces a refetch, so the full chain is always one click away regardless of depth. Every avatar opens a popover component, and both the popover and the rows redirect through hr.employee.get_formview_action, so access rules keep applying to navigation.

Version compatibility

VersionStatusNotes
Odoo 20.0VerifiedNot released. The module is deleted and the widget moves into hr under the same name; see below.
Odoo 19.0VerifiedVerified against the shipped source. The res.users fallback from 17 and 18 is removed.
Odoo 18.0VerifiedSame widget, plus an explicit fallback for res.users forms via employee_id and employee_parent_id.
Odoo 17.0VerifiedFirst modern Owl version with useRecordObserver; res.users fallback present.
Odoo 16.0VerifiedOlder implementation extending the Field class with a custom popover helper; same chart on the page.

Upgrade note. Migrating to 19: any custom view that relied on the widget resolving employees on res.users forms must be reworked, because the fallback fields are no longer read. Migrating to 20: update JavaScript patch paths from hr_org_chart to hr and template names from hr_org_chart.* to hr.*.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. We read the widget on the public development branch at the time of writing; the branch is unstable and this page will be re-verified after release.

The headline change is structural: the whole hr_org_chart module is deleted and the widget, its controller and templates move into the hr module under the same widget name. Templates rename from hr_org_chart.* to hr.*, so template inheritance breaks even though views keep working. Behavior also moves: chart data lands in reactive state guarded by a fetch counter against out-of-order responses, the RPC gains a disk cache with a stale-while-revalidate callback, the chart now also refetches when job_title is edited, and record navigation switches from get_formview_action to a new get_record_default_action method.

Common problems and fixes

SymptomCause and fix
The chart area is empty on a custom formThe record has no parent_id loaded or the model is not an employee model the controller understands. Load parent_id in the view (invisible is fine) and keep the widget on hr.employee or hr.employee.public.
Chart worked on the user form in 18 and broke after upgrading to 19Odoo 19 removed the res.users fallback that read employee_id and employee_parent_id. Move the chart to the employee form, or patch the component to re-add the mapping.
Long manager chains show only part of the hierarchyThe controller caps the chain at 5 manager levels by default. Click the more managers link, which refetches with a cap of 100, or override _managers_level server side.
Subordinate counts look wrongCounts come from the controller's subordinate computation, not from child_ids in the view. Check parent_id data quality; archived employees and multi-company rules affect the counts.
Clicking a person does nothing for some usersNavigation goes through hr.employee.get_formview_action, so record rules apply. Grant the user read access to the target employees or accept the restriction.

Org chart widget vs the alternatives

WidgetBest forKey difference
hr_org_chartWalking the reporting hierarchy from any employee recordFetches its own data from /hr/get_org_chart; ignores the field it is mounted on
many2one_avatar_employeeShowing or editing the manager as a single fieldA plain relational field with an avatar chip; no hierarchy walking
many2many_avatar_employeeA flat set of employees on a recordRenders avatars for a many2many; no reporting-line semantics
hr_employee_chatJumping into a Discuss chat with the employeeA one-icon view widget; complements the chart rather than replacing it

The org chart is a navigation surface, not a data widget. If you only need to show or edit who the manager is, the plain relational widgets on parent_id are lighter; the chart earns its place when users need to walk the hierarchy.

Frequently asked questions

Why does the org chart stop after five managers?+
The controller caps the manager chain with _managers_level = 5, a hardcoded class attribute whose source comment reads # FP request. The chart then shows a more link that refetches with a cap of 100 levels.
Which field should hr_org_chart be placed on?+
It does not matter functionally; the widget never reads the field's value. Core uses the child_ids one2many with readonly="1" nolabel="1" purely as a mount point. What matters is that the record is an employee and parent_id is in the view.
Can I show the org chart on the res.users form?+
Not in Odoo 19 without code. Versions 17 and 18 read employee_id and employee_parent_id as a fallback on user forms; 19 removed that and reads only the record id and parent_id, so a user form resolves the wrong record.
How do the Direct and Indirect subordinate links work?+
Each count carries a data attribute read by the onEmployeeSubRedirect hook, which calls /hr/get_subordinates with the type and opens a Team kanban filtered to the returned ids, with default_parent_id preset.
Does the chart update when I change the manager before saving?+
Yes. A record observer watches parent_id and the record id and refetches the chart on every change, so the hierarchy redraws live while the form is still dirty.
Is there a full-page org chart for the whole company?+
The widget itself is per-employee. The 19 form adds an action link above it to a dedicated org chart view for wider exploration, and list or kanban groupings by manager remain the standard company-wide angles.

Does your Odoo org chart match how the company actually runs?

Reporting lines drive approvals, time off delegation and team dashboards across Odoo HR. We clean up parent_id data, extend the org chart controller for deeper or custom hierarchies, and keep it all working through version upgrades, including the module shuffle coming in Odoo 20.

Talk to an Odoo HR expert

How this page was produced

Behavior was read from hr_org_chart.js, hooks.js and controllers/hr_org_chart.py on the Odoo 19.0 branch, compared against the same files on 16.0, 17.0 and 18.0, and against the development branch where the module is folded into hr. Core usage was verified in the 19.0 view XML. Spotted an error? Tell us and we will correct the page.