1. Purpose
A Categorization is a user-defined, flexible tagging system for reporting and slicing. Categorizations are purely optional (unlike Codes, which are required for integration) and never affect system behaviour — they are strictly reporting-only. Items, Resources, Tenders, and Estimates can be tagged with Categorization Options, enabling cross-cutting analysis (e.g., “total cost by Trade”, “all Electrical work across items and resources”).
Analogy (glossary cooking model): Categorizations are labels on ingredients and finished dishes — you can organize and report on them, but labeling doesn’t change how the cooking works.
Contrast with Codes (BR-125):
- Categorization = optional, reporting-only, never affects behaviour
- Code = required, integration-linked, blocks downstream systems if missing
2. Attributes
Categorization (Container)
| Attribute | Type | Required | Default | Notes |
|---|---|---|---|---|
id | UUID | ✅ | generated | System-managed |
name | string (short text) | ✅ | — | The Categorization name, e.g., “Trade”, “Project Phase”, “Region”. Shown in UI and reports |
scope | multi-select enum | ✅ | — | Entity types this Categorization applies to: any combo of Item / Resource / Tender / Estimate. See §3 |
description | string (long text) | ❌ | null | Admin notes on the Categorization’s purpose and usage |
created_at / updated_at | timestamp | ✅ | system | Audit |
created_by / updated_by | User ref | ✅ | system | Audit |
Categorization Option (Value)
| Attribute | Type | Required | Default | Notes |
|---|---|---|---|---|
id | UUID | ✅ | generated | System-managed |
categorization_id | UUID ref | ✅ | — | Parent Categorization. Every Option belongs to exactly one Categorization |
value | string (text) | ✅ | — | The internal code/identifier, e.g., “concrete”, “electrical”, “auckland”. Used in data exports and APIs |
display_label | string (text) | ✅ | — | The human-readable label shown in UI, e.g., “Concrete Works”, “Electrical”, “Auckland”. Can differ from value for readability |
colour | predefined palette OR hex code | ❌ | null | Optional colour: either a predefined palette entry (e.g., “primary-blue”, “accent-orange”) OR a custom hex code (e.g., #FF5733). Used in visualizations and reports |
archived_at | timestamp | ❌ | null | Soft-delete marker. If populated, this Option is archived; visible on historical entities but unavailable for new selections. Can be un-archived (restored). Per BR-120a (no hard-delete) |
display_order | integer | ✅ | auto | Order within parent Categorization |
created_at / updated_at | timestamp | ✅ | system | Audit |
created_by / updated_by | User ref | ✅ | system | Audit |
3. Scope
A Categorization’s scope is a multi-select flag that defines which entity types it applies to. A single Categorization can apply to multiple entity types.
| Scope Option | Meaning | Notes |
|---|---|---|
| Item | Options can be assigned to Items in an Estimate | Enables filtering/reporting on Item categories across the build-up |
| Resource | Options can be assigned to Resources in Price Books | Enables filtering/reporting on Resource categories (e.g., “Concrete Resources in this Tender”) |
| Tender | Options can be assigned to Tenders | Enables portfolio-level categorization (e.g., “Infrastructure projects”) |
| Estimate | Options can be assigned to Estimates | Enables categorization at the estimate level (e.g., “base case” vs “value engineered”) |
Flexibility: a single Categorization can have scope = [Item, Resource]. This allows the same “Trade” (Concrete, Steel, Electrical) to tag both Items and Resources, enabling unified reporting across the cost model (e.g., “total steel cost across all items and resources”).
4. Relationships
Inbound (things referring to Categorization)
| From | Cardinality | Notes |
|---|---|---|
| Categorization Option | CatOption M:1 Categorization | Every Option belongs to exactly one Categorization (parent); a Categorization has many Options |
| Item | Item M:M CatOption | If Categorization scope includes Item. Sub-Items inherit parent’s Categorization Options at creation; estimator can override (see §4 Inheritance) |
| Resource | Resource M:M CatOption | If Categorization scope includes Resource |
| Tender | Tender M:M CatOption | If Categorization scope includes Tender |
| Estimate | Estimate M:M CatOption | If Categorization scope includes Estimate |
| Rule | Rule M:M CatOption | For Commercials Rule scoping (BR-073): a Rule can target “all costs tagged with Categorization Option X” |
| Reference Rate | RefRate M:0..1 CatOption | Optional scope: a Reference Rate can be scoped to a specific Categorization Option (BR-145) |
Outbound (things Categorization references)
| To | Cardinality | Required | Notes |
|---|---|---|---|
| Categorization Option | Categorization 1:M CatOption | ✅ | Every Categorization has at least one Option |
Inheritance (sub-Items)
When a sub-Item is created under a parent Item, the sub-Item inherits the parent’s Categorization Options at creation time:
- Sub-Item is automatically tagged with the same Categorization Options as its parent.
- Estimator can override: the Estimator can modify, remove, or add Categorization Options on the sub-Item immediately after creation.
- No retroactive propagation: Changes to the parent’s Categorization Options after the sub-Item is created do not retroactively update the sub-Item’s tags. Inheritance is one-time, at creation only.
- This enables consistency across work breakdowns while preserving local control at the sub-Item level.
5. Validation / Invariants
Rules that must hold at all times:
- Categorization scope non-empty.
scopemust contain at least one entity type (Item / Resource / Tender / Estimate). Cannot be empty. - Option belongs to exactly one Categorization. Every Categorization Option has a parent Categorization; no orphaned Options.
- Unique value within Categorization. Within a single Categorization,
valuemust be unique (case-sensitive). Different Categorizations can have Options with the samevalue. - Colour format (if present). If
colouris populated, it must be either a predefined palette entry (e.g., “primary-blue”) OR a valid hex colour code (e.g.,#FF5733,#000). Freeform colour names (e.g., “red”, “blue”) are not permitted. - At least one Option per Categorization. A Categorization must have at least one Option to be usable. Deleting the last Option is prevented (or Categorization is soft-deleted).
6. Bulk tagging
Supported: Users can select multiple Items or Resources and apply the same Categorization Option in one action. This is a UX feature (not a data model rule) that improves efficiency for large estimates:
- Estimator selects a list of Items or Resources (checkbox multi-select).
- Estimator chooses a Categorization Option from a dropdown.
- System applies that single Categorization Option tag to all selected entities in one batch operation.
- Each entity’s existing Categorization Options are preserved; the new Option is added.
- Example: select 15 Items that are all “Electrical work” and tag them with the “Electrical” Trade option in one click.
7. Usage patterns
Pattern A — Trade Categorization (Items + Resources)
A single Categorization applies to both Items and Resources, enabling unified reporting across the cost build-up.
Categorization: "Trade"
scope: [Item, Resource]
Options:
- value: "concrete" / display_label: "Concrete Works" / colour: #4A7BA7
- value: "steel" / display_label: "Structural Steel" / colour: #C0504D
- value: "electrical" / display_label: "Electrical Systems" / colour: #FFC000
- value: "mechanical" / display_label: "Mechanical Systems" / colour: #70AD47
- value: "finishes" / display_label: "Finishes" / colour: #92278F
Usage:
- Item "Supply and place concrete to piers" → tagged with "Concrete Works" Option
- Item "Install electrical cabling" → tagged with "Electrical Systems" Option
- Resource "Ready-mix concrete (35MPa)" → tagged with "Concrete Works" Option
- Resource "Electrician — general (Auckland)" → tagged with "Electrical Systems" Option
Report: "Total Concrete cost (Items + Resources)" = $280,000
Report: "Cost by Trade across all Items and Resources" = breakdown across Concrete, Steel, etc.
Pattern B — Project Phase Categorization (Items only)
A Categorization scoped to Items only, used to drive time-based cost distribution linked to a Tender Program.
Categorization: "Project Phase"
scope: [Item]
Options:
- value: "mobilisation" / display_label: "Mobilisation" / colour: #FFEB9C
- value: "earthworks" / display_label: "Earthworks" / colour: #A9D08E
- value: "structures" / display_label: "Structures" / colour: #B4C7E7
- value: "finishes" / display_label: "Finishes" / colour: #F8CBAD
Usage:
- Item "Site setup and accommodation" → tagged with "Mobilisation"
- Item "Excavation and pile driving" → tagged with "Earthworks"
- Item "Concrete pour — structural frame" → tagged with "Structures"
- Item "Painting and cladding" → tagged with "Finishes"
Commercials Rule: "Apply schedule of values by phase" → targets Project Phase Options to drive time-based cost distribution linked to Program Tasks.
Pattern C — Regional Categorization (Resources only)
A Categorization scoped to Resources, enabling regional rate analysis.
Categorization: "Region"
scope: [Resource]
Options:
- value: "auckland" / display_label: "Auckland" / colour: #0099FF
- value: "wellington" / display_label: "Wellington" / colour: #FF6600
- value: "christchurch" / display_label: "Christchurch" / colour: #00CC00
Usage:
- Resource "Carpenter day rate (Auckland region)" → tagged with "Auckland" Option
- Resource "Concrete supply (Wellington depot)" → tagged with "Wellington" Option
- Resource "Scaffolding (Christchurch hires)" → tagged with "Christchurch" Option
Report: "Labour rates by region" = identifies regional rate variations across the Estimate.
Reference Rate: "Concrete works — Auckland" can be scoped to the "Auckland" Option.
7. Lifecycle and permissions
Admin & Lead Estimator
- Admin (per BR-085) can create, edit, rename, and archive Categorizations and their Options. Hard-delete is not supported (BR-120a); archiving is the only removal path. Categorizations are system-wide; admin is the gate-keeper to prevent proliferation of conflicting taxonomies.
- Lead Estimator can assign Categorization Options to Items and Resources within their Estimate (read: no special restriction; same as any Estimator under BR-095 per-Item locking).
- Estimator can assign Categorization Options to Items they are editing; cannot edit Categorization definitions.
Soft-delete (Archive) lifecycle
Categorizations and Options support soft-delete (archive) rather than hard delete:
- When a Categorization or Option is archived, it is marked with
archived_attimestamp. - Archived items remain visible on all historical Tenders, Items, and Resources that used them, preserving audit and reporting integrity.
- Archived items are unavailable for new selections (UI/API filters exclude archived Options from dropdowns and selectors).
- Un-archive allowed: Archived Categorizations and Options can be restored by clearing the
archived_attimestamp, making them available for new selections again. - No hard delete is supported; deletion is always soft (archive).
8. Worked Examples
Example A — Trade across Items and Resources with bulk tagging
Setup (Admin):
Categorization "Trade":
scope: [Item, Resource]
Options:
1. "concrete" → Concrete Works
2. "steel" → Structural Steel
3. "electrical" → Electrical
4. "mechanical" → Mechanical
Price Book "Materials Q2 2026":
- Resource "Ready-mix concrete 35MPa" → tagged "concrete"
- Resource "Reinforcement steel 500MPa" → tagged "steel"
- Resource "PVC conduit DN20" → tagged "electrical"
Estimate "Bridge rebuild":
- Item "Concrete pier caps (12×1.5m)" → tagged "concrete"
- Item "Structural steel frame" → tagged "steel"
- Item "Electrical distribution board & cabling" → tagged "electrical"
Reporting:
- “Total Concrete cost (Items + Resources)” = Item cost + Material Resource cost
- “Cost breakdown by Trade” = pie chart of Concrete / Steel / Electrical / Mechanical
- “Anomaly Review: Reference Rate matching” = Concrete items checked against “Concrete Reference Rate” (optionally scoped to “Concrete” Option)
Example B — Project Phase for Schedule of Values
Setup (Admin):
Categorization "Project Phase":
scope: [Item]
Options:
1. "mobilisation" → Mobilisation
2. "earthworks" → Earthworks
3. "structures" → Structures
4. "finishes" → Finishes
Estimate "Infrastructure project":
- Item "Site accommodation + safety setup" → "mobilisation"
- Item "Excavation (19,000m³)" → "earthworks"
- Item "Concrete slab (2,500m²)" → "structures"
- Item "Painting + cladding" → "finishes"
Tender Program linked to Estimate:
- Task "Mobilisation" (weeks 1–2)
- Task "Earthworks" (weeks 3–8)
- Task "Concrete pour" (weeks 9–14)
- Task "Finishes" (weeks 15–20)
Commercials Rule "Schedule of Values by Phase":
- Scope: Categorization Option "mobilisation" → 5% cumulative
- Scope: Categorization Option "earthworks" → 25% cumulative
- Scope: Categorization Option "structures" → 75% cumulative
- Scope: Categorization Option "finishes" → 100% cumulative
Effect:
- Client schedule of values driven by phase categories, linked to program timeline.
- Cost is not affected by Categorization (BR-120) — the Rule uses Categorization as a scoping target, not a cost modifier.
Example D — Multi-Categorization on one entity
Setup:
Categorization "Trade": [concrete, steel, electrical]
Categorization "Supplier Tier": [preferred, alternative, budget]
Categorization "Risk Level": [low, medium, high]
Resource "Subcontractor — ABC Concrete (preferred, low-risk)":
- Trade: "concrete"
- Supplier Tier: "preferred"
- Risk Level: "low"
Reporting:
- “Preferred suppliers’ cost” = filter by Supplier Tier = “preferred”
- “Concrete cost from preferred suppliers” = filter by Trade = “concrete” AND Supplier Tier = “preferred”
- “High-risk costs” = filter by Risk Level = “high” (for risk review)