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
| Regra | Significado |
|---|---|
| Unidade base | Quantidades em movimento e em lote usam sempre a unidade base definida no produto. |
| Variante e saldo | Variante descreve SKU/embalagem e fator de conversão; não constitui saldo separado — o stock agrega ao produto. |
| Serial | Identidade: par (ProductId, Serial); não há unicidade global do texto do serial. |
| Lote | Código de lote único por produto: (ProductId, Code). |
| Barcode | Valor 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.
| Atributo | Papel |
|---|---|
ProductId | Produto base. |
Code | Código SKU. |
Volume | Quantidade/volume associado à apresentação comercial. |
UnitOfMeasure | Unidade do Volume. |
ConversionFactorToBase | Multiplicador: quantidade na base = quantidade informada × fator (quando a movimentação referencia esta variante). |
TrackSerial | Opcional; indica intenção de rastrear unidades por serial nesta variante. |
IsActive | Variante utilizável. |
Cardinalidade: um produto, muitas variantes; cada variante, muitos barcodes; opcionalmente muitos seriais.
ProductVariantBarcode
| Atributo | Papel |
|---|---|
ProductVariantId | Variante. |
Barcode | Valor; único globalmente. |
Type | Tipo de código (opcional). |
IsPrimary | Marca o código principal da variante. |
IsActive | Código ativo. |
SerialNumber
| Atributo | Papel |
|---|---|
ProductId | Produto (obrigatório). |
ProductVariantId | Variante (opcional). |
LotId | Lote do mesmo produto (opcional). |
Serial | Valor; unicidade com ProductId. |
Status | Ciclo de vida (ex.: disponível, reservado, consumido, vendido, bloqueado). |
ManufactureDate / ExpirationDate | Opcionais. |
IsActive | Registo 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
| Atributo | Papel |
|---|---|
ProductId | Produto movimentado. |
LotId | Lote (opcional). |
ProductVariantId | Variante 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). |
Quantity | Sempre na unidade base do produto. |
TrackSerial | Quando 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 ×ConversionFactorToBaseda variante.
Movimentação: serial
Quando TrackSerial é verdadeiro no item (incluindo efeito da variante, se a regra de negócio assim o definir):
Quantityna 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
StockMovementItemSerialtem de coincidir comQuantity.
Persistência: índices e unicidade
| Entidade / tabela | Constraint ou índice |
|---|---|
| Lot | UNIQUE (ProductId, ProductVariantId, Code) |
| ProductVariantBarcode | UNIQUE (Barcode) |
| SerialNumber | UNIQUE (ProductId, Serial); índice (ProductId, Status) |
| StockMovementItem | Índice em ProductVariantId |
| StockMovementItemSerial | UNIQUE (StockMovementItemId, SerialNumberId); índice em SerialNumberId |
Exposição HTTP (contrato)
| Recurso | Rotas |
|---|---|
| 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 movimento | POST /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.