Saltar al contenido principal

Product Variants Module

Overview

The product-variants module manages the variant system for FlowPOS retail products. A product can have multiple sellable SKUs (variants) defined by combinations of option types and option values (e.g. Size=M + Color=Red). Up to 3 option dimensions are supported per product.

Domain Concepts

ConceptDescription
Option TypeA dimension axis (e.g. Size, Color, Material). Business-scoped, reusable across products.
Option ValueA specific choice within an option type (e.g. S, M, L for Size).
Product VariantA sellable SKU defined by a combination of option values. Carries its own barcode, price override, and inventory.
Variant LabelAuto-computed display string (e.g. "M / Red") from sorted option values.
Default VariantAuto-created placeholder variant for non-variant products. Removed when real variants are added.

Architecture

product-variants/
├── product-variants.module.ts # NestJS module definition
├── domain/
│ └── product-variants-repository.domain.ts # Repository port + domain interfaces
├── application/
│ └── product-variants.service.ts # Business logic / use cases
├── infrastructure/
│ └── product-variants.repository.ts # Kysely DB adapter
└── interfaces/
├── product-variants.controller.ts # HTTP routes
├── dtos/
│ ├── create-option-type.dto.ts
│ ├── update-option-type.dto.ts
│ ├── create-option-value.dto.ts
│ ├── update-option-value.dto.ts
│ ├── create-variant.dto.ts
│ ├── update-variant.dto.ts
│ ├── create-variants-bulk.dto.ts
│ └── assign-barcode.dto.ts
└── query/
├── list-option-types.query.ts
└── list-variants.query.ts

Dependencies: DatabaseModule, BarcodesModule

API Endpoints

Option Types

MethodPathDescription
GET/product-option-typesList option types with their values
POST/product-option-typesCreate an option type
PATCH/product-option-types/:idUpdate an option type
DELETE/product-option-types/:idDelete an option type (fails if values are in use)

Option Values

MethodPathDescription
GET/product-option-types/:typeId/valuesList values for an option type
POST/product-option-types/:typeId/valuesCreate an option value
PATCH/product-option-values/:idUpdate an option value
DELETE/product-option-values/:idDelete an option value (fails if assigned to variants)

Variants

MethodPathDescription
GET/products/:productId/variantsList variants for a product
POST/products/:productId/variantsCreate a single variant
POST/products/:productId/variants/bulkCreate variants in bulk
GET/product-variants/:idGet variant by ID
PATCH/product-variants/:idUpdate a variant
DELETE/product-variants/:idDelete a variant (fails if it has transaction history)

Barcode Management

MethodPathDescription
POST/product-variants/generate-barcodesGenerate barcodes for variants without one
GET/product-variants/lookupPOS lookup by SKU or barcode
PATCH/product-variants/:id/barcodeAssign a barcode to a variant
PATCH/product-variants/:id/barcode/retireRetire a variant's barcode
PATCH/product-variants/:id/barcode/reactivateReactivate a retired barcode

Key Use Cases

Bulk Variant Creation

When creating variants in bulk (POST /products/:productId/variants/bulk):

  1. Validates no duplicate SKUs or barcodes in the batch.
  2. Optionally auto-generates internal barcodes (generateMissingBarcodes: true).
  3. Removes the default variant placeholder (with markdown conflict check).
  4. Sets product.hasVariants = true.
  5. Runs in a single transaction for atomicity.

Barcode Lifecycle

Barcodes follow a lifecycle: unassignedactiveretiredactive (reactivated).

  • Assign: Validates format, checks cross-entity uniqueness, retires old barcode if present.
  • Retire: Marks assignment as retired in history; barcode value preserved on variant.
  • Reactivate: Checks availability and creates new assignment record.

Internal barcodes follow the format: FP-{BUSINESS_SHORT_CODE}-{SKU} (with -N suffix on collision).

Deletion Guards

  • Option types: Cannot delete if any values are assigned to existing variants.
  • Option values: Cannot delete if assigned to any variant.
  • Variants: Cannot delete if they have inventory or adjustment history; deactivate instead.
  • Default variant removal: Blocked if product has an active markdown wave.

Database Tables

  • product_option_type — Option type definitions (business-scoped)
  • product_option_value — Values within each option type
  • product_variant — Sellable SKU records
  • product_variant_option — Join table linking variants to their option value assignments
  • barcode_assignment — Barcode lifecycle history

Permissions

Uses PolicyResource.ProductVariant with standard CRUD actions (Create, Read, Update, Delete).