Skip to content

ADR-0005. UoM storage — uom jsonb on catalog + uomId soft ref on lines

FieldValue
StatusAccepted
Date2026-04-05
Decidersinventory-team, PM
Supersedes

Context

  • Material has multiple UoM "roles": base (storage unit, e.g. gram), purchase (vendor invoice unit, e.g. kg), sale (display unit, e.g. portion).
  • PurchaseOrderItem, VendorItem, MaterialRecipeItem, InventoryTracking each have a single UoM context per row.
  • Hard FK from line tables to UnitOfMeasure is fragile: UoM rows are reference data and may be soft-deleted or re-keyed by merchants.

Decision

Two-layer storage:

  1. Catalog entities (Material, ProductVariant) store roles in a single jsonb column uom of shape:
    ts
    type TUomRoles = {
      base: { id: string; code: string; name: I18n; ratio: number };
      purchase?: { ... };
      sale?: { ... };
    };
  2. Line entities (PurchaseOrderItem, VendorItem, MaterialRecipeItem, InventoryTracking) store uomId: text as a soft reference plus multiplier: decimal(15,4). The model.ts relations() declaration provides the uom accessor for queries; the DB has no foreign key.

Consequences

ProsCons
Catalog snapshots are stable — even if a merchant's UoM is deleted, Material.uom survivesNeed service-layer validation that uomId resolves at write time
Line entities decouple from UnitOfMeasure lifecycleNo DB-level referential integrity for uomId
multiplier is captured at line-write time → unit conversion immutable post-factTwo storage shapes to remember (jsonb vs uomId)
Replay/audit reasoning works without joining UnitOfMeasureReports must be careful to use line-level multiplier, not catalog uom

Alternatives Considered

OptionProsConsWhy rejected
Hard FK uomId → UnitOfMeasure.id everywhereReferential integrityBreaks on UoM soft-delete; fragile under merchant overridesWrong primitive for reference data
Single jsonb on every line entityNo FK fragilityRepeats UoM data per line; hard to query "all lines with kg"Storage waste, query pain
UnitOfMeasure with versioned rows (no soft-delete)Stable IDsMassive design change, doesn't fix multiplier-snapshot needOut of scope

References

  • core/src/models/schemas/inventory/material/schema.ts (uom jsonb)
  • core/src/models/schemas/inventory/purchase-order-item/schema.ts (uomId soft ref + model.ts uom relation)
  • Memory: feedback_uom_storage_convention.md

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.