URD: Product
| Module | Version | Updated |
|---|---|---|
CORE-05 | v1.1 | 2026-07-15 |
What the Product module does
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.
Seven points to understand first
This is the most important part of the document. Understanding the seven points below is understanding 80% of the module.
1. A product is a concept, the variant is what actually sells
"T-shirt" isn't something a customer buys - it's just the name, description, and category the owner uses to group every version of the same item together. What the customer actually carries to the counter is "red t-shirt, size M" - a variant. Every product must have at least one variant from the moment it's created, and exactly one of them is flagged as the default.
This boundary is easy to blur because the two things look similar on screen, but the data behind them is completely separate: price, barcode, and stock quantity all attach to the variant, never to the product.
| Attribute | Attached to Product | Attached to Variant |
|---|---|---|
| Name, description, category, images | Yes | - |
| Sale channel (where it's displayed) | Yes | - |
| Price (fare) | - | Yes |
| Barcode, SKU, QR code | - | Yes |
| Stock, unit of measure | - | Yes |
| Variant type (stockable, service, combo…) | - | Yes |
Even an item that only comes in one form, with no size or color to choose - say "Bottled water 500ml" - still always has exactly one variant underneath representing that very item. A product is never sold directly; it's always sold through its variant, even when there's only one variant to speak of.
2. A variant is identified by its combination of choices, and only declared combinations get generated
A product varies along axes - Size, Ice level, Color. Each axis carries an ordered list of values (S/M/L, or 100%/50%/0% ice). A variant picks exactly one value on each axis it uses, and that combination of choices is the variant's identity within the product - no two variants of the same product may share a combination.
The easy-to-miss part: the system doesn't auto-explode every possible combination. The owner declares the axes and their values, then declares exactly the variants they actually sell; the system generates only that many.
| Given | Theoretical combinations | Declared variants | Generated variants |
|---|---|---|---|
| A milk tea cup with Size (S/M/L) and Ice level (100/50/0) | 3 × 3 = 9 | 5 (the owner only sells 5 real combinations, e.g. no size-S 100%-ice) | 5 |
Exactly one variant in that declaration has to be the default - whichever one is explicitly flagged, or if none is, the first declared variant is automatically promoted to default. Declaring two as default rejects the whole batch, not just that one variant.
3. The catalog owns product identifiers, inventory only holds physical-unit identifiers
This is the most commonly confused boundary between the two modules. There are two kinds of "code" that are completely different in nature, assigned at two different moments, on two different screens:
- Catalog identifier - system code, slug, SKU, barcode, QR code. These represent a type of item, not one specific unit sitting on the shelf. Assigned when the owner creates or edits a variant on the product screen. Example: "red t-shirt, size M" carries barcode
8934567890123- every red size-M t-shirt in stock, regardless of which batch it arrived in, shares that one barcode. - Warehouse identifier - IMEI, serial number. These represent one individual physical unit, not a type of item. Assigned at goods receipt, entered or scanned by warehouse staff on arrival. Example: receiving 10 phones of the same model (same variant, same catalog barcode), but each carries a different IMEI - 10 IMEIs for 10 units, even though all 10 are "the same product" per the catalog.
In short: the catalog answers "what kind of item is this", inventory answers "which unit or batch is this". Whether a variant has lot tracking or serial tracking turned on is also a flag declared in the catalog when the variant is created - but the lot code or serial code itself is only generated once the goods physically arrive at the warehouse.
4. Identifiers are unique per merchant, not system-wide
A barcode doesn't belong to one particular merchant - it's usually a code the manufacturer pre-prints on the packaging (a GS1 code), and two different merchants can perfectly well sell a product carrying the exact same barcode. So identifier uniqueness only makes sense within the scope of one merchant - Merchant A assigns barcode 8934567890123 to its own "can of soda X", Merchant B assigns that exact same code to their "can of soda X", and the two assignments don't collide - neither merchant is rejected for "code already in use" somewhere else.
This runs against the natural assumption that "a barcode is unique worldwide, so the system should block duplicates system-wide too" - wrong, because that's the manufacturer's code, not a code a merchant generates itself to manage its own inventory.
5. Three different component-explosion mechanisms: KIT, COMBO, MANUFACTURED
At a glance, these three item types all look like "assembled from several other things", but underneath they're three completely different mechanisms for stock and for when the deduction happens. Confusing these three is the most common mistake for anyone new to the module.
| Mechanism | Selling 1 unit deducts what | Has its own stock | Explodes when | Example |
|---|---|---|---|---|
| KIT | Deducts each raw material directly, per recipe | No - has no stock code of its own | Right at the moment of sale, at the counter | A milk tea cup: selling 1 cup immediately deducts milk, tea, and tapioca pearls per the recipe quantities |
| COMBO | Deducts the stock of each component variant (each component still keeps its own stock) | No - the combo's stock is inferred from the lowest stock among its components | Right when it's added to the cart, at the counter | A 2-piece-chicken + fries combo: selling 1 combo deducts the "2-piece chicken" variant's stock and the "fries" variant's stock separately |
| MANUFACTURED | Deducts nothing extra at sale time - only deducts the item's own pre-made stock | Yes - carries its own stock code | At production time (a separate production order, entirely apart from the sale) | A batch of pre-baked bread: baking one batch is one deduction of flour, yeast, and eggs through a production order; selling each loaf afterward only deducts baked-bread stock, never touching the raw materials again |
A quick way to tell them apart: KIT is "mixed or made the instant the customer orders it", COMBO is "bundle several items that already have their own stock and sell them at one price", MANUFACTURED is "made ahead, held in stock, sold later". Only MANUFACTURED has its own stock line the merchant can see on the stock board; neither KIT nor COMBO ever has a stock line for the assembled item itself.
6. Barcode-lookup order at the counter: barcode, then SKU, then QR code, then slug, then system code
Because each identifier scheme is only unique within its own scheme, not across schemes, it's entirely possible for one string to be both this variant's barcode and, coincidentally, another variant's SKU in the same merchant. When a cashier scans or types a string at the counter, the system must always return exactly one result, never a list to pick from - so it needs a fixed resolution order to settle the tie.
That order is: barcode → SKU → QR code → slug → system code. Barcode and SKU come first because those are the two code types cashiers actually scan or type at the counter every day; slug and system code come last because they serve internal lookup, not counter data entry.
Example: the string ABC123 is the barcode of "red t-shirt, size M", and also happens to be the SKU of "blue t-shirt, size L". Scanning ABC123 at the counter always resolves to "red t-shirt, size M" - because barcode beats SKU in the order above; it never resolves to both, and never returns an ambiguous error.
7. Variant type decides how stock is deducted - there is no separate "stock mode"
Every variant carries exactly one of seven variant types: stockable, consumable, service, KIT, combo, manufactured, and entitlement (a virtual variant granting access to a service or resource, tied to no physical goods). This type, together with a "track inventory or not" flag declared right on that same variant, is everything needed to decide how stock deduction behaves - there's no separate "stock mode" field sitting alongside the variant type.
| Variant type | Tracks its own stock when | Example |
|---|---|---|
| Stockable, Manufactured | Yes, whenever the tracking flag is turned on for the variant | Red t-shirt size M; a batch of baked bread |
| Combo | Never - always inferred from its components, regardless of the flag | 2-piece-chicken + fries combo |
| Consumable, Service, KIT, Entitlement | No - always created untracked, whether the flag is on or not | Ice cubes (consumable); a parking fee (service); a milk tea cup (KIT); a trial class session (entitlement) |
Combo is the most special case: even if the merchant turns on the tracking flag for a combo variant, the system still doesn't create separate stock for it - a combo isn't, by nature, an independent physical item code, it's just a way of bundling variants that already have their own stock and selling them at one price.
What is in the module
Ten features. Click an AREA code to open Details at that area.
AREA | Name | Phase | Status |
|---|---|---|---|
PRD | Product Catalogue | P1 | Partial |
CAT | Categories | P1 | Built |
VAR | Variants | P2 | Partial |
BND | Combos & Bundles | P2 | Built |
OPT | Product Options | P2 | Partial |
FAR | Fares / Pricing | P2 | Built |
PID | Identifiers | P1 | Partial |
CMP | Promotions | P3 | Partial |
ACC | Access Control | P1 | Built |
SCH | Sale Channels | P2 | Built |