Skip to main content

How a product flows through the system

A product is the anchor every other record in SKU.io points back to. It's created once, guaranteed a unit of measure, then referenced — never copied — by sales orders, purchase orders, warehouse transfers, and assemblies. Those references are what make a product used, and being used is what decides whether it can ever be deleted.

This page explains that lifecycle end to end: what happens the moment a product is created, how each downstream document links to it, how a sold or ordered product resists deletion, and how pricing tiers turn a bare order line into a real price. Stock numbers, opening balances, and FIFO layers are the Inventory guide's job — this page shows only where the product plugs into them, not how the ledger itself works.

The lifecycle at a glance

The two states that matter most are Referenced and its consequence, Archived. A product that has ever been touched by an order, movement, or assembly can't be deleted — the system archives it instead, preserving the history those documents depend on.

Create: What SKU.io guarantees

When you save a new product (see Create a product), three guarantees fire before it's usable:

  • A SKU is mandatory. Saving a new product with an empty sku throws an error — the SKU is the product's stable identity across every downstream document.
  • Name defaults to the SKU. If you leave the name blank on creation, SKU.io copies the SKU into the name so the product is never nameless.
  • Register visibility inherits a default. Unless you set it explicitly, Show in register is seeded from the auto-show new products in register product setting (defaulting to on), so new products appear in the POS register without extra work.

Immediately after the row is written, SKU.io ensures the product has a stock unit of measure. If none exists, it creates one — using your preferred stock UoM if the form supplied one, otherwise EACH, otherwise the first available unit. This is why every product has a working UoM even if you never open the units tab; you can refine it later in Configure a product's units of measure. Only the inventory-bearing types — standard, kit, blemished, and manufactured — will carry a stock ledger; bundle and matrix never hold inventory of their own (see Product types explained).

The create, edit, detail, import, and merge surfaces all route through one shared CRUD service, so the guarantees above hold no matter how a product enters the system — form, spreadsheet import, or merge.

Stock: Giving the product a quantity

A newly created product exists but holds nothing. You give it a starting quantity with an opening balance, which writes the first inventory movement and the first FIFO layer for a warehouse. That mechanism — opening balance, per-warehouse stock, min-stock levels, and FIFO layering — lives entirely in the Inventory guide; the product simply owns the relationships those records hang off. For the numbers a product surfaces (on-hand, available, per-warehouse breakdown, lots and expiry), see the product's inventory tab documentation in the Inventory guide.

Two planning fields — min stock level and manufacturing target stock days — live on the product record but carry their reorder and planning meaning on the inventory and manufacturing side; the catalog only stores them.

For composite types, availability is derived rather than stored. A kit's or bundle's potential inventory is the number of whole units you could build from component stock — the minimum across every component of component stock ÷ quantity per unit, with nested kits flattened so quantities multiply through the hierarchy. A standard product's potential inventory is just its own available stock. The composition side (which components, how many) is documented in Build bundles & kits in the Workshop and Kits vs bundles; the stock arithmetic itself belongs to inventory.

Sell: How sales orders reference a product

A product can appear on a sales order line in two distinct ways, and this is the single most important seam to understand:

  • As the line's product — an ordinary sold line, referencing the product directly.
  • As the line's bundle — when a bundle is sold, its parent is recorded on the line separately from the components it explodes into.

Both references are RESTRICT foreign keys in the database, meaning neither can be deleted while a line still points at it. The direct-product relationship covers the first case; a separate bundle relationship covers the second. This matters for deletability: a bundle sold as a parent looks deletable if you count only direct product references, but the database would block the actual delete. SKU.io counts both relationships when deciding whether a product is used, so a sold bundle is correctly archived rather than deleted (see Archive, unarchive & delete a product).

When a bundle sells, it doesn't carry revenue as a parent line — it expands into component lines and prorates its amount across them. That split is its own topic: Bundle pricing & revenue proration.

Pricing: How a bare order line gets a price

When a sales order line arrives without an explicit unit price, SKU.io resolves one through pricing tiers rather than falling back to a single catalog price. The resolution runs in two steps:

  1. Pick the tier. If the customer has a pricing tier assigned, that tier applies. Otherwise the site-wide default tier is used. (This same resolver serves sales orders, quotes, and POS checkout, so a customer is priced identically across all three.)
  2. Read the product's price in that tier. The line takes the product's price row for the chosen tier.

