Mirror all SPECs and construction docs from pizza-digital, add hub/sidebar links and bidirectional sync scripts.
8.5 KiB
SPEC — Estoque (Insumos & CMV)
| Campo | Valor |
|---|---|
| Produto | Pizza Digital |
| Capítulo | SPEC §7.9 RF-EST (novo) |
| Versão | 0.1.0 |
| Data | 2026-10-19 |
| Owner | Roger / iTecnologys |
| SPEC irmã (obrigatória) | SPEC-CARDAPIO.md — menu + ficha técnica |
| Dispara baixa | Order confirmado → em_producao (Central / PDV / canais) |
1. Objetivo
Controlar o que a cozinha tem (insumos e embalagens), amarrado ao o que a loja vende (cardápio).
Sem estoque ligado ao cardápio: pedido vende o que não existe → ruptura, CMV cego, pausa manual.
Compra / ajuste ──► Ingredient.stockQty
│
Pedido (OrderItem) ──BOM──┤
▼
StockMovement (saída)
│
▼
se qty < min → MenuItem paused
2. Escopo
Inclui
- Cadastro de insumos (
Ingredient) com unidade e saldo - Ficha técnica (
Recipe) porMenuItem(+ tamanho opcional) - Movimentos: entrada (compra), saída (pedido), ajuste, perda
- Alerta mínimo / pausa automática no cardápio
- Relatório CMV simples (custo teórico por pedido)
- SKU 1:1 para bebidas prontas (refri, energético)
Não inclui (fases posteriores)
- NFe XML automática / SPED
- Multi-depósito / transferência entre lojas
- WMS / código de barras avançado
- Produção de massa com OP industrial
3. Modelo de dados
Ingredient
id, storeId, name, sku?
unit ← g | ml | un | kg | L
stockQty ← Decimal
minQty ← alerta
costCents ← custo médio unitário (na unidade base)
active
Recipe
id, storeId, menuItemId
sizeCode? ← broto|media|grande|gigante|null (= preço base)
yieldNote? ← "1 pizza G"
lines: RecipeLine[]
RecipeLine
id, recipeId, ingredientId
qty ← quantidade na unidade do Ingredient
wasteFactor? ← 1.05 = 5% perda
StockMovement
id, storeId, ingredientId
kind ← purchase|sale|adjust|waste|cancel_restore
qty ← sinal: + entrada / − saída
unitCostCents?
refType ← order|orderItem|manual|purchase
refId?
note?, userId?, createdAt
MenuItem.stockMode ← bom | sku | none (campo no Cardápio)
MenuItem.skuIngredientId? ← quando stockMode=sku
4. Ponte com Cardápio
Contrato: todo item vendável com impacto de estoque declara
stockModena SPEC-CARDAPIO.
stockMode |
Baixa |
|---|---|
bom |
Soma RecipeLine.qty × OrderItem.quantity (meia-a-meia: 0,5 + 0,5) |
sku |
−1 un do skuIngredientId por unidade vendida |
none |
Sem movimento |
Momento da baixa (política piloto)
| Momento | Ação |
|---|---|
order → em_producao |
Baixa (sale) — padrão piloto |
order → cancelado e ainda não saiu da cozinha |
Estorno (cancel_restore) |
Após pronto |
Cancelamento não devolve (perda / waste opcional) |
Alternativa futura: reserva no confirmado + baixa no pronto.
Exemplos Joinville
Calabresa G (bom) — pesos em g (fonte: FICHAS-TECNICAS-PESO.md):
| Insumo | Qty |
|---|---|
| Massa (disco) | 480 g |
| Molho tomate | 90 g |
| Mussarela | 200 g |
| Calabresa fatiada | 120 g |
| Cebola | 40 g |
| Orégano | 2 g |
| Peso montado ≈ | 930 g |
Coca-Cola 2L (sku): −1 un ingredient:coca-2l
Vitamina Morango (bom):
| Insumo | Qty |
|---|---|
| Leite integral | 300 ml |
| Morango | 100 g |
| Açúcar | 20 g |
| Gelo | 100 g |
4.1 Quantização por peso — pizzas (obrigatório)
Catálogo completo (38 tipos):
data/fichas-pizzas-peso.json· resumoFICHAS-TECNICAS-PESO.md
| Regra | Valor |
|---|---|
| Unidade canônica toppings/massa | gramas (g) |
| Base Grande 40 cm | massa 480 · molho 90 · mussarela 200 |
| Base Broto 25 cm | massa 200 · molho 40 · mussarela 85 (≈ 0,42× G) |
| Baixa | Σ (line.qty_g × quantity) |
| Meia-a-meia | massa/molho 100% · toppings 50% de cada sabor |
| Cobertura piloto | 38 pizzas (20 trad. + 5 broto + 6 especiais SC + 7 doces) |
Cada tipo de pizza tem ficha própria — não compartilhar um único “peso médio”. Ajustes por loja na UI sem quebrar o JSON seed.
5. Requisitos (RF-EST)
| ID | Requisito | Track |
|---|---|---|
| RF-EST-01 | CRUD insumos (nome, unidade, min, custo) | E1 |
| RF-EST-02 | Ficha técnica por MenuItem (+ size) | E1 |
| RF-EST-03 | Baixa automática no pedido (política §4) | E1 |
| RF-EST-04 | Entrada por compra / ajuste manual | E1 |
| RF-EST-05 | Alerta stockQty ≤ minQty (WS + UI gestor) |
E1 |
| RF-EST-06 | Pausa automática MenuItem se insumo crítico zerar | E2 |
| RF-EST-07 | Estorno em cancelamento elegível | E1 |
| RF-EST-08 | Relatório CMV teórico por dia / item | E2 |
| RF-EST-09 | Inventário físico (contagem → ajuste) | E2 |
| RF-EST-10 | Perda / desperdício com motivo | E2 |
| RF-EST-11 | Bloquear venda se strictStock=true e saldo insuficiente |
E2 |
| RF-EST-12 | Auditoria: todo movimento com user + ref | E1 |
| RF-EST-13 | Ficha por tipo de pizza com qty em gramas (38+ SKUs menu) | E1 |
| RF-EST-14 | Importar/versionar catálogo fichas-pizzas-peso.json |
E1 |
| RF-EST-15 | Escala Broto/Grande/Gigante por fator ou ficha dedicada | E1 |
6. Eventos
stock.low → store:{id} (ingredientId, qty, minQty)
stock.updated → store:{id}
menu.item.paused → store:{id} (menuItemId, reason=stock)
menu.item.resumed → store:{id}
PDV / PWA escutam menu.item.paused|resumed (SPEC-CARDAPIO RF-MEN-13).
7. Regras de negócio
- Unidade canônica por insumo; conversões (kg↔g) só na UI de entrada.
- Custo médio móvel em compras: atualiza
Ingredient.costCents. - Meia-a-meia: duas half-recipes; se faltar um sabor, cardápio bloqueia o par.
- Bebidas
sku: não passam por montagem/forno; só estaçãobebidas+ expedição. - Vitaminas/sucos naturais: baixa no mesmo momento do pedido; se fruta acabou → pausa só o sabor.
strictStock(config loja): default false no piloto (alerta sem bloquear); true em produção madura.
8. Track E — implementação
| ID | Entrega | Dependência |
|---|---|---|
| E0 | Este SPEC + link no SPEC.md / Cardápio | ✅ agora |
| E1 | Prisma Ingredient/Recipe/Movement + baixa no create/transition | feito 2026-10-20 |
| E2 | Pausa auto + CMV + inventário UI | E1 · Central estável |
| E3 | Compras com custo médio + multi-loja | Fase 3 |
Ordem sugerida com Cardápio: M0 (menu Joinville) → E1 → M1/M2 (admin ficha) → E2.
9. Critérios de aceite
- Cadastro de mussarela / calabresa / Coca 2L com saldo e mínimo.
- Ficha Calabresa G salva e listável a partir do MenuItem.
- Criar pedido com 1× Calabresa G reduz mussarela em 200 g (ou qty da ficha).
- Criar pedido com 1× Coca 2L reduz 1 un do SKU.
- Cancelar pedido em produção devolve saldo.
- Mussarela em 0 → itens BOM que usam mussarela pausam (E2) ou alerta (E1).
- Movimentos auditáveis com
refType=order.
10. UI alvo (gestor)
| Tela | Rota (proposta) | Função |
|---|---|---|
| Insumos | /estoque |
Lista + saldo + min |
| Ficha técnica | /cardapio/:itemId/ficha |
BOM editor |
| Movimentos | /estoque/movimentos |
Extrato |
| Inventário | /estoque/inventario |
Contagem |
Papel: gestor (escrita) · pdv (leitura alertas).
11. Fora de escopo
- Integração contábil completa
- Validade / lote FEFO (fase futura)
- App de compras do fornecedor
12. Glossário cruzado
| Termo | Onde |
|---|---|
| MenuItem | SPEC-CARDAPIO — o que vende |
| Recipe / BOM | Esta SPEC — como consome |
| Ingredient | Esta SPEC — o que tem |
| stockMode | Cardápio declara · Estoque executa |
| Order | SPEC principal / Central — dispara baixa |
SPEC satélite. Versão do produto pai: SPEC.md. Cruzamento obrigatório com SPEC-CARDAPIO.
13. Implementação E1 (2026-10-20)
Entregue: stockQty · StockMovement · strictStock · /admin/estoque · baixa em produção · estorno no cancelamento · RF-EST-11.
strictStock default false (piloto). Gestor liga em /admin/estoque para bloquear pedido sem saldo.