Skip to main content
iVentureTeam

grouped_view_widget

The grouped_view_widget renders a read-only, grouped table from a JSON string, and it is how the Accrued Orders wizard shows you the entries it is about to book before you commit.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 25, 2026Updated August 25, 20266 min read
Technical namegrouped_view_widget
Field typestext (JSON string)
Viewsform (wizard)
Moduleaccount, installed with Invoicing or Accounting
Used in core2 references in 1 module; the single view usage is preview_data on the Accrued Orders wizard
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
Alternativesone2many, account-tax-totals-field, actionable_errors

What the grouped view widget does

Some wizards need to show a table that does not exist yet. The Accrued Orders wizard is the canonical case: it computes the accrual entries it would create for the selected purchase or sale orders, and shows them for review before anything touches the ledger. Those lines are not records, so an embedded one2many is the wrong tool. Odoo's answer is to serialize the preview server side and render it client side, and grouped_view_widget is the renderer.

The widget reads the field's value, a plain string, runs JSON.parse on it, and renders a table styled like a grouped Odoo list: a header row built from options.columns, one shaded full-width row per group carrying group_name, then the item rows, each cell pulled from the vals dict by the column's field key with the column's CSS class applied. When the value is empty it renders nothing at all, and when the server capped the preview it prints a plain sentence: N are not shown in the preview.

There is no interaction anywhere: no sorting, no clicking, no editing. It is a picture of a table, deliberately.

What this means for your team

The pattern this widget embodies is the interesting part: preview before commit. Accruals are exactly the kind of entry that is annoying to reverse, so the wizard shows the complete picture, grouped and totaled, while it is still free to cancel. If your team books accrued revenue or expenses at close, this screen is why the Odoo flow is safer than posting first and checking after.

For builders, the widget is a free, core-maintained way to put any computed table inside a wizard without creating transient records or a custom component. Approval previews, import dry-runs, payroll simulations, price recalculation summaries: compute a dict, json.dumps it into a Text field, point this widget at it. We reach for it in custom wizard work whenever a client asks to see what will happen before it happens, because the server stays the single source of truth and the client stays dumb.

The one discipline it imposes is honest truncation. The server decides how many rows the preview carries and reports the remainder through discarded_number, which keeps giant previews from freezing the browser while never hiding that lines were cut.

Working examples

How core uses it (Accrued Orders wizard)

<field name="preview_data" widget="grouped_view_widget" class="w-100"/>

The JSON contract, minimal and complete

{
    "groups_vals": [{
        "group_name": "August 2026",
        "items_vals": [
            [0, 0, {"account": "Accrued Expenses", "amount": "1,250.00"}]
        ]
    }],
    "options": {
        "columns": [
            {"label": "Account", "field": "account", "class": ""},
            {"label": "Amount", "field": "amount", "class": "text-end"}
        ],
        "discarded_number": ""
    }
}

Note the row shape: each entry of items_vals is a three-element list and the widget reads index 2. Core emits o2m-command-style [0, 0, vals] triples so the same structure can seed real records later; a bare vals dict will not render.

Feeding it from a custom wizard

preview_data = fields.Text(compute="_compute_preview_data")

# in the compute:
record.preview_data = json.dumps({
    "groups_vals": groups,
    "options": {"columns": cols, "discarded_number": ""}
})

Format everything server side: amounts, dates and currency symbols arrive as display strings, because the widget applies no formatters of its own.

Parsing, a hardcoded color, and a fossil template

Values are strings, not JSON fields. The core field is fields.Text, and the widget's getValue does JSON.parse on every render, with an empty-structure fallback when the field is falsy but no error handling for malformed JSON. A truncated or hand-edited value throws in the template, so always emit via json.dumps, never string concatenation.

The group row styling is hardcoded. The shaded group header uses an inline background-color: #dee2e6 in the template rather than a CSS class, one of the few inline styles left in core widget templates. Restyling means overriding the template, not writing CSS.

A dead template ships in the same file. grouped_view_widget.xml still contains account.OpenMoveTemplate, an anchor bound to widget.value, the pre-16 legacy widget API. No component references it and the expression could not evaluate under the modern engine; it has simply survived every cleanup since, including on the current development branch.

