Item Catalog Module¶
Purpose¶
Central registry for all items in the system — ingredients, finished products, intermediates, packaging, consumables, services, and resale goods. Every item that exists anywhere in GrinSystem (in recipes, stock, orders, invoices) must first exist in ItemCatalog. Other modules reference items by item_id and own their own extension data (nutrition, stock rules, pricing) linked to that ID.
Actors¶
| Actor | Interaction |
|---|---|
| Admin | Full access: create, edit, deactivate, import, manage groups |
| Manager (Kierownik) | Create and edit items; manage groups; cannot delete |
| Technologist (Technolog) | Create and edit items (same as Manager); needed for R&D ingredient discovery |
| Worker (Pracownik) | Read-only: browse and search catalog (all item types) |
| Warehouse Keeper (Magazynier) | Read-only: browse and search catalog, scan items |
| Sales Rep (Handlowiec) | Read-only: browse orderable items |
| QA Specialist | Read-only |
Use Cases¶
IC-UC-01: Create Item¶
Actor: Admin, Manager, Technologist Phase: 1
User creates a new item by providing:
- Code (unique per tenant, required, max 50 chars)
- Name (required, max 200 chars)
- Item type (required): see item type table below
- Unit of measure / storage_unit (required): kg | g | l | ml | piece
- Description (optional, free text, max 500 chars) — internal notes / production context
- Tags (optional, list of free-text strings, max 20 tags, each tag max 50 chars)
- Item Group assignment (optional): assign to an existing ItemGroup of the same item_type
- Shelf life in days / shelf_life_days (optional, integer): applicable to product and intermediate types. Used by Warehouse module to auto-calculate expiry date on production output LOTs (expiry = production_date + shelf_life_days)
- EAN / barcode (optional, string, max 20 chars): GS1 standard barcode. Used by Warehouse for scan-based item lookup on PZ, and by CRM for customer-facing labels. Central source for all modules.
- Active status: defaults to true
Item Types:
| Type | Description | In BOM/Recipe | In Stock | In Invoicing |
|---|---|---|---|---|
ingredient |
Raw material used in production | ✓ | ✓ | ✓ |
product |
Finished good produced by a recipe | — | ✓ | ✓ |
intermediate |
Semi-finished item with its own recipe, used in multi-level BOM | ✓ | ✓ | ✓ |
packaging |
Packaging material usable in BOM | ✓ | ✓ | ✓ |
consumable |
Production consumable (cleaning agents, gloves, etc.); tracked in stock, never in BOM/recipe | — | ✓ | ✓ |
service |
Labour or service; invoicing only; no stock, no BOM | — | — | ✓ |
resale |
Bought and sold without transformation | — | ✓ | ✓ |
System validates code uniqueness per tenant and saves the item. An ItemCreated event is published for other modules to react (e.g., Warehouse creates a default stock entry, Manufacturing creates an allergen extension placeholder and auto-creates an IngredientGroup for ingredient and intermediate types).
Item audit metadata: Each item record stores: created_by (user_id), created_at (timestamp), updated_by (user_id), updated_at (timestamp). These fields are visible on the item detail page and provide item-level edit tracking complementing the general audit log.
Business rules:
1. Code must be unique per tenant — case-insensitive match.
2. storage_unit is immutable after creation; cannot be changed even if no stock or recipe uses the item yet (prevents silent unit mismatch in BOM).
3. item_type is immutable from the moment the item is saved — it cannot be changed under any circumstances. Events (ItemCreated) carry the type; changing it later would invalidate downstream module state.
4. service and consumable type items cannot be used in BOM or production orders.
5. System items (is_system = true) are pre-populated ingredient type items provided by GrinSystem (approx. 200 common Polish food raw materials). They are shared across all tenants and are read-only. Nutritional values and allergen data for system items are managed by GrinSystem and stored as Manufacturing extensions.
Acceptance criteria:
- Item appears in catalog immediately after creation.
- Other modules receive ItemCreated event.
- Attempting to create with a duplicate code returns a clear validation error identifying the conflict.
- Attempting to edit item_type after creation returns a validation error.
IC-UC-02: Edit Item¶
Actor: Admin, Manager, Technologist Phase: 1
User can edit: name, description, tags, item_group assignment, shelf_life_days, EAN/barcode. Cannot edit: code, storage_unit, item_type.
Business rules:
1. code is immutable once saved.
2. storage_unit is immutable always.
3. item_type is immutable — set at creation and cannot be changed.
4. Changing name does not retroactively affect historical documents (LOTs, WZs, ZPs snapshot name at time of use).
5. Tags are case-insensitively deduplicated on save (e.g. 'Organic' and 'organic' are treated as the same tag).
6. Changes to shelf_life_days affect future LOTs only — existing LOTs retain the expiry date calculated at the time of their PZ receipt. Existing LOTs are not retroactively updated.
7. EAN/barcode can be corrected or updated after creation (e.g., supplier changes barcode or an error was made at creation).
IC-UC-03: Deactivate / Reactivate Item¶
Actor: Admin Phase: 1
Admin marks an item as inactive. Inactive items: - Are hidden from creation forms (recipe BOM, PZ, WZ, ZS) by default - Can be shown via "include inactive" filter toggle - Cannot be added to new recipes or orders - Existing references (open ZPs, open ZSs) are not broken
Reactivation restores full visibility.
Business rules:
1. Cannot deactivate an item that has non-zero stock — must clear stock first (manual correction or disposal).
2. Cannot deactivate while item appears in any open (non-completed/non-cancelled) ZP or ZS.
3. Cannot deactivate an item that is used in any Published recipe BOM (cross-module check with Manufacturing). Item must first be removed or replaced in all Published recipes.
4. ItemDeactivated event published; other modules may react (e.g., remove from orderable list in CRM).
5. Deactivated item codes are reserved — no other item in the same tenant can claim the code of a deactivated item. This means reactivation is always allowed from a code-uniqueness standpoint (no conflict is possible).
6. Reactivation is allowed only if item status is inactive. No code conflict check is needed (see BR-5 above).
IC-UC-04: Search and Browse Catalog¶
Actor: All roles Phase: 1
Users can search items using a partial-match text query. Search is: - Case-insensitive - Diacritic-insensitive (e.g. "maka" matches "mąka", "ser" matches "sér")
Searchable fields: name, code, description, item_group.name, tags
Additional filters: item_type (multi-select), active status (default: active only), item_group.
Results show: code, name, type, unit, active status, assigned group (if any), source (system or tenant).
Business rules: 1. System items are visible to all tenants in read-only mode. 2. Worker role sees all item types in catalog search (read-only). What Workers can see and do within Manufacturing ZP context is defined in the Manufacturing spec. 3. Search with no query and no filters returns the full active catalog (paginated).
IC-UC-05: Create and Manage Item Groups¶
Actor: Admin, Manager Phase: 1
Item Groups are organisational containers for items of the same type, used to browse and filter the catalog by business category (e.g. "Drinks", "Hard Cheeses", "Cleaning Agents"). Every group belongs to exactly one item_type — you cannot mix types inside a group.
Operations: - Create group: name (required, max 100 chars, unique per tenant + item_type), item_type (required, immutable after creation) - Edit group: name only - Delete group: allowed only when no items are assigned to it - Assign item to group: move an item into a group; item must share the same item_type as the group - Remove item from group: item becomes unassigned (appears in "Ungrouped" view)
Business rules: 1. A group is scoped to a single item_type — groups cannot span types. 2. An item belongs to at most one ItemGroup at any time. 3. Groups are per-tenant; system items belong to system-managed groups, tenants cannot modify them. 4. Deleting a group with items assigned is not permitted — items must first be reassigned or removed from the group. 5. "Ungrouped" is a virtual view (not a real group); items with no group assignment appear there. 6. Group names are unique per (tenant, item_type) combination — case-insensitive.
Note: IngredientGroups (used for recipe ingredient substitution) are a separate concept owned by the Manufacturing module. They are automatically created when a new
ingredientorintermediateitem is added to the catalog. Seemanufacturing.md§ IngredientGroups.
IC-UC-06: Import Items from Excel¶
Actor: Admin Phase: 1
Download the provided template, populate rows with: code, name, item_type, storage_unit, description (optional), group_name (optional), tags (optional, comma-separated), ean (optional), shelf_life_days (optional, integer — applicable to product and intermediate types only; ignored for other types).
Import behaviour: - System processes ALL rows before returning any result. - If any row has a validation error, the entire import is aborted — zero rows are saved. - The response always includes a complete list of errors with row number, field name, and human-readable message, so the user can fix all issues in one edit cycle. - Import succeeds only when the file contains zero errors.
Error categories detected:
- Missing required fields (code, name, item_type, storage_unit)
- Invalid item_type or storage_unit value
- Duplicate code within the file itself (both rows are flagged)
- Code already exists in tenant catalog (per-row error)
- group_name references a group of the wrong item_type
- Malformed tags (e.g. tag value exceeds 50 chars)
Error response format:
[
{ "row": 2, "field": "item_type", "message": "Unknown type 'rawmaterial'. Valid types: ingredient, product, intermediate, packaging, consumable, service, resale." },
{ "row": 7, "field": "code", "message": "Code 'SER-01' already exists in your catalog." },
{ "row": 7, "field": "code", "message": "Code 'SER-01' also appears on row 3 — duplicate within file." }
]
Business rules: 1. Import never overwrites existing items. 2. System items cannot be imported or overwritten. 3. All-or-nothing: partial saves are never permitted, regardless of how many rows are valid.
Business Rules¶
| # | Rule |
|---|---|
| BR-IC-01 | Every item must have a unique code per tenant. System item codes occupy a global namespace separate from per-tenant codes. |
| BR-IC-02 | storage_unit (kg / g / l / ml / piece) is set once and immutable. All BOM quantities and stock entries use this unit. There is no automatic conversion between units. |
| BR-IC-03 | Item types service and consumable cannot appear in BOM or production orders. service items have no stock. |
| BR-IC-04 | System items (is_system = true) are managed by GrinSystem, visible to all tenants in read-only mode, and cannot be created, edited, or deactivated by tenants. |
| BR-IC-05 | An item may belong to at most one ItemGroup. ItemGroups are scoped to a single item_type. Groups are managed manually — no auto-creation. |
| BR-IC-06 | Deactivation requires zero stock, no open orders, and no usage in Published recipe BOMs. Active items cannot be hard-deleted — only deactivated. |
| BR-IC-07 | item_type is immutable from creation. Cannot be changed under any circumstances. |
| BR-IC-08 | Excel import is all-or-nothing. System collects ALL row-level errors before responding. Zero rows are saved if any error exists. |
| BR-IC-09 | Search is case-insensitive and diacritic-insensitive, covering name, code, description, item_group.name, and tags. |
Out of Scope¶
- Allergens and nutritional values → owned by Manufacturing module (linked by item_id)
- Stock levels, reorder points, LOT tracking settings, dimensions → owned by Warehouse module
- Sale price, pricing tiers, product descriptions, images, orderable flag → owned by CRM module (covers
product,resale, andservicetype items) - Recipe/BOM associations, yield percentages → owned by Manufacturing module
- Supplier information → owned by Warehouse module (supplier linked to PZ, not to item)
- Item photos → owned by CRM module (product presentation concern, not item identity)
- IngredientGroups (recipe substitution groups) → owned by Manufacturing module
Open Questions¶
| # | Question | Impact |
|---|---|---|
| OQ-IC-02 | Resolved: System items can be updated by GrinSystem. Nutrition/allergen data is owned by the Manufacturing module, which handles recalculation of affected recipes and snapshot creation. Not an ItemCatalog concern. | Manufacturing module scope |