Skip to main content
iVentureTeam

account-tax-totals-field

The untaxed, tax and total block at the bottom right of every Odoo invoice is one widget: account-tax-totals-field. It renders a JSON payload, and it quietly lets you type over a computed tax amount.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 24, 2026Updated August 24, 20266 min read
Technical nameaccount-tax-totals-field
Field typesJSON value (no supportedTypes declared; core stores it in a computed binary field)
Viewsform (invoices, bills, sales orders, purchase orders)
Moduleaccount, with template overrides shipped by sale and purchase
Used in core3 occurrences across 3 modules: account, sale, purchase
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo. Core wires it to the tax_totals field; Studio does not offer it
Alternativesmonetary, statinfo, float

What the Tax Totals widget does

Every accounting document in Odoo ends with the same block: untaxed amount, one line per tax group, total, and sometimes rounding lines. That block is not a set of separate fields. It is one widget, account-tax-totals-field, rendering one JSON value computed server-side into the tax_totals field.

The JSON carries subtotals, tax groups with their amounts, the currency and its decimal precision. The widget walks that structure and renders each tax group through a child component. On a draft document, those group amounts are clickable: the cell becomes an input, and committing a change updates the JSON and hands it back to the record, which triggers the server recomputation of the document.

Note the name: account-tax-totals-field, with hyphens. Almost every other field widget in Odoo uses underscores, so this one regularly gets mistyped in custom views, and a mistyped widget name fails silently to the default field rendering.

What this means for your team

The editable tax amount is the business feature hiding in this widget. Vendor bills routinely arrive with a cent or two of tax difference from what Odoo computes, and the legally correct amount is the one on the paper. Instead of fighting tax configuration, an accountant clicks the tax amount on the draft bill, types the vendor's figure, and the widget spreads the difference into the totals. That is the intended workflow for rounding discrepancies.

The guardrails matter too: the edit only works while the document is a draft, a zero tax amount is refused outright, and an unchanged value does nothing. Teams should still treat manual tax edits as an exception with a policy behind it, because the edit overrides what your tax configuration says and flows straight into the tax report.

Working examples

How core applies it (invoice form)

<field name="tax_totals" widget="account-tax-totals-field"
       nolabel="1" colspan="2"
       readonly="state != 'draft'"/>

The readonly expression is what disables tax editing on posted documents; the widget itself only checks the readonly prop it receives.

Reusing it on a custom model

<field name="my_tax_totals_json" widget="account-tax-totals-field"/>

This only works if two conditions hold: the model exposes a currency_id field (the source states this requirement in a comment), and the field's value reproduces the core JSON contract with subtotals, tax groups carrying tax_amount_currency, currency_id and currency_pd. In practice that means calling the same server-side helpers the core uses.

How editing a tax amount actually works

The edit cycle is more careful than it looks. Each tax group line is a TaxGroupComponent with three states: readonly, edit and disable. Clicking enters edit; committing enters disable while the change is applied, so you cannot double-submit; any re-render drops the line back to readonly.

On commit, the widget does not just store your number. It computes the delta against the old amount and adds it to four places in the JSON: the tax group, the subtotal it belongs to, the document's tax amount, and the document total. Then it calls record.update with the whole totals object, and the server recomputes the document from there. If you type something unparseable, the input resets to the old value and stays in edit mode; if you type 0 or the same value, the line simply returns to read-only, because a zero tax group is not a rounding fix but a configuration question.

One more detail from the source: after pushing an update, the widget deletes cash_rounding_base_amount_currency from its local copy of the JSON, so the rounding base is recomputed server-side rather than compounding client-side.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Development branch adds an in-widget cash rounding dropdown and migrates to the signals frontend. Re-verified after launch.
Odoo 19.0VerifiedBehavior verified against the shipped source; identical to 18.0.
Odoo 18.0VerifiedByte-identical file to 19.0.
Odoo 17.0Partial / changedWidget exists with the same name but the JSON uses tax_group_amount and tax_group_id keys.
Odoo 16.0Partial / changedSame older JSON contract as 17.0.

Upgrade note for 16 and 17. The JSON contract was renamed: 16.0 and 17.0 used tax_group_amount and identified groups by tax_group_id, while 18.0 and 19.0 use tax_amount_currency on groups identified by id, with deltas propagated into subtotals and totals. Any integration or report that parses tax_totals JSON must map keys when crossing an Odoo migration. 18.0 and 19.0 are byte-identical.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. What follows is read from the public development branch, which is unstable until feature freeze; we re-verify this page against the shipped release.

