Designing item types
Inventory tables: the third table kind, its five column roles, and how stock becomes queryable.
An item type is an inventory table: a data table with the kind
inventory_item, living inside an environment and designed in the same schema
designer as your entity and result tables. One row is one physical item, a vial
or a tube. Its columns define each item's fields, and a few of them carry a
role that makes the table stock-aware.
The third table kind
Every table in an environment is one of three kinds:
- Entity
- A thing the lab tracks over time. One row = one animal, one compound, one antibody.
- Result
- A measurement event. One row = one recorded value, split into dimensions and measurements.
- Inventory item
- Physical stock of an entity. One row = one vial, one tube, one plate, with a quantity you draw down.
The distinction that trips people up is entity versus inventory item. Test
Articles is an entity table: it holds the catalog record for compound DLA-7,
once. Sample Stock is an inventory table: it holds the eleven vials of DLA-7
actually sitting in freezer L-204, each with its own quantity and location. The
entity_link role is what connects the second to the first.
Role columns
Five roles exist. A role is assigned on an ordinary column, and a table may use each role at most once.
| Role | Badge | Column type | Required | What it does |
|---|---|---|---|---|
| Quantity | QTY | Number | Yes | Holds the live amount on hand, in the unit set on the column. |
| Entity link | LINK | Reference | No | Ties each item to the catalog record it instantiates. |
| Expiration | EXP | Date | No | An expiry date per item, for stock that expires independently of its lot. |
| Label type | LBL | Text | No | The label template these items print with. |
Everything else on the table is an ordinary column with no role, and there is no limit on those. Concentration, supplier, catalogue number and freezer notes are all just columns.
There is no unit column. The quantity column carries the item type's unit, picked
from the platform's unit registry (µL, mg, vials and any compound such as mg/mL);
any spelling you type is canonicalized, and a typo is rejected rather than guessed
at. A unit the registry does not know can still be kept as a custom unit: it is
stored and displayed exactly as written, but it never converts or rescales, so use
it only where no real unit fits. Stock that genuinely mixes units, liquids in µL next to
solids in mg, ticks Allow multiple units on that column and lists the choices;
each item then picks one of them when it is created, and queries read it as _unit.
Compatible units convert: consuming 0.5 mL from a 500 µL vial just works.
Building one
- Open the environment designer
Item types are designed under Data, not under Inventory. Open the environment you want the stock to live in.
- Add an inventory table
Use Add Inventory Table. It arrives pre-seeded with Name, Quantity (in µL), Entity link and Label type, because a table without a quantity column that carries a unit cannot be committed. Rename it to something concrete like
sample_stock. - Set the roles you need
Open any column and pick its Role. Delete the seeded columns you do not want, and add ordinary columns for whatever else you track.
- Point the entity link at a catalog table
On the Entity link row, use the reference chip to pick the table these items are stock of, e.g. Test Articles. An entity link with no target table is rejected at commit.
- Set the SKU pattern
The table's display pattern mints each item's SKU, exactly like a naming scheme mints
SMP-024for a sample. The default isITEM-{###}. - Commit
Like any schema change, the design is a draft until you commit it. The commit validates the roles.
What the commit checks
A commit is rejected if a table's roles do not add up, and the error names the table and the offending roles. The rules are:
- Exactly one quantity column, and it must be a number whose unit is a registry code (its allowed units, when set, must include that default).
- At most one each of entity link, expiration and label type.
- An entity link must name a concrete target table.
- Inventory tables are workspace-wide, and a table cannot be converted into or out of the inventory kind after it is created.
Permissions
Designing an inventory table needs two permissions: the usual right to change
schema, plus MANAGE_INVENTORY_STRUCTURE. Someone who can design entity tables
but does not administer inventory cannot quietly redefine what a vial is. See
roles and permissions.
Quantity is managed, and that is on purpose
Once the table is committed, the quantity cell (the amount together with the
item's unit) stops behaving like an ordinary cell. Its opening value is set when
the item row is created, by item create, bulk create, or an import that inserts
new rows: that is the receiving act. From then on the generic write paths refuse them with
INVENTORY_MANAGED_COLUMN, so the data grid, the data API and an import that
syncs onto matched rows all bounce. Quantity moves only through the inventory
operations that keep an audit trail: Log Usage, Adjust, and the batch flows in
receiving and consuming inventory.
Every other column, including entity link, expiration and label type, stays freely editable. A label type cell is checked when you update a single item row: it must name a label template that exists in the workspace and prints items. The create and bulk-update paths do not run that check.
Querying stock
Because item types are tables, inventory is a first-class citizen in saved queries. Select and filter their columns like any entity table, join them to the catalog table through the entity link, and aggregate quantities.
Custody lives outside the table, so it is exposed through reserved fields you can filter and group on:
- _status
- available, placed, checked_out, staged, discarded, archived or conflict.
- _quantity_state
- in_stock, low_stock, depleted or untracked.
- _container_id
- The uuid of the container the item is placed in.
- _container_name
- The container the item currently sits in.
- _position
- Its position inside that container, e.g. A3.
- _lot_number
- The lot the item belongs to.
- _lot_id
- The uuid of that lot.
- _location
- Filter only. Matches the container or any container above it.
discarded is reserved in the status enum and nothing sets it: the Discard
action archives the item, so a thrown-away vial reads archived.
"Total millilitres of DLA-7 on hand, excluding archived vials" is one grouped query against the stock table, which was not expressible before item types became tables.