If the product has no price row in the resolved tier, resolution throws rather than silently guessing a price — an order line can't invent a number. So a product must be priced in every tier a customer might be resolved to. Set those prices in Set a product's price, and manage the tiers themselves in Manage pricing tiers.

Tiers can also be relative — derived from a base tier, a supplier tier, or an attribute rate/offset with rounding rules — rather than hand-entered. The default tier is the ultimate fallback, and archived tiers are excluded from resolution by default. The mechanics of derived pricing are covered in Relative pricing tiers and the guardrails in the Pricing tier reference.

Purchase: How purchase orders attribute incoming supply

Purchase order lines reference a product to record incoming supply — stock on the way in but not yet received. SKU.io attributes that unreceived quantity to a warehouse with a deliberate rule:

  • The unreceived portion of each open inbound shipment line lands in that shipment's destination warehouse.
  • Any remainder on the PO line not yet covered by a shipment falls back to the PO header's destination warehouse.

Only open inbound shipments count. Terminal shipments — received, closed, or cancelled — have already released their unreceived expected quantity back to the PO line, so counting them too would double-count phantom supply. Incoming totals are memoized per request so repeated reads within one page load stay consistent and cheap.

For deletability, any purchase order line referencing the product blocks a hard delete — the product is archived instead.

Move: How warehouse transfers reference a product

A warehouse transfer moves a product's stock between locations. While stock is mid-move, its quantity shows as in transit — the transfer quantity a product surfaces is the sum of its inventory movements whose status is in transit.

Transfer lines aren't in the explicit "is used" block list; instead, deletion protection for transferred products rides on the product's inventory movements — a transfer creates movements, and any inventory movement blocks a hard delete. The net effect is the same: a product that has ever moved between warehouses is archived, not deleted.

Assemble: How kits, BOMs, and manufacturing reference a product

Products link to other products through a self-referencing component relationship — a parent/component join carrying a per-component quantity. This one relationship powers both kits and bundles.

  • A kit holds its own stock and is assembled from components. Its component list stays editable throughout its life — inventory history doesn't lock it — and a save is only rejected (with a structured 422) when the component set itself is invalid: empty, containing a non-Standard/non-Kit product, self-referencing, or circular. (What inventory history does guard is the product type change — turning an inventory-bearing product into a stockless Bundle is blocked once it has sale/assembly movements. That check is deliberately narrow: it counts only sale and assembly movements, so a kit whose only history is a stock take still converts freely.) Build and edit kits in the Workshop; see the Kit & bundle component reference for the field-level rules.
  • A manufactured product is inventory-bearing and gated by a manufacturing-enabled flag plus its bills of materials (BOMs). The catalog only defines the type and the flag; the assemble/disassemble behavior, BOMs, and assembly lines belong to the Manufacturing area.

Assembly lines referencing a product also block a hard delete, so an assembled or component product is archived rather than removed.

Delete vs archive: Why used products persist

Deletion is the lifecycle's most important cross-system rule, because it enforces that history is never silently destroyed. When you delete a product, SKU.io first checks whether it's used — referenced by any of a long list of RESTRICT foreign keys that a delete would otherwise trip over:

sales order lines (direct and as a bundle parent), purchase order lines, inventory movements, FIFO layers, inventory adjustments, assembly lines, warehouse transfer lines, return-receipt lines, RMA lines, sales-credit lines, derived blemished products, inbound-shipment lines, and subscription editions — and the same list is checked on every variant child, so a used variation protects its parent.

If any of those exist, the product (and its variants) is archived — hidden but intact — instead of deleted, and the delete call returns the list of what's holding it. Only a genuinely unreferenced product is hard-deleted, and even then the cascade of child cleanup (attributes, components, listings, pricing, supplier links, images, variations, inventory, and Amazon-owned FIFO/mapping cleanup) plus the final delete run inside a single transaction — so if a RESTRICT key the pre-check didn't cover still blocks the delete, the whole thing rolls back rather than leaving a half-stripped, orphaned product.

This is why the Delete control in the UI is truthful: it reflects the same used-check, so a product it offers to delete really will delete, and one that's used is offered as an archive. See Archive, unarchive & delete a product and its bulk equivalent.

Report: The product as a reporting anchor

Once a product accrues movements, FIFO layers, and financial lines, it feeds dashboards, profitability, and forecasting — always by reference, never by copy. Those reporting seams (channels, sales-history windows, benchmarks) are their own topic: see Products, channels & reporting (seams).

Next steps

Last verified: