PRD: Materials
| Module | Inventory (CORE-06) | PRD ID | PRD-MAT-001 |
| Status | Shipped | Owner | Inventory squad |
| Date | 2026-05-18 | Version | v1.0 |
| Packages | @nx/inventory · @nx/core · apps/client | URD | MAT |
TL;DR
Gives F&B merchants a first-class Material catalog - raw ingredients and semi-finished goods tracked separately from sellable products. Each material carries an i18n name, a system identifier, and multiple identifier schemes (SKU, barcode, QR) so it can be looked up at receipt and stock-take time. Materials are the principal that recipes (BOM) consume, turning ad-hoc ingredient notes into a managed, scannable inventory.
1. Context & Problem
F&B merchants buy and consume raw ingredients - flour, milk, syrup - that are never sold directly but are needed to produce sellable products. The existing inventory model only tracks product variants, so there is nowhere to record these inputs or to drive a bill-of-materials decrement at the kitchen. Without a material entity, ingredient stock cannot be counted, looked up by barcode, or consumed by a recipe, which blocks accurate cost tracking and kitchen automation for the F&B vertical KICKO targets.
Materials introduces a per-merchant catalog of raw and semi-finished goods kept separate from the product catalog. It is the principal that recipes (the REC feature) consume, and it carries its own identifier scheme so it can be found by SKU, barcode, or QR code.
2. Goals & Non-Goals
Goals
- A per-merchant Material catalog entity, isolated per merchant, separate from product variants.
- An i18n material name (multiple locales) and an editable description.
- A system identifier (
MAT_YYYYMMDD_id), auto-generated and not user-editable. - A lifecycle that supports deactivate and archive (archived is terminal / read-only).
- A MaterialIdentifier sub-entity supporting schemes SYSTEM, SLUG, SKU, BARCODE, QRCODE, with SYSTEM as the default and
(scheme, code)unique across materials. - Multiple identifiers of different schemes on one material, plus barcode search.
- A material aggregate API (create/update with identifiers and vendor dimensions) backing the client management screens.
Non-Goals
- Material recipes / BOM and kitchen explosion - owned by the
RECfeature. - Multi-level BOM (material as principal for sub-assemblies) - URD Non-Goal.
- Recipe yield and scrap percentage - URD Non-Goal.
- Full barcode-scanning integration for stock takes - URD Non-Goal.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Catalog adoption | F&B merchants maintain their raw ingredients as materials rather than free-text notes |
| Identifier uniqueness | Zero duplicate (scheme, code) pairs across a merchant's materials |
| Lookup coverage | Materials with a registered BARCODE are found by barcode search at receipt/stock-take |
| BOM readiness | Materials available as the principal that recipes (REC) can consume |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Owner | Maintain the raw-ingredient catalog, assign SKUs/barcodes, archive obsolete materials |
| Inventory staff | Find a material by barcode/QR at receipt and stock-take time |
| Kitchen (via Recipes) | Reference materials as recipe components for BOM decrement |
Core scenarios: create a material with an i18n name and vendor dimensions → attach SKU/barcode/QR identifiers → look it up by barcode at receipt → deactivate or archive when it is no longer used.
5. User Stories
- As an owner, I want to create a material with an i18n name and description, so my raw ingredients are catalogued separately from sellable products.
- As an owner, I want each material to get a unique system identifier automatically, so I never have to invent or collide codes.
- As an owner, I want to attach SKU, barcode, and QR identifiers to a material, so it can be scanned and looked up at receipt and stock-take.
- As inventory staff, I want to search a material by its registered barcode, so I can find it quickly on the floor.
- As an owner, I want to deactivate or archive a material, so obsolete ingredients leave the working catalog without losing history.
- As an owner, I want to create and update a material together with its identifiers and vendor dimensions in one aggregate call, so the management screen saves a complete record at once.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Create a per-merchant material, isolated per merchant, separate from product variants | URD-MAT-001 |
| FR-2 | Material has an auto-generated system identifier (MAT_YYYYMMDD_id) and an i18n name supporting multiple locales | URD-MAT-002..003 |
| FR-3 | Material description is editable | URD-MAT-004 |
| FR-4 | Material lifecycle supports deactivate and archive (archived is terminal / read-only) | URD-MAT-005..006 |
| FR-5 | Material identifier is unique | URD-MAT-007 |
| FR-6 | Owner can attach a BARCODE identifier to a material | URD-MAT-101 |
| FR-7 | Identifier supports schemes SYSTEM, SLUG, SKU, BARCODE, QRCODE; scheme defaults to SYSTEM when unspecified | URD-MAT-102 · URD-MAT-104 |
| FR-8 | A (scheme, code) pair is unique across materials | URD-MAT-103 |
| FR-9 | Multiple identifiers of different schemes can coexist on one material | URD-MAT-105 |
| FR-10 | Material is searchable by registered barcode | URD-MAT-106 |
| FR-11 | Material aggregate create/update accepts identifiers and vendor dimensions; malformed update payloads are rejected | URD-MAT-001..007 |
Full requirement text and acceptance criteria live in the Inventory URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Data integrity | (scheme, code) uniqueness is enforced across a merchant's materials; identifier conflicts are rejected |
| Tenancy & authz | Materials are isolated per merchant (x-merchant-id) and gated by inventory permissions |
| Lifecycle safety | Archived materials are terminal / read-only; soft-delete preserves history |
| i18n | Material names are stored as an i18n object ({ en, vi, … }) supporting multiple locales |
| Vendor linkage | Vendor association flows through VendorItem, never a vendorId column on the material |
| Consistency | Aggregate create/update (material + identifiers + vendor dimensions) is transactional; malformed payloads are rejected without partial writes |
8. UX & Flows
Key screens (in apps/client): the material management screen with unit-of-measure support, form validation, vendor multi-select, identifier entry, and an overview with summary widgets.
9. Data & Domain
| Entity | Role |
|---|---|
Material | The catalog principal - i18n name, description, system identifier, status, typed metadata jsonb |
MaterialIdentifier | A sub-entity holding an identifier (scheme, code); schemes SYSTEM / SLUG / SKU / BARCODE / QRCODE |
VendorItem | The link between a material and its vendors (sole source of truth for vendor association) |
InventoryItem / stock | Optional stock linkage created together with the material |
Conceptual only - full schema and invariants in the inventory domain model.
10. Dependencies & Assumptions
Depends on
- Inventory items & stock (URD-ITM · URD-STK) - a material can be created together with its inventory item/stock linkage.
- Vendors (URD-VEN) - vendor dimensions on a material link through VendorItem.
@nx/core- material and material-identifier schemas/models live under the centralized core inventory schemas.
Assumptions
- The merchant exists and has a default inventory location for any stock linkage.
- Identifier codes are unique per
(scheme, code)within the merchant's materials.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
Duplicate (scheme, code) would break lookup | Uniqueness enforced across materials; conflicting writes rejected |
| Vendor link could drift if stored on the material | Vendor association flows through VendorItem only - no vendorId column |
| Archived materials still referenced by recipes | Archive is terminal/read-only; recipes (REC) consume only active materials |
| Full barcode-scanning integration for stock takes | Out of scope this increment; barcode search is supported, hardware scan flow deferred |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P3 (BOM + Advanced) - see URD feature catalog |
| Rollout | All merchants; no feature flag (F&B verticals are the primary users) |
| Migration | None - new entities under @nx/core inventory schemas |
| Launch criteria | Create material → attach SKU/barcode/QR → barcode lookup returns it → (scheme, code) uniqueness rejected on conflict → aggregate create/update verified end-to-end |
| Monitoring | Material count per merchant, identifier-conflict rejection rate, barcode-search hit rate |
13. FAQ
How is a material different from a product? A material is a raw or semi-finished input that is never sold directly; a product variant is sellable. Materials are catalogued separately and consumed by recipes (BOM).
Can one material have several barcodes? Yes - multiple identifiers of different schemes (SKU, BARCODE, QRCODE) can coexist on one material, as long as each (scheme, code) pair is unique across materials.
What scheme is used if I do not pick one? The identifier scheme defaults to SYSTEM, and a system identifier (MAT_YYYYMMDD_id) is generated automatically.
Can I delete a material? Materials are soft-deleted and can be deactivated or archived; archived is terminal and read-only, so history is preserved.
How do I link a vendor to a material? Vendor association flows through VendorItem (the vendor multi-select on the management screen), never a column on the material itself.
References
- URD: Inventory - Materials (
MAT) - requirements + acceptance - Related: Recipes / BOM (
REC) - consumes materials as the principal - Module: Inventory - overview + traceability
- Developer: @nx/inventory · domain model