Products
+ New productProject Gaia — static mockup in Filament v3 table idiom. No backend. Sample data: Katana tenant snapshot 2026-04-12.
Switch factory
CYTO-11 — Balakong Jaya CTSB CYTO-29 CTSB CYTOWELL-54 — Taming Jaya PIC/S CTWSB CYTOCNGZ-602 — Guangzhou CTCNGZOnly factories you are granted appear here (Filament tenant menu).
Project Gaia — static mockup in Filament v3 table idiom. No backend. Sample data: Katana tenant snapshot 2026-04-12.
Turning OFF is blocked once stock movements exist — GMP traceability.
They disappear from pickers and new documents but keep full history and batch traceability. Items still holding stock remain visible in Stock until zeroed out.
Exports run async with a completion notification — same pipeline as the “Import done.” toast in the Katana capture.
Only items with zero stock and no stock movements can be deleted — anything with ledger history is skipped (archive instead; movement history is immutable under GMP). This cannot be undone.
| Field | Filament component | Type | Source | FK target |
|---|---|---|---|---|
| Name | TextColumn::searchable(isIndividual)->url()->sortable() | string (product / variant) | derived: product.name + ' / ' + variant option values; one grid row per variant | variants.id |
| Variant code / SKU | TextColumn::searchable(isIndividual) | string, nullable | stored: variants.sku — separate field, not derived (BBeast rows show variant in Name but empty SKU) | — |
| Category | TextColumn::badge() + SelectFilter | string lookup | stored: items.category_id (End Product, Tea Powder observed) | categories.id |
| Default sales price | TextColumn::money('MYR') | money MYR, nullable | stored per variant; 0 on most rows, blank (never set) on 2 rows in sample | — |
| Cost | TextColumn::money('MYR') (not filterable) | money MYR | derived: inventory engine cost — moving average / BOM + operations roll-up | — |
| Profit | TextColumn::money('MYR')->color(fn) red when negative | money MYR | derived: default sales price − cost (verified on every dump row) | — |
| Margin | TextColumn::state(fn) — blank when price 0 | percent, nullable render | derived: profit ÷ default sales price; engine suppresses divide-by-zero (blank on all sample rows) | — |
| Prod. time | — (dropped: empty across entire snapshot) | duration | derived: total production time rolled up from recipe/operations rows | — |
| Active / Archive tabs | Tabs (lifecycle filter); Archive view not captured | bool flag | stored: items.archived_at | — |
| Bulk actions | BulkAction: Bulk edit / Archive / Delete / Export | action | Katana toolbar icon cluster (download, print, edit, archive, delete) | — |
Profit = Default sales price − Cost, computed per row (all dump rows verify exactly).Margin = Profit ÷ Default sales price, rendered blank when sales price is 0 — engine suppresses divide-by-zero instead of showing 0% or infinity.BulkAction::make('bulkEdit')->form(...) (category / price / batch tracking); ->archive() sets items.archived_at; ExportAction runs via the Filament export job (async notification, same pipeline as the import toast); DeleteBulkAction is policy-guarded — deletable only when no stock_movements rows reference the variant, so items with ledger history are skipped (archive instead — GMP immutability). Toolbar Export CSV icon runs the same export over the filtered set when nothing is selected.