This widget is getting one of the larger upgrades we have tracked in this series. The development branch adds a cash rounding manager directly inside the totals block: a dropdown listing rounding methods from account.cash.rounding, with select, create (via a form dialog) and remove actions, wired to invoice_cash_rounding_id and saved immediately. It only appears for users in the cash rounding group, on customer documents (out_ move types) in draft.

Alongside it, a computed taxGroupWithRoundingAmount highlights the tax group carrying the biggest rounding line when has_biggest_tax_cash_rounding_line is set, and the component migrates to the new signals-based frontend (computed, signal, proxy instead of useState and lifecycle hooks). Patches against the 19.0 lifecycle hooks will not survive that migration.

Common problems and fixes

SymptomCause and fix
Widget not rendering, field shows raw JSON or nothingThe widget name is hyphenated; account_tax_totals_field with underscores silently falls back to default rendering. Use widget="account-tax-totals-field" exactly.
Tax amount not clickableThe field is readonly, which core sets whenever the document is not a draft. Reset the document to draft; the totals become editable again.
Typing 0 as the tax amount does nothingThe widget explicitly refuses a zero value and returns the cell to read-only. A genuinely zero tax belongs in tax configuration, not a manual edit.
Edited tax reverts after savingServer-side recomputation of tax_totals ran again, for example after a line changed. Make the manual adjustment last, after all lines are final.
Crash or empty block on a custom modelThe model lacks currency_id or the field's JSON does not follow the core contract. Expose currency_id and build the JSON with the core tax totals helpers.

Tax Totals widget vs the alternatives

WidgetBest forKey difference
account-tax-totals-fieldThe computed totals block on invoices, bills and ordersRenders one JSON payload and allows delta-edits to tax group amounts on drafts
monetaryShowing a single currency amountOne field, one number; no groups, no editing logic
statinfoNumbers inside smart buttonsDisplay-only formatting inside a navigation button
floatPlain numeric fieldsNo currency formatting, no JSON awareness

Nothing else in Odoo renders the totals JSON. The alternatives below are for the adjacent job of showing individual monetary amounts rather than the whole computed block.

Frequently asked questions

What is the account-tax-totals-field widget?+
It is the widget that renders the totals block (untaxed, per-tax-group amounts, total) on invoices, vendor bills, sales orders and purchase orders from the JSON stored in the tax_totals field, and lets users edit tax group amounts on draft documents.
Why can I edit the tax amount on a draft invoice?+
By design. Vendor documents often carry slightly different tax rounding than Odoo computes, and the correct amount is the one on the document. Clicking the amount opens an input; the widget applies your change as a delta to the group, subtotal and total, then the server recomputes. Zero and unchanged values are refused.
Why is the widget name hyphenated?+
Historical accident: it was registered as account-tax-totals-field and never renamed, making it one of the very few hyphen-named field widgets in Odoo. Typing it with underscores silently falls back to default rendering, which is the most common mistake with it.
Can I use account-tax-totals-field on my own model?+
Yes, if you meet its contract: the model must expose a currency_id field (stated in a source comment), and the field must contain JSON matching the core structure, which in practice means reusing Odoo's server-side tax totals computation helpers.
Did the tax_totals JSON change between versions?+
Yes. 16.0 and 17.0 used tax_group_amount and tax_group_id; 18.0 renamed to tax_amount_currency with groups identified by id, and 19.0 is identical to 18.0. Integrations parsing this JSON need key mapping when upgrading.
What is changing in Odoo 20?+
The development branch builds a cash rounding manager into the widget: a dropdown to select, create or remove an account.cash.rounding method on draft customer documents, visible to users in the cash rounding group. The component also migrates to the new signals-based frontend. Unstable until the September 2026 release.

Tax totals that will not tie out?

Rounding differences, localization tax reports, or an integration that parses tax_totals JSON and broke on upgrade: these are accounting-engine problems, not view problems. We debug and build Odoo tax and invoicing customizations across versions 16 through 19, with Odoo 20 already on our bench.

Fix my tax setup

How this page was produced

This page was written from the Odoo 19.0 source of tax_totals.js in the account module, diffed against 16.0 (key renames), 18.0 (byte-identical) and the public development branch (cash rounding manager). The editing state machine, the zero-refusal rule and the delta propagation were read directly from the component code, and the core usage XML was checked in account, sale and purchase. Corrections are welcome via our contact page.