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.

RoleBadgeColumn typeRequiredWhat it does
QuantityQTYNumberYesHolds the live amount on hand, in the unit set on the column.
Entity linkLINKReferenceNoTies each item to the catalog record it instantiates.
ExpirationEXPDateNoAn expiry date per item, for stock that expires independently of its lot.
Label typeLBLTextNoThe 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.

The unit lives on the quantity column

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

  1. Open the environment designer

    Item types are designed under Data, not under Inventory. Open the environment you want the stock to live in.

  2. 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.

  3. 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.

  4. 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.

  5. Set the SKU pattern

    The table's display pattern mints each item's SKU, exactly like a naming scheme mints SMP-024 for a sample. The default is ITEM-{###}.

  6. 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.

What's next