History in one line. Odoo 16 cached the parsed value with onWillUpdateProps and registered the bare class; 17 moved to the parse-on-render getter and descriptor registration; 18, 19 and the development branch are byte-identical apart from a removed pragma and cosmetic template rewrites.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Byte-identical JavaScript on the development branch; cosmetic template rewrites only. Re-verified after launch.
Odoo 19.0VerifiedVerified against the shipped source; identical to 18.
Odoo 18.0VerifiedIdentical file apart from the removed odoo-module pragma.
Odoo 17.0VerifiedSame behavior; the parsed-value caching from 16 was replaced by the parse-on-render getter.
Odoo 16.0VerifiedOriginal version with onWillUpdateProps caching and class-style registration; same JSON contract.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026, and as always we read the public development branch rather than speculate, with the caveat that the branch keeps moving until feature freeze and this page is re-verified after release.

For this widget the development branch shows no functional change whatsoever. The JavaScript is identical to 19, the template's only diff is the framework-wide rewrite to explicit this. expressions, the Accrued Orders usage is unchanged, and even the fossil account.OpenMoveTemplate is still there. Custom wizards feeding this widget should migrate without any attention.

Common problems and fixes

SymptomCause and fix
The widget renders nothingThe field value is empty or groups_vals is an empty list; the table only renders when at least one group exists. Check the compute actually assigns json.dumps output for the current wizard state.
JavaScript error mentioning JSON.parseThe field contains malformed JSON; the widget has no error handling. Always emit with json.dumps and never truncate the string in SQL or XML.
Rows are missing but no error showsRows must be [0, 0, vals] triples; bare dicts are skipped because the template reads item_vals[2]. Wrap each row dict in the o2m-command triple shape core uses.
Cells are empty for one columnThe column's field key does not match a key present in the row vals. Align options.columns[].field with the keys emitted per row.
Numbers show unformatted or in the wrong localeThe widget applies no formatters; whatever string the server emitted is displayed. Format amounts and dates in the compute with the user's locale before dumping.
A sentence says some lines are not shownThe server capped the preview and set discarded_number. Expected; the full set is booked on confirmation even though the preview is truncated.

Grouped view widget vs the alternatives

WidgetBest forKey difference
grouped_view_widgetRead-only computed previews inside wizardsRenders a JSON string as a grouped list-styled table with zero interaction
one2manyTables of real (or transient) recordsFull record grid with editing, requires the lines to exist as records
account-tax-totals-fieldThe tax totals block on invoicesAlso JSON-driven, but purpose-built for tax summaries and editable rounding
actionable_errorsValidation summaries with action buttonsJSON-driven list of messages with severity sorting and clickable fixes

The rule of thumb: records get a one2many, computed previews get this widget, and single computed numbers belong in plain fields. Reaching for a custom Owl component is only justified once the preview needs interaction.

Frequently asked questions

Where does Odoo actually use grouped_view_widget?+
One place: the preview_data field of the Accrued Orders wizard in the account module, which previews the accrual entries for selected purchase or sale orders before they are created.
What field type does grouped_view_widget need?+
A text-like field containing a JSON string; core uses a computed fields.Text filled with json.dumps. The widget parses it client side on every render, so the server must emit valid JSON or the template throws.
Why are my rows wrapped in [0, 0, {...}]?+
The template reads each row as item_vals[2], the vals slot of an o2m-command triple. Core chose that shape so the same structure could double as command lists for record creation. A bare dict is silently skipped.
Can users click or edit anything in the table?+
No. The widget renders static markup only: no sorting, no row clicks, no inputs. If the preview must become interactive, you have outgrown it and need a one2many on transient records or a custom component.
What does the 'are not shown in the preview' line mean?+
The server truncated the preview and reported the hidden count via options.discarded_number. It affects the preview only; confirming the wizard still books every line.
Has grouped_view_widget changed between Odoo 16 and 20?+
Barely. 16 cached the parsed JSON, 17 switched to parsing on render, and 18 through the Odoo 20 development branch are byte-identical apart from cosmetic template rewrites. Custom code built on it migrates untouched; we re-verify after the September 2026 release.

Want a see-before-you-post preview in your own wizard?

Dry-run tables are the cheapest trust feature you can add to any batch process: accruals, imports, mass updates, payroll runs. We build preview-first wizards on this exact core pattern for Odoo 16 through 19.

Build my preview wizard

How this page was produced

Verified by reading grouped_view_widget.js and grouped_view_widget.xml in the Odoo 19.0 account module, the preview_data compute in accrued_orders.py confirming the Text field and json.dumps contract, and the wizard view usage. Version rows come from diffing the component across 16.0 through the public development branch. The preview flow was exercised on a clean Odoo 19 database, where the screenshot was captured. Found something off? Tell us.