Reference Data — COICOP, Items, Brands
Three modules together define the taxonomy every observation gets classified under, the catalogue of priced items, and the cosmetic layer (branding, emails). Nothing in HCPI works without these — every collection line, every outlet item, every index is ultimately a record hanging off something defined here.
hcpi_coicop — the classification spine
Depends on: mail
Purpose: Implements the international COICOP (Classification of Individual Consumption by Purpose) hierarchy — a 6-level tree from broad categories ("Food and non-alcoholic beverages") down to the smallest aggregation unit before individual items.
The six levels
Each level is its own model. They form a parent-child chain via FKs.
Model (_name) |
Level | Example |
|---|---|---|
hcpi.division |
1 (top) | 01 Food and non-alcoholic beverages |
hcpi.group |
2 | 01.1 Food |
hcpi.class |
3 | 01.1.1 Bread and cereals |
hcpi.sub.class |
4 | 01.1.1.1 Rice |
hcpi.micro.class |
5 | 01.1.1.1.01 Long-grain rice |
hcpi.elementary.aggregate |
6 (bottom) | 01.1.1.1.01.001 Imported long-grain rice — the parent of actual items |
Each level has:
code— the level-local code (01,1,01, …).composite_weight— the aggregation weight, used when rolling indices up.- A
nameanddescription. - A FK to its parent level.
The elementary aggregate computes a full_code by concatenating the chain: 01.01.01.01.01.001. That string is what shows up on outlet items, observations, and basket rows everywhere downstream.
All six models inherit mail.thread so structural changes (renaming, re-weighting) appear in the chatter for audit.
Security
Two groups, defined in hcpi_coicop/security/:
coicop_user— read-only on every model. The default for analysts who consume the taxonomy.coicop_manager— full CRUD. Restrict to a small team — changes here ripple through every CPI run.
Where to look to change something
| You want to… | Open |
|---|---|
| Add or rename a division | hcpi_coicop/models/hcpi_division.py + data file under hcpi_coicop/data/ if seeded |
| Change a level's weight | The level's model file (e.g. hcpi.class.py) — look at composite_weight |
Change how full_code is composed |
hcpi.elementary.aggregate — search for full_code compute |
| Restrict who can edit the taxonomy | hcpi_coicop/security/ir.model.access.csv and the group definitions next to it |
| Migrate to a new COICOP revision | New data files seeding the new codes, plus migration scripts to move existing items |
hcpi_item — the catalogue of priced things
Depends on: hcpi_coicop
Purpose: Defines the actual items whose prices get collected ("1 kg sugar", "500 ml cooking oil") and the units they're priced in. Every observation in the database eventually traces back to an hcpi.item row.
The models
hcpi.item — the catalogue entry. Key fields:
| Field | Purpose |
|---|---|
name |
Display name. |
code |
Auto-incremented within its elementary aggregate when not supplied. |
elementary_aggregate_id |
FK to the EA — that's how the item slots into the COICOP tree. |
uoo_id |
Unit of observation (hcpi.uoo). |
standard_quantity |
The quantity at which prices are typically collected (e.g. "1" for a kg). |
is_standard |
If True, the item is pre-portioned — not re-weighed at the outlet. |
full_code |
Computed: <ea_code>.<code>. The reference string everywhere. |
Inherits mail.thread for change tracking. Validates duplicate names per EA on both create() and during import — see the validation methods in hcpi.item.
hcpi.uoo — unit of observation (pieces, kg, litres, dozen, …). Tiny model; just a name and a code.
Where to look to change something
| You want to… | Open |
|---|---|
Add a new field to items (e.g. barcode) |
hcpi_item/models/hcpi_item.py + matching view in hcpi_item/views/ |
| Change the duplicate-name validation | hcpi.item — look for the _check_* methods and the @api.constrains |
| Add a new unit of measure | Create a hcpi.uoo record via the UI or seed it in hcpi_item/data/ |
| Mark items as "standard" in bulk | Use the ORM via the Odoo shell — env['hcpi.item'].search([('elementary_aggregate_id.code', '=', 'X')]).write({'is_standard': True}) |
| Change how the item code increments | hcpi.item — find the create() override that derives code from the EA |
hcpi_brand — branding, emails, login
Depends on: auth_signup, mail
Purpose: The cosmetic layer. No business logic, no models of its own — just the visual customisation HCPI ships out of the box.
What's inside
static/src/menu.js— custom menu tweaks (logo, app launcher behaviour).static/src/custom.css— colour overrides, sizing, layout tweaks for the navbar and login pages.data/email templates — signup, password reset, account activation. These aremail.templaterecords that the auth_signup flow renders.security/— rules tied to the email templates (who can edit them).
hcpi_brand vs kola_web_enterprise
Both modules customise the UI. The difference:
hcpi_brand— country-agnostic HCPI defaults: logo, login screen, transactional emails.kola_web_enterprise— replaces deeper parts of the web UI (navbar layout, SCSS variables). See Infrastructure.
When you change look-and-feel, you'll usually touch one or both.
Where to look to change something
| You want to… | Open |
|---|---|
| Change the logo | hcpi_brand/static/description/icon.png for the app tile; the login template under hcpi_brand/views/ for the login page |
| Change a transactional email | hcpi_brand/data/ — the mail.template records |
| Tweak the navbar | hcpi_brand/static/src/custom.css + the OWL/QWeb templates under the same folder |
| Replace the navbar layout entirely | kola_web_enterprise — see Infrastructure |
| Add a new "welcome" email | New <record model="mail.template"> in hcpi_brand/data/, then register it from wherever you want it sent (likely auth_signup) |
How these three connect
COICOP tree Item catalogue Branding (separate)
hcpi.division login screen
↓ emails
hcpi.group custom navbar
↓
hcpi.class hcpi.uoo (units)
↓ ↑
hcpi.sub.class hcpi.item ──────► used everywhere downstream:
↓ ↑ outlet items, observations,
hcpi.micro.class │ collection lines, indices
↓ │
hcpi.elementary.aggregate ────────┘
If you're adding a new priced-product category, your edits ripple in this order:
- Add the COICOP path down to a new elementary aggregate (
hcpi_coicop). - Add the item that lives under that EA (
hcpi_item). - Make sure outlets that should price it are linked through
hcpi.outlet.item(Outlets).
Next
- Outlets — where items get linked to collection points.
- Data Collection — where prices for those items are recorded.