AstroBaaS

Catalogue & product data

Variant Management

Free — GPL coresize Lplanned, not built

Generated from docs/plan/core/variant-management/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

Core feature for managing product variants (size, color, material). Merchants with multi-option products (e.g., T-shirts in S/M/L × Red/Blue) can define options, create variant combinations, and set per-variant pricing, stock, images, and SKU. This is foundational; variant picker UI is a presentation layer concern.

The problem

Merchants with size/color variants must create separate products; they triple their workload.

What it does

  • Define variant attributes: for a product, list the attributes (Size, Color, Material) and their allowed values
  • Variant combinations: show matrix of all possible option combinations; merchant selects which are valid and in stock
  • Per-variant stock: set qty for each variant independently (e.g., S-Red: 50, S-Blue: 20, L-Red: 100)
  • Per-variant pricing: option to override product price for specific variants (e.g., XL size costs +€5 premium)
  • Per-variant SKU: assign a unique SKU to each variant for fulfillment (e.g., SHIRT-S-RED)
  • Per-variant images: assign a unique image to each variant (e.g., show shirt in red color when Red variant selected)
  • Per-variant GTIN/UPC: assign barcode to each variant for inventory tracking
  • Variant-specific weight: option to set weight per variant (e.g., heavier materials)
  • Variant enable/disable: toggle variant on/off without deleting (e.g., hide S-Red but keep data)
  • Bulk variant creation: generate all combinations from attributes (Size × Color), then prune invalid ones
  • Variant reordering: reorder variants in the list (affects display order in picker)
  • Variant duplication: clone a variant’s settings to create a similar one (e.g., copy S-Red to M-Red, adjust stock)
  • Import variants from CSV: bulk import variant data (options, SKU, price, stock, image)
  • Export variants: CSV export with all variant data for analysis or re-import

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • Visual variant selection (swatches, 3D views) — that’s presentation layer (storefront); core provides option data only
  • Automatic price calculation based on attribute cost — merchant enters variant price manually
  • Inventory forecasting or reorder suggestions — that’s analytics; core stores stock levels only
  • Variant-specific compliance or safety data — use product-level metadata; variants don’t have GPSR fields
  • Variant-specific shipping costs or dimensions — variants inherit product shipping; per-variant overrides are out of scope

Data model

ProductVariant entity with fields: id (string, auto-generated), product_id (foreign key), options (JSON map of attribute name → chosen value, e.g., {size: ‘L’, color: ‘red’}), sku (string, nullable), gtin (string, nullable), price_cents (nullable; if null, inherits product.price_cents), regular_price_cents (nullable), sale_price_cents (nullable), stock (nullable; null means untracked), in_stock (boolean, derived from stock), weight_grams (nullable), image (string, optional variant-specific image), enabled (boolean, default true), created_at, updated_at. Also add product.attributes field (array of {name: string, values: string[], visible: boolean}) to define available options.

API

  • GET /products/:id/attributes — list variant attributes (Size, Color, etc.) for the product
  • POST /products/:id/attributes — add a new attribute to the product
  • PUT /products/:id/attributes/:attr_name — update attribute values
  • DELETE /products/:id/attributes/:attr_name — remove an attribute (and all variants using it)
  • GET /products/:id/variants — list all variants with options, stock, pricing, images
  • POST /products/:id/variants — create a new variant with options, stock, price
  • PUT /products/:id/variants/:variant_id — update variant (price, stock, image, enabled)
  • DELETE /products/:id/variants/:variant_id — remove a variant
  • POST /products/:id/variants/generate — generate all possible attribute combinations
  • POST /products/:id/variants/import — bulk import variants from CSV
  • GET /products/:id/variants/export — export variants as CSV

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Variant management section in product edit form: (1) Attributes section (list attributes, add new, edit values, remove), (2) Variants matrix view (table with option combinations × columns for SKU, stock, price, image, enabled), (3) Bulk generate button (create all combos from attributes), (4) Variant edit modal (detailed form for SKU, pricing, stock, weight, image, enabled), (5) Import/export buttons (CSV), (6) Reorder variants (drag-drop or up/down buttons).

The seam — why this is core

Core owns the variant schema, CRUD API, and admin UI. Storefront (Next.js) owns the picker UI that renders variant options to customers. Core returns raw variant data; storefront decides visual presentation.

Core owns the interface + honest variant editor; product variants are core infrastructure, not a support commitment or credential.

Dependencies

  • product-management (core; variants are part of the product model)
  • inventory-system (core; variant stock tracking)
  • pricing-system (core; variant-level pricing)

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • A product with attributes Size: [S, M, L] and Color: [Red, Blue] has POST /products/:id/variants/generate creating 6 variants (S-Red, S-Blue, M-Red, M-Blue, L-Red, L-Blue).
  • Setting SHIRT-L-Red.price_cents=1800 (product price=1500) shows the variant price when selected in the storefront.
  • Toggling enabled=false on a variant hides it from the picker, but historical orders containing that variant still reference it correctly.
  • Reassigning image=‘shirt-blue.jpg’ to the M-Blue variant causes the storefront to swap to that image when M-Blue is selected.
  • A variant with stock=0 and in_stock=false cannot be selected in the picker; the option is disabled.
  • Deleting an attribute (Size) cascade-deletes all variants using it; variants for Color alone remain.
  • Exporting variants as CSV includes variant_id, options (size;color), sku, price_cents, stock, image, enabled columns.
  • Importing variants from CSV with 10 new records shows preview before commit; validation rejects duplicate option combos.

Risks

If a merchant deletes an attribute used by existing variants, all variants are cascade-deleted and order history becomes inconsistent. If variant pricing is not clearly marked as an override, merchants forget and accidentally charge wrong prices. If stock is tracked per-variant but the picker shows combined stock, customers see conflicting info. If variant IDs are regenerated on every save, historical orders can’t link to variants.

Commercial context

Suggested pricefree (core)
Rival anchorShopify: included; Magento: included

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and ../../AI-GUIDE.md for how to work on this repository.