Pular para o conteúdo principal

Produto, variantes, serial e lote

Modelo de catálogo e movimentação de estoque: produto na unidade base, SKUs com conversão, códigos de barras por variante, números de série e lotes com unicidade no âmbito do produto.

erDiagram
Product ||--o{ ProductVariant : possui
Product ||--o{ SerialNumber : possui
Product ||--o{ Lot : possui
ProductVariant ||--o{ ProductVariantBarcode : possui
ProductVariant ||--o{ SerialNumber : opcional
Lot ||--o{ SerialNumber : opcional
StockMovementItem }o--|| Product : referencia
StockMovementItem }o--o| ProductVariant : opcional
StockMovementItem }o--o| Lot : opcional
StockMovementItem ||--o{ StockMovementItemSerial : quando serial
StockMovementItemSerial }o--|| SerialNumber : referencia

Princípios

RegraSignificado
Unidade baseQuantidades em movimento e em lote usam sempre a unidade base definida no produto.
Variante e saldoVariante descreve SKU/embalagem e fator de conversão; não constitui saldo separado — o stock agrega ao produto.
SerialIdentidade: par (ProductId, Serial); não há unicidade global do texto do serial.
LoteCódigo de lote único por produto: (ProductId, Code).
BarcodeValor de código de barras único em todo o sistema (uma ocorrência por variante).

Entidades

Product

Agregado de catálogo e referência da unidade base. Associações: variantes (ProductVariants), seriais (SerialNumbers).

ProductVariant

SKU com volume comercial e conversão para a base.

AtributoPapel
ProductIdProduto base.
CodeCódigo SKU.
VolumeQuantidade/volume associado à apresentação comercial.
UnitOfMeasureUnidade do Volume.
ConversionFactorToBaseMultiplicador: quantidade na base = quantidade informada × fator (quando a movimentação referencia esta variante).
TrackSerialOpcional; indica intenção de rastrear unidades por serial nesta variante.
IsActiveVariante utilizável.

Cardinalidade: um produto, muitas variantes; cada variante, muitos barcodes; opcionalmente muitos seriais.

ProductVariantBarcode

AtributoPapel
ProductVariantIdVariante.
BarcodeValor; único globalmente.
TypeTipo de código (opcional).
IsPrimaryMarca o código principal da variante.
IsActiveCódigo ativo.

SerialNumber

AtributoPapel
ProductIdProduto (obrigatório).
ProductVariantIdVariante (opcional).
LotIdLote do mesmo produto (opcional).
SerialValor; unicidade com ProductId.
StatusCiclo de vida (ex.: disponível, reservado, consumido, vendido, bloqueado).
ManufactureDate / ExpirationDateOpcionais.
IsActiveRegisto ativo.

Entre módulos de domínio: a entidade de serial no módulo Products não expõe coleção de vínculos a movimentos, para manter o módulo StockMovements como dependente de Products, e não o contrário.

Lot

Quantidade do lote em unidade base. Atributo opcional ProductVariantId: null para matéria-prima ou bulk; preenchido para lotes de SKU (envase). Unicidade persistida como (ProductId, ProductVariantId, Code) (ver Ordens de produção: bulk, envase e lote).

StockMovementItem

AtributoPapel
ProductIdProduto movimentado.
LotIdLote (opcional).
ProductVariantIdVariante usada na operação (opcional); quando preenchido, aplica-se conversão para preencher Quantity. Se LotId estiver preenchido, deve coincidir com Lot.ProductVariantId (ambos null ou o mesmo id).
QuantitySempre na unidade base do produto.
TrackSerialQuando verdadeiro, exige vínculos em StockMovementItemSerial coerentes com Quantity.

StockMovementItemSerial

Associa um item de movimento a um SerialNumber. Um mesmo par item+serial não se repete: (StockMovementItemId, SerialNumberId) único.


Movimentação: conversão

  • Sem variante no item: a quantidade informada grava-se diretamente em Quantity (já em base).
  • Com variante: Quantity = quantidade informada × ConversionFactorToBase da variante.

Movimentação: serial

Quando TrackSerial é verdadeiro no item (incluindo efeito da variante, se a regra de negócio assim o definir):

  • Quantity na base tem de corresponder a um número inteiro de unidades rastreadas.
  • Lista de seriais referenciados: mesma cardinalidade que essa quantidade, sem repetir identificadores.
  • Seriais têm de pertencer ao mesmo produto do item.
  • Na conclusão da movimentação, o número de linhas em StockMovementItemSerial tem de coincidir com Quantity.

Persistência: índices e unicidade

Entidade / tabelaConstraint ou índice
LotUNIQUE (ProductId, ProductVariantId, Code)
ProductVariantBarcodeUNIQUE (Barcode)
SerialNumberUNIQUE (ProductId, Serial); índice (ProductId, Status)
StockMovementItemÍndice em ProductVariantId
StockMovementItemSerialUNIQUE (StockMovementItemId, SerialNumberId); índice em SerialNumberId

Exposição HTTP (contrato)

RecursoRotas
Variantes e barcodes de um produto/api/products/{productId}/variants, .../variants/{variantId}/barcodes
Seriais/api/serial-numbers, /api/serial-numbers?productId=, /api/serial-numbers/{id}
Itens de movimentoPOST /api/stock-movements/{id}/items — corpo: productId, quantity, opcionalmente lotId, productVariantId, trackSerial, serialNumberIds

Operações sujeitas às políticas de permissão de stock da API.