Items & Inventory¶
Backend domains:
- app/graphql/items/ — catalog, categories, UoMs, alternative unit prices, client discounts
- app/graphql/inventories/ — stock per item, manual adjustments, movement logs
RBAC Paths: ITEMS, INVENTORIES.
Items (product catalog)¶
An Item is a sellable / purchasable / inventoriable thing. The model supports:
- Item type — product, service, or kit.
- Default UoM plus a list of
unit_of_measuresthe item can be transacted in. - Default tax rate — points at
itbms_tax_rates(e.g. 0%, 7%, 10%, 15%). - Category — a tree of
ItemCategoryrows for reporting and price-rule scoping. - Default accounts — sales account, COGS account, inventory account (overrides the tenant defaults).
- Alternative unit prices — per-UoM, per-currency, or per-quantity-tier pricing (
alternative_unit_price_*). - Client-specific discounts —
items_client_discount_*lets a tenant lock a price for a particular customer.
Key mutations:
- Items:
createItem,updateItem,deleteItem,deleteItems,getOrCreateItem. - Categories:
createItemCategory,updateItemCategory,deleteItemCategory. - Alt prices:
addAlternativeUnitPrice,updateAlternativeUnitPrice,deleteAlternativeUnitPrice. - Client discounts:
addClientDiscount,updateClientDiscount,deleteClientDiscount. - UoMs:
updateUnitOfMeasuresVisibility(per-tenant UoM enable/disable).
Key queries: itemSearch, getItemById, getItemByItemNumber, findAlternativeUnitPriceByItemId, getItemClientDiscountsByItemId, itbmsTaxRates, unitOfMeasures, getUnitOfMeasureByName, getDefaultUom, itemCategorySearch, getItemCategoryById.
Items are bulk-imported via app/graphql/items/tabular/ (CSV/XLSX).
Inventories¶
Inventory is event-sourced from invoices, supplier-invoice receivings, manual adjustments, and (planned) inter-location transfers. Stock per item is derivable; the inventory listeners maintain a cached current quantity for fast queries.
Key mutations:
updateInventory— direct override (admin only).createManualInventoryAdjustment— recommended path: writes a labelled adjustment with a reason and posts the inventory-adjustment JE.
Key queries:
getItemStockOverTime— time-series stock graph.getInventoryLogsByItemId— full movement history.
Stock posting goes through LedgerService for inventory accounts only when items have is_inventoriable=true. Services post to revenue/COGS without involving inventory.
Inventory dimensions (locations, lots, costing methods)¶
See the full feature doc at Inventory dimensions. At a glance:
- Multi-location —
InventoryLocationrows are tenant-scoped. EveryInventory,InventoryLog,InventoryLot, andInventoryCostLayerrow carries alocation_id. Exactly one location is markedis_default(enforced by a partial unique index). Receipts, adjustments, and queries that don't specify a location fall back to the default. - Lot / serial tracking —
Item.tracking_modeis one ofNONE(default),LOT, orSERIAL. When enabled, every inflow/outflow must carry alot_number. SERIAL items use oneInventoryLotrow per unit withquantity_received = 1. - Costing methods —
Item.costing_methodis one ofAVERAGE(default — current behaviour),FIFO, orLIFO. FIFO/LIFO items maintain anInventoryCostLayerper receipt; outflow consumes layers oldest-first (FIFO) or newest-first (LIFO) and returns the COGS for the line. AVERAGE items continue to use the runningItem.average_cost.
Switching costing_method only affects future receipts (no retroactive recompute). Switching tracking_mode from NONE requires the item to have zero stock to keep lot history clean.
What it does NOT have yet¶
- Inter-location transfers (in-transit balance) —
transferStockis planned in a follow-up. - Bill-of-materials for kits / manufacturing.
- Barcode / scanner input on the API surface (frontend-only today).
- Stocktake / cycle-count workflow.
- Per-invoice-line location/lot picking on outflow (today, invoices draw from the default location).
These are documented as roadmap candidates in the PYMES feature ideas section.