Skip to main content
iVentureTeam

nb_attachment

A number and a paperclip: how many receipts are attached to an expense. Fifteen lines of code, and one detail worth knowing about when the number updates.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated September 8, 20265 min read
Technical namenb_attachment
Field typesinteger
Viewsform, list
Modulehr_expense, the Expenses app
Used in core1 occurrence, the expense form in hr_expense
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0
No-code setupNo. It only works on a field with one specific name
Alternativesstatinfo, integer, many2many_binary, badge

What the attachment counter does

An expense without a receipt is a problem waiting for an approver. Odoo counts the attachments on an expense in a computed field, and this widget is what puts that count on the form in a form people read at a glance: a number beside a paperclip rather than a labelled integer.

It is one of the smallest widgets in Odoo core. A component, a template, and a single line in setup that copies the count off the record.

That single line carries a quirk worth knowing about. It reads the field by name literally, rather than through the name the field element supplies, which means the widget only works on a field actually called by that name. On any other field it renders nothing.

What this means for your team

Receipt discipline is the whole battle in expense management. An expense with no attachment either gets rejected, which costs the employee a resubmission, or gets approved without evidence, which costs the company at audit. Making the count visible on the expense itself, rather than requiring someone to open the attachments panel, moves the check earlier.

The wider pattern is worth noting for any approval workflow: put the evidence count where the approval decision is made. It is a one line view change and it removes a category of back and forth entirely.

Working examples

The core usage

<field name="nb_attachment" widget="nb_attachment"
       nolabel="1" readonly="True"/>

The field name and the widget name are the same, which is not a coincidence: the widget only works on that name.

What does not work

<!-- renders an empty counter -->
<field name="receipt_count" widget="nb_attachment"/>

The widget reads its own literal property name from the record, so a differently named field produces nothing.

A generic alternative

<button class="oe_stat_button" icon="fa-paperclip">
  <field name="receipt_count" widget="statinfo"/>
</button>

For any other model, the stat info widget gives the same idea without the name constraint.

Two lines, two quirks

There is very little code and two things worth recording about it.

The first is the literal field read. The component copies the count onto itself in setup using a fixed property name, ignoring the field name it was passed. That makes the widget non-portable in a way nothing announces: on a differently named field it renders without error and without a number.

The second is that the read happens in setup rather than in a getter, so the value is captured when the component is created. Attaching a receipt through the chatter updates the record's field, but the counter keeps its original value until the component is recreated. In practice the expense form reloads after an attachment operation often enough that this rarely shows, but on a custom form that avoids reloading, it will.

The registration is equally minimal: a component and nothing else. No supported types means the widget will not object to being placed on any field type, and no extractor means anything in the options dictionary is ignored.

Between Odoo 17 and Odoo 19 the only change is the removal of the module marker comment that Odoo 19 dropped across the codebase. The behavior has not moved.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Byte-identical to Odoo 19 on the development branch.
Odoo 19.0VerifiedVerified against the shipped source. Unchanged in substance since Odoo 17.
Odoo 18.0VerifiedIdentical behavior.
Odoo 17.0VerifiedFirst version.
Odoo 16.0Not availableWidget does not exist.

Upgrade note. Nothing to do. The widget arrived in Odoo 17 and is unchanged in substance through Odoo 19. Databases coming from Odoo 16 gain it with the standard expense views.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The development branch is unstable and this may still change; we re-verify the page after release.

Byte-identical. The file on the development branch matches Odoo 19 exactly, including the literal field read and the capture in setup. Nothing about this widget changes in Odoo 20.

Common problems and fixes

SymptomCause and fix
The counter is emptyThe field is not named the way the widget expects; it reads a literal property from the record. Use it only on the expense count field, or use a stat button with the stat info widget instead.
The count does not update after attaching a receiptThe value is captured when the component is created. Reload the form; the record's field is correct, only the rendered snapshot is stale.
Options are ignoredThe registration has no extractor. Nothing to configure here.
The widget renders on a non-integer fieldNo supported types are declared, so nothing objects. Point it at the intended integer field.
Missing widget errorThe expenses module is not installed in that database. Install Expenses, or use a stat button.

Attachment counter vs the alternatives

WidgetBest forKey difference
nb_attachmentShowing how many receipts an expense carries, on the expense formA minimal counter that reads one specific field name literally and captures it once
statinfoA count on any model, inside a stat buttonWorks on any field name and is used throughout core
integerShowing the number plainlyA labelled input rather than a counter
many2many_binaryListing the attachments themselvesShows the files rather than how many there are
badgeA value shown as a pillGeneric badge styling with no attachment meaning

For a count on any other model, the stat info widget inside a stat button gives the same information with none of the name constraints, and it is used everywhere in core. This widget is worth naming only on the expense field it was written for.

Frequently asked questions

Can I use nb_attachment on another field?+
No. The widget reads a literal property name from the record rather than the field name it was given, so on any other field it renders an empty counter with no error.
Why does the count not refresh?+
It is read once when the component is created. The record's field is updated correctly; only the rendered snapshot is stale until the form reloads.
Does it support any options?+
None. The registration provides only a component, with no options, no extractor and no supported types.
What should I use on another model?+
The stat info widget inside a stat button. It gives the same idea, works on any field name, and is the pattern used throughout Odoo.
Which versions have it?+
Odoo 17 onwards, unchanged in substance, and byte-identical on the Odoo 20 development branch.

Expense claims that get approved the first time

Receipt capture, approval rules and the accounting behind them decide whether expenses are a monthly argument or a background process. We implement Odoo Expenses end to end on versions 16 through 19.

Book a free consultation

How this page was produced

The literal field read, the capture in setup and the minimal registration were read from nb_attachment.js on the Odoo 19.0 branch, with the usage taken from hr_expense/views/hr_expense_views.xml. Version coverage comes from comparing the file across the 17.0 and 18.0 branches and a byte comparison against the public development branch. Spotted an error? Tell us and we will correct the page.