URD: Product
| Module | CORE-05 | Version | v0.4 |
|---|---|---|---|
| Status | Built | Date | 2026-05-29 |
1. Purpose
Define the user-facing requirements for product catalogue management: how owners create and maintain products, the variants they actually sell, the prices (fares) those variants carry, the categories that organize them, and the identifiers used to look them up. The catalogue is the foundation every selling channel reads from.
2. Scope
| Included | Excluded |
|---|---|
| Product create / update / status management | Bill of Materials & recipes → Inventory |
| Categories (with add-on flag) | Stock levels per location → Inventory |
| Variants and aggregate variant operations | Order processing & checkout → Orders |
| Fares and fare sets (pricing input) | Promotion discount calculation (In-progress) |
| Multi-scheme identifiers (SYSTEM, SLUG, SKU, BARCODE, QRCODE) | Unit-conversion engine (Planned) |
| Sale-channel availability | CSV / bulk import (Planned) |
| Variant types & units of measure | Technical API specifications → developer docs |
| Role-based data filtering |
Conceptual definitions only — concrete schema and behavior live in commerce domain model and pricing fares.
3. Definitions
| Term | Definition |
|---|---|
| Product | A base item in a merchant's catalogue. |
| Variant | A sellable unit of a product (e.g. a size or flavour). What is actually sold and priced. |
| Variant type | Classification driving stocking & bundling: STORABLE, CONSUMABLE, SERVICE, KIT, COMBO, MANUFACTURED. |
| Fare set | The pricing container linked 1-to-1 to a variant. |
| Fare | A price entry inside a fare set; can be a base price, an override, or a quantity/time/channel tier. |
| Category | A product grouping within a merchant; can be flagged add-on. |
| Identifier | A scheme-based code (SYSTEM, SLUG, SKU, BARCODE, QRCODE) used for lookup. |
| Bundle | A combo, add-on, or frequently-bought-together relationship between variants. |
| Sale-channel product | A mapping controlling which products appear in which channels. |
4. Conceptual Model
Conceptual only — full schema lives in the commerce domain model and pricing fares.
5. Functional Requirements
One table per functional area.
<AREA>codes match the test-case IDs. Priority = MoSCoW (Must / Should / Could / Won't).
5.1 Product (PRD)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-PRD-001 | M | Owner can create a product within a merchant | Built |
| URD-PRD-002 | M | Each product gets a system-generated identifier and slug | Built |
| URD-PRD-003 | M | Product name & description support multiple languages | Built |
| URD-PRD-004 | M | A default variant is created with each new product | Built |
| URD-PRD-005 | M | Owner can update product information | Built |
| URD-PRD-006 | M | Owner can deactivate / reactivate / archive a product | Built |
| URD-PRD-007 | M | Product slug is unique within the same merchant | Built |
| URD-PRD-008 | M | User can find a product by ID or slug | Built |
| URD-PRD-009 | M | User can view a product list (role-filtered) | Built |
| URD-PRD-013 | S | Owner can attach images / media to products | Built |
| URD-PRD-014 | S | Owner can assign products to specific sale channels | Built |
| URD-PRD-015 | S | Archived products are read-only (terminal status) | Built |
| URD-PRD-016 | S | Products support a parent-child hierarchy | Built |
| URD-PRD-017 | S | User can search products by name (partial, i18n-aware) | Built |
| URD-PRD-018 | S | User can filter the product list by category | Built |
| URD-PRD-019 | C | Owner can import a catalogue from CSV / spreadsheet | Planned |
5.2 Category (CAT)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-CAT-001 | M | Owner can create categories within a merchant | Built |
| URD-CAT-002 | M | Category name supports multiple languages | Built |
| URD-CAT-003 | M | Owner can update and delete categories | Built |
| URD-CAT-004 | S | A category can be flagged as add-on | Built |
5.3 Variant (VAR)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-VAR-001 | M | Every product has at least one variant; status can change | Built |
| URD-VAR-002 | M | Owner can create additional variants | Built |
| URD-VAR-003 | M | Aggregate create: variant + info + fare set + fares + identifiers in one atomic step | Built |
| URD-VAR-004 | M | Aggregate update: update a variant with all related data atomically | Built |
| URD-VAR-005 | M | Each variant has a system-generated identifier | Built |
| URD-VAR-006 | M | Variant type (STORABLE / CONSUMABLE / SERVICE / KIT / COMBO / MANUFACTURED) is selectable | Built |
| URD-VAR-007 | S | Variant can have an effective date range | Built |
| URD-VAR-008 | S | Owner can assign SKU / BARCODE / QRCODE identifiers | Built |
| URD-VAR-009 | S | Variant stores base / purchase / sale units of measure | Built |
| URD-VAR-010 | S | Bundles relate variants as combo / add-on / frequently-bought-together | Built |
| URD-VAR-011 | C | A variant's resolved price can be read via a dedicated endpoint | Planned |
| URD-VAR-012 | C | Unit conversions between purchase and sale units | Planned |
5.4 Fare / Pricing (FAR)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-FAR-001 | M | Every variant has exactly one fare set | Built |
| URD-FAR-002 | M | Every fare set contains at least one (default) fare | Built |
| URD-FAR-003 | M | Fare amount must be zero or positive | Built |
| URD-FAR-004 | M | At sale time the system resolves the applicable fare: override → discount → default | Built |
| URD-FAR-005 | S | Fares can have effective date ranges | Built |
| URD-FAR-006 | S | Fares can have min / max quantity windows | Built |
| URD-FAR-007 | S | Fares support parent-child groups with context rules (channel, time, quantity) | Built |
The current fare model uses OVERRIDE and DISCOUNT group types (an earlier draft labelled these SALE/OVERRIDE). See pricing fares.
5.5 Identifiers (PID)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-PID-001 | M | Each identifier is unique per scheme within a merchant | Built |
| URD-PID-002 | M | A product / variant can hold multiple identifiers across schemes | Built |
| URD-PID-003 | M | SYSTEM and SLUG identifiers are auto-generated on creation | Built |
| URD-PID-004 | S | Owner can assign SKU, BARCODE, QRCODE identifiers | Built |
5.6 Promotions (CMP)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-CMP-001 | S | Owner can create and manage promotion campaigns (with date range, usage limit) | In-progress |
| URD-CMP-002 | S | Promotions are scoped to a merchant | In-progress |
| URD-CMP-003 | C | Promotion discounts are applied automatically at pricing time | Planned |
Promotion CRUD is functional; the discount calculation engine is disabled, so discounts are not yet applied automatically. See pricing promotions.
5.7 Access (ACC)
| ID | P | Requirement | Status |
|---|---|---|---|
| URD-ACC-001 | M | All product operations are filtered by the requesting user's role | Built |
| URD-ACC-002 | M | Owner sees only products under own merchants | Built |
| URD-ACC-003 | M | Employee sees only products in assigned merchants | Built |
| URD-ACC-004 | M | Admin / Super Admin bypass role filtering | Built |
6. Acceptance Criteria
AC-PRD-01: Product creation
| Given | When | Then |
|---|---|---|
| Owner with an active merchant | Creates a product with name + category | Product created with generated identifiers; default variant created; product linked to merchant |
| Slug would collide | System ensures a unique slug within the merchant |
AC-VAR-01: Aggregate variant
| Given | When | Then |
|---|---|---|
| An existing product | Owner creates a variant with info, fares, and identifiers in one step | Variant + info + fare set + fares + identifiers created atomically |
| Any sub-step fails | Entire operation rolls back; nothing is persisted |
AC-FAR-01: Fare resolution
| Given | When | Then |
|---|---|---|
| A variant with multiple active fares | Added to cart | Override group wins; otherwise lowest qualifying discount; otherwise the default fare; date & quantity windows filter candidates first |
| No qualifying fare | The default (base) fare is used |
AC-ACC-01: Role-based access
| Given | When | Then |
|---|---|---|
| Owner with merchants X, Y | Views products | Only products under X and Y are returned |
| Employee assigned to X | Views products | Only products under X are returned |
| Admin | Views products | All products returned; filtering bypassed |
7. Constraints & Non-Goals
Constraints
| ID | Constraint |
|---|---|
| C-01 | A product belongs to exactly one merchant |
| C-02 | Product slug is unique within the same merchant |
| C-03 | Every product has at least one variant |
| C-04 | Every variant has exactly one fare set with at least one fare |
| C-05 | Aggregate create / update operations are all-or-nothing |
| C-06 | All records use soft-delete; nothing is physically removed |
| C-07 | Role-based filtering applies to every list and count operation |
Non-Goals
- Bill of Materials / recipe management (→ Inventory)
- CSV / Excel bulk import
- Unit-conversion engine
- Promotion discount calculation (CRUD only today)
8. Version History
| Date | Author | Description | Ver |
|---|---|---|---|
| 2026-02-26 | P. Do — Product Owner | Initial user stories & requirements | v0.1 |
| 2026-04-16 | P. Do — Product Owner | Expanded variant & fare requirements | v0.3 |
| 2026-05-29 | Docs migration | Restructured to module-docs convention; status-honest Built/In-progress/Planned per dev docs; promotions split from inventory (moved to Inventory module) | v0.4 |