Variants — Reference
The Variants surface is the per-SKU plane for SGEN commerce products. It owns the relationship between a parent product and the concrete sellable units beneath it — size, color, material, or any other axis a product is sold along — together with the per-variant inventory and price that distinguish them.
This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or scoping a catalog import. Customer-facing how-tos live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
Variants lives under the Products module in SG-Admin, nested inside the product editor. The module renders three primary views — the variant table on the parent product, the per-variant detail form, and the variant-axis editor — and exposes a small set of write operations for create, update, archive, restock, and reprice.
The module is paired by convention: one half renders the views and prepares the parent-and-children data, the other half handles writes and returns AJAX responses. Engineers reading the SG-Admin source will see this split across two controller files; the reference below describes the combined surface as it appears to a calling integration.
A parent product without variants behaves as a single sellable unit. A parent product with variants becomes a non-sellable shell — every purchase passes through one of its child variants, which carry the actual SKU, inventory count, and price. The parent retains shared content (title, description, media) so variants do not duplicate it.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Products → (open product) → Variants tab
- URL prefix:
/sg-admin/variants/ - View templates:
application/views/Admin/Products/Variants/
┌──────────────────────────────────────────────────────────────────────┐│ Product: Field Tee [ Variants tab ] │├──────────────────────────────────────────────────────────────────────┤│ Axes: Size (S, M, L, XL) · Color (Sand, Charcoal, Navy) ││ ││ SKU Size Color Price Stock ││ ───────────────── ───── ───────── ────────── ──────────────────││ FT-S-SAND S Sand $32.00 42 in stock ││ FT-S-CHARCOAL S Charcoal $32.00 18 in stock ││ FT-M-SAND M Sand $32.00 6 (low stock) ││ FT-M-NAVY M Navy $34.00 0 (out of stock) ││ FT-L-CHARCOAL L Charcoal $32.00 archived ││ FT-XL-SAND XL Sand $34.00 12 in stock ││ ││ [ + Add variant ] [ Edit axes ] [ Bulk restock ] [ Bulk reprice ]│└──────────────────────────────────────────────────────────────────────┘Actions
The Variants surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List variants on a product
Returns the variant set for a parent product — one row per variant with SKU, axis values, price, inventory count, and status. Driven by the data-table contract used across SG-Admin modules. The view distinguishes active variants from archived; archived variants remain attached to the parent for historical attribution but are hidden from the storefront.
Edit axes
Sets the list of axes that distinguish variants on a parent product — typically one to three axes such as size, color, material. Each axis carries a name and a fixed list of allowed values. The axis editor is the upstream of the variant create flow — adding an axis or an axis value enlarges the matrix of variants the parent can carry.
Create variant
Adds a sellable child under a parent product. Required fields: SKU (unique site-wide), an axis value for each declared axis, price, and initial inventory. Optional fields: variant-specific media, weight, dimensions, barcode, supplier reference. The SKU uniqueness check spans the catalog, not the parent.
Create variant matrix
Bulk path used after axis edits. Generates the complete cartesian product of axis values into draft variants, with shared default price and zero inventory. Operators edit the drafts in place to set per-variant pricing and stock before publishing. Draft variants are not yet sellable.
Edit variant
Loads a variant into the detail form pre-populated. Submit replaces the stored variant with the posted values. Axis values are read-only post-creation; moving a variant to a different axis combination requires archiving the current variant and creating a new one to preserve historical order references.
Restock variant
A dedicated write path for inventory. Accepts a target count and a reason. Writes the new count, records the delta in the inventory ledger with the reason, and emits a Restock event. Used both for receiving new stock and for periodic counts.
Bulk restock
Same shape as restock, scoped to a multi-select on the variant list. Each affected variant gets its own ledger entry and event.
Reprice variant
Writes a new price. The prior price is retained in the price history for the variant. Effective immediately on the storefront.
Bulk reprice
Same shape, scoped to a multi-select. Supports flat-amount and percentage shifts. Each variant gets a price-history entry.
Archive variant
Marks the variant inactive without removing it. Archived variants disappear from storefront listings and from the cart's add path, but remain attached to the parent for historical reporting — past orders, sales reports, and the inventory ledger continue to resolve them.
Restore variant
Reverses an archive. The variant returns to the active set with its prior axis values, price, and inventory intact.
Permanent delete
Hard-removes a variant. Available only after archive, only when the variant has zero past order references. The presence of any historical sale blocks permanent delete — archive is the terminal state for previously-sold variants.
Data model
The Variants surface spans three related records — the parent product, the variant, and the axis definition that joins them.
Variant record:
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
product_id | integer | Foreign key to parent product. Cannot change after creation. |
sku | string | Unique site-wide. Editable with care — past orders carry historical SKU. |
axis_values | object | Map of axis name to selected value. Read-only after creation. |
price | decimal | Per-variant. May differ from parent default. |
compare_at_price | decimal | Optional. Used for sale-price displays. |
inventory_count | integer | Current sellable stock. Updated by restock and order events. |
inventory_policy | enum | track, track-allow-oversell, untracked. |
weight | decimal | Optional. Shipping calculations. |
dimensions | object | Optional. L × W × H. |
barcode | string | Optional. UPC, EAN, ISBN, etc. |
media | array | Optional. Variant-specific image references; falls back to parent media when empty. |
status | enum | active, archived, draft. |
created_at | timestamp | Set on create, immutable. |
updated_at | timestamp | Touched on any edit. |
| Field | Type | Notes |
|---|---|---|
name | string | Axis name shown in the storefront and admin. |
values | array | Fixed list. Adding a value is non-destructive; removing a value requires no active variant references it. |
display_order | integer | Order in which axes appear in the storefront selector. |
| Field | Type | Notes |
|---|---|---|
variant_id | integer | Foreign key. |
delta | integer | Signed. Positive for restock, negative for orders. |
reason | string | Free-text label or order reference. |
operator_id | integer | Optional. Set when the change was operator-driven. |
created_at | timestamp | Set on insert, immutable. |
PARENT PRODUCT├── id, title, description, media (shared)├── axes[]│ ├── { name: "Size", values: ["S","M","L","XL"] }│ └── { name: "Color", values: ["Sand","Charcoal","Navy"] }│└── variants[]├── VARIANT sku=FT-S-SAND axis_values={Size:S, Color:Sand} price, stock├── VARIANT sku=FT-M-NAVY axis_values={Size:M, Color:Navy} price, stock└── VARIANT...↓ each variantINVENTORY LEDGER(delta · reason · timestamp)Permissions
Access to the Variants surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Variants surface.
Layer 2 — per-action capability. Within SG-Admin, each Variants action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:
| Capability | Administrator | Editor | Viewer |
|---|---|---|---|
| List variants | ✔ | ✔ | ✔ |
| Edit axes | ✔ | ✔ | — |
| Create variant | ✔ | ✔ | — |
| Create variant matrix | ✔ | ✔ | — |
| Edit variant | ✔ | ✔ | — |
| Restock variant | ✔ | ✔ | — |
| Bulk restock | ✔ | — | — |
| Reprice variant | ✔ | ✔ | — |
| Bulk reprice | ✔ | — | — |
| Archive variant | ✔ | — | — |
| Restore variant | ✔ | — | — |
| Permanent delete | ✔ | — | — |
Referential rules. Permanent delete is blocked when the variant has any historical order reference. Axis-value removal is blocked when any active variant references the value. SKU edits are allowed but warned — past orders retain the historical SKU on the order line, so renaming live affects future fulfilment paperwork only.
Audit. Every write — variant create, edit, restock, reprice, archive, restore, delete, axis edit — emits an Activity Log entry. The log records the acting operator, the target variant or axis, and the change shape. Inventory deltas additionally write to the dedicated inventory ledger described above, which is queryable independently of the Activity Log.
VARIANT OPERATION REQUEST│▼┌───────────────────────────┐│ Admin gate │ unauth → reject└─────────────┬─────────────┘│ authed▼┌───────────────────────────┐│ Capability check │ role lacks cap → reject│ (per action) │└─────────────┬─────────────┘│ allowed▼┌───────────────────────────┐│ Referential rules │ permanent delete with orders → reject│ │ axis value with variants → reject└─────────────┬─────────────┘│ passes▼action executes│▼Activity Log entry + Inventory ledger (where applicable)Related references
- Products — Reference. Owns the parent product record. Shared content (title, description, media) lives there; variants inherit from it.
- Inventory — Reference. Aggregates per-variant inventory ledger entries into stock counts, low-stock alerts, and movement reports.
- Orders — Reference. Order lines carry the variant identifier and the historical SKU at time of purchase. Variant edits do not retroactively change order records.
- Pricing — Reference. Per-variant price overrides interact with site-wide price rules (sale schedules, customer-group pricing) defined here.
- Media — Reference. Variant-specific media references resolve against the same Media library used by parent products.
- Reports — Reference. Sales-by-variant and stock-on-hand reports read directly from the variant record and its inventory ledger.
/docs/products/variants.