197 lines
7.2 KiB
Markdown
197 lines
7.2 KiB
Markdown
# SPEC — Cardápio (Menu)
|
||
|
||
| Campo | Valor |
|
||
|-------|--------|
|
||
| **Produto** | Pizza Digital |
|
||
| **Capítulo** | SPEC §7.5 RF-MEN · domínio Menu* |
|
||
| **Versão** | 0.1.0 |
|
||
| **Data** | 2026-10-19 |
|
||
| **Owner** | Roger / iTecnologys |
|
||
| **Pesquisa regional** | [`CARDAPIO-JOINVILLE.md`](CARDAPIO-JOINVILLE.md) |
|
||
| **SPEC irmã (obrigatória)** | [`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md) — ficha técnica / BOM |
|
||
| **Canais** | [`SPEC-CANAIS-DIGITAIS.md`](SPEC-CANAIS-DIGITAIS.md) — PWA `/c/:slug` |
|
||
|
||
---
|
||
|
||
## 1. Objetivo
|
||
|
||
Ser a **fonte única da verdade** do que a loja vende: categorias, itens, preços, tamanhos, modifiers (borda/meia-a-meia) e mapeamento para KDS.
|
||
|
||
O cardápio **não guarda saldo**. Saldo, compra e baixa ficam no **Estoque**. A ponte é a **ficha técnica (BOM)**: cada item do menu consome 1+ insumos.
|
||
|
||
```text
|
||
Cardápio (o que vende) ──BOM──► Estoque (o que tem)
|
||
│ │
|
||
▼ ▼
|
||
PDV / PWA / iFood alerta / bloqueio / CMV
|
||
│
|
||
▼
|
||
Order → KDS
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Escopo
|
||
|
||
### Inclui
|
||
- Categorias e itens (CRUD gestor)
|
||
- Preços por tamanho (Broto / G / Gigante) ou SKU por tamanho
|
||
- Meia-a-meia, bordas, adicionais
|
||
- Map item → estações KDS + componentes (tags)
|
||
- Disponibilidade: ativo / pausado / esgotado (via Estoque)
|
||
- Cardápio Joinville piloto (tradicionais, especiais SC, doces, bebidas)
|
||
- Exposição ao PDV, Central, PWA público e (futuro) iFood
|
||
|
||
### Não inclui (Estoque)
|
||
- Quantidade em prateleira, NF de compra, inventário físico → [`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md)
|
||
|
||
---
|
||
|
||
## 3. Modelo de dados (alvo)
|
||
|
||
```text
|
||
MenuCategory
|
||
id, storeId, name, sortOrder, active, channelVisibility[]
|
||
|
||
MenuItem ← já existe (Prisma)
|
||
id, storeId, categoryId, name, description
|
||
priceCents ← preço base (ou ver Size)
|
||
halfHalf, active, sortOrder
|
||
stations[] · components[]
|
||
|
||
MenuItemSize (novo — Track M1)
|
||
id, menuItemId, sizeCode ← broto|media|grande|gigante
|
||
diameterCm, slices, maxFlavors
|
||
priceCents, active
|
||
|
||
MenuModifier (borda / adicional)
|
||
id, storeId, name, priceCents, kind ← borda|extra|bebida_combo
|
||
stationCodes[]
|
||
|
||
Recipe (ficha técnica) ← vive na SPEC-ESTOQUE, FK menuItemId
|
||
id, menuItemId, sizeCode?
|
||
lines[] → Ingredient + qty + unit
|
||
```
|
||
|
||
### Tamanhos padrão Joinville (congelar no piloto)
|
||
|
||
Referência de prato de mesa (~25 cm) + diâmetro / fatias comerciais SC:
|
||
|
||
| sizeCode | Nome UI | cm | Fatias | ≈ pratos | Sabores máx. (split) | Fator preço (base = Grande) |
|
||
|----------|---------|----|--------|----------|----------------------|------------------------------|
|
||
| `broto` | Broto | 25 | 4 | 1 | **1** | 0,55 |
|
||
| `pequena` | Pequena | 30 | 6 | 1,5 | **1** | 0,70 |
|
||
| `media` | Média | 35 | 8 | 2 | **2** | 0,85 |
|
||
| `grande` | Grande | 40 | 12 | 2,5 | **3** | 1,00 |
|
||
| `gigante` | Gigante | 45 | 16 | 3 | **4** | 1,20 |
|
||
| `familia` | Família | 50 | 20 | 3,5 | **4** | 1,35 |
|
||
|
||
**Regras de split:** Broto e Pequena = inteira (1 sabor). Média até 2. Grande até 3. Gigante e Família até 4. Preço = **maior sabor** × fator do tamanho (+ borda).
|
||
|
||
**Default comercial:** `grande` (40 cm). Catálogo em `GET /api/menu/sizes` e no sheet do PDV.
|
||
|
||
---
|
||
|
||
## 4. Categorias piloto (já no seed)
|
||
|
||
| # | Categoria | Papel |
|
||
|---|-----------|--------|
|
||
| 1 | Pizzas Tradicionais | Sabores · preço base Grande; tamanho no pedido |
|
||
| 2 | Especiais Joinville | Diferencial SC |
|
||
| 3 | Pizzas Doces | Pós-salgada |
|
||
| 4 | Refrigerantes | Acompanhamento |
|
||
| 5 | Sucos | Natural + caixa |
|
||
| 6 | Energéticos | Acompanhamento |
|
||
| 7 | Vitaminas · frutas c/ leite | Feitas na hora |
|
||
| 8 | Bordas e extras | Modifiers |
|
||
|
||
---
|
||
|
||
## 5. Requisitos (RF-MEN + extensão)
|
||
|
||
| ID | Requisito | Fase |
|
||
|----|-----------|------|
|
||
| RF-MEN-01 | CRUD categorias, itens, preços, adicionais | ✅ parcial |
|
||
| RF-MEN-02 | Mapear item → estações KDS | ✅ |
|
||
| RF-MEN-03 | SLA por categoria/item | Fase 1 |
|
||
| RF-MEN-04 | Cardápio digital público PWA | Track C1 |
|
||
| RF-MEN-05 | Meia-a-meia / combos | ✅ split multi-sabor por tamanho |
|
||
| RF-MEN-06 | Horário loja aberta/fechada | Track C1 |
|
||
| RF-MEN-07 | Áreas entrega + taxa | Track C1 |
|
||
| RF-MEN-08 | Tamanhos Broto→Família + preço por fator | ✅ PDV + `/menu/sizes` |
|
||
| RF-MEN-09 | Pausar item (manual ou por estoque baixo) | **M1** |
|
||
| RF-MEN-10 | Ficha técnica obrigatória p/ item produzido | **M1** → Estoque |
|
||
| RF-MEN-11 | Bebida 1:1 → 1 unidade de SKU estoque | **M1** |
|
||
| RF-MEN-12 | Vitamina/suco natural → BOM (fruta + leite + gelo) | **M1** |
|
||
| RF-MEN-13 | Sync disponibilidade → PWA/PDV ≤ 2 s | Track C1+M1 |
|
||
| RF-MEN-14 | Preço meia-a-meia = maior sabor (config) | ✅ regra |
|
||
| RF-MEN-15 | Cada pizza com BOM em **gramas** (catálogo Joinville 38) | **M1** ↔ EST-13 |
|
||
|
||
---
|
||
|
||
## 6. Ponte Cardápio ↔ Estoque
|
||
|
||
| Evento cardápio | Efeito estoque |
|
||
|-----------------|----------------|
|
||
| Criar/editar `MenuItem` produzido | Exigir `Recipe` (ou marcar `stockMode=none`) |
|
||
| Pedido `confirmado` / `em_producao` | Reserva ou baixa (política EST) |
|
||
| Pedido `cancelado` antes da produção | Estorna reserva |
|
||
| Saldo < `minStock` de insumo crítico | Item → `paused` ou badge “esgotado” |
|
||
| Compra / ajuste no Estoque | Recalcula disponibilidade do menu |
|
||
|
||
Detalhe de baixas, **pesos em g por tipo de pizza**, unidades e alertas:
|
||
**[`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md) §4–4.1** · [`FICHAS-TECNICAS-PESO.md`](FICHAS-TECNICAS-PESO.md) · [`data/fichas-pizzas-peso.json`](data/fichas-pizzas-peso.json).
|
||
|
||
### `stockMode` por item
|
||
|
||
| Modo | Uso | Exemplo |
|
||
|------|-----|---------|
|
||
| `bom` | Consome receita | Pizza Calabresa G |
|
||
| `sku` | 1 item = 1 unidade SKU | Coca 2L, Red Bull |
|
||
| `none` | Sem controle | Observação / taxa |
|
||
|
||
---
|
||
|
||
## 7. Regras de negócio
|
||
|
||
1. Item `active=false` ou `paused` **não aparece** no PDV/PWA (gestor ainda vê no admin).
|
||
2. Meia-a-meia: ambos sabores precisam estar disponíveis; baixa BOM proporcional (½ + ½) — ver Estoque.
|
||
3. Borda é `MenuModifier` com BOM própria (catupiry/cheddar).
|
||
4. Vitamina: sempre `stockMode=bom` (leite + fruta + gelo); se faltar fruta → pausa só aquele sabor.
|
||
5. Preços em **centavos BRL**; UI pt-BR (`formatBRL`).
|
||
|
||
---
|
||
|
||
## 8. Track M — implementação
|
||
|
||
| ID | Entrega | Dependência |
|
||
|----|---------|-------------|
|
||
| **M0** | Seed Joinville (pizzas + bebidas) no ar | ✅ |
|
||
| **M1** | `Recipe` + `Ingredient` + baixa no pedido | SPEC-ESTOQUE E1 |
|
||
| **M2** | Admin UI ficha técnica + pausa auto | M1 |
|
||
| **M3** | `MenuItemSize` multi-preço | M0 |
|
||
| **M4** | Cardápio PWA lê só itens disponíveis | C1 + M1 |
|
||
|
||
---
|
||
|
||
## 9. Critérios de aceite
|
||
|
||
1. Gestor lista/edita categorias e itens no admin (ou seed + API).
|
||
2. PDV mostra categorias Joinville com preços BRL.
|
||
3. Todo item `stockMode=bom|sku` tem ficha ou SKU ligado (M1).
|
||
4. Coca 2L sem saldo → some/pausada no PDV em ≤ 2 s (M1).
|
||
5. Pizza sem mussarela no estoque → Calabresa e Mussarela pausam (M1).
|
||
6. SPEC-ESTOQUE referencia este doc e vice-versa (sem requisitos órfãos).
|
||
|
||
---
|
||
|
||
## 10. Fora de escopo (agora)
|
||
|
||
- Cardápio multi-idioma
|
||
- Fotos HD / CMS marketing
|
||
- Precificação dinâmica por horário
|
||
- Marketplace multi-loja
|
||
|
||
---
|
||
|
||
*SPEC satélite. Versão do produto pai: SPEC.md. Cruzamento obrigatório com SPEC-ESTOQUE.*
|