Each PRD documents real UI/API processes, service methods, states and gaps. Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
4.2 KiB
Markdown
111 lines
4.2 KiB
Markdown
# PRD — Cardápio Joinville (Menu)
|
||
|
||
| Campo | Valor |
|
||
|-------|--------|
|
||
| **Produto** | Pizza Digital |
|
||
| **Versão app auditada** | v0.5.0 |
|
||
| **Ambiente** | https://pizza.itecnologys.com · VM125 · loja `pizza-digital-piloto` |
|
||
| **Spec de origem** | `docs/CARDAPIO-JOINVILLE.md + docs/SPEC-CARDAPIO.md` |
|
||
| **Módulo** | Cardápio / PDV sheet de pizza |
|
||
| **Rotas UI** | `/pdv` · Admin menu |
|
||
| **APIs** | `GET/POST/PATCH/DELETE /api/menu/*` · `GET /api/menu/sizes` |
|
||
| **Data** | 2026-11-09 |
|
||
| **Estilo** | PRD operacional detalhado (visão de desenvolvedor) |
|
||
| **Audiência** | Dev / QA / Product |
|
||
|
||
> Como ler: cada seção descreve o **processo real** (o que o sistema faz hoje),
|
||
> com passos de usuário, chamadas de API, regras de negócio e estados.
|
||
> Status: ✅ construído · ⚠️ parcial · ⏳ especificado / não entregue · 🐛 gap observado.
|
||
|
||
|
||
## 1. Contexto do desenvolvedor
|
||
|
||
O cardápio Joinville não é só uma lista de sabores: é o **catálogo canônico** que o PDV usa para montar `OrderItem`.
|
||
Pesquisa regional (`CARDAPIO-JOINVILLE.md`) virou seed + `PizzaSizeBase`. Preço catalogado = **Grande 40 cm**;
|
||
tamanho/split resolvem no pedido.
|
||
|
||
### Artefatos de código
|
||
- UI: `apps/web/src/PdvPage.tsx` (sheet tamanho/sabores)
|
||
- API: `apps/api/src/menu/menu.controller.ts` · `menu.service.ts` · `pizza-sizes.ts`
|
||
- Dados: `MenuCategory`, `MenuItem`, `PizzaSizeBase`, `MenuItemStation`, `MenuItemRecipeLine`
|
||
- Seed: `apps/api/prisma/seed.ts`
|
||
|
||
### Inventário auditado
|
||
8 categorias ativas · **92 itens ativos** · 6 tamanhos · cat. Broto legado `active=false`.
|
||
|
||
---
|
||
|
||
## 2. Processo — carregar cardápio no PDV
|
||
|
||
1. Operador autentica (`POST /api/auth/login` com `storeSlug`).
|
||
2. Front chama `GET /api/menu` → `{ pizzaSizes, categories[] }` filtrando `active=true`.
|
||
3. Front monta abas/lista por categoria.
|
||
4. Item com `halfHalf=true` abre sheet; demais vão direto ao carrinho.
|
||
|
||
**Estados**
|
||
- Sem token → 401
|
||
- Categoria sem itens ativos → não deve renderizar (ou vazia)
|
||
- 🐛 Em 2026-11-09 a UI chegou a mostrar “Nenhuma categoria…” com API OK — validar fetch/storeSlug no client
|
||
|
||
---
|
||
|
||
## 3. Processo — montar pizza (passo a passo)
|
||
|
||
1. Atendente escolhe fulfillment: Entrega / Balcão / Mesa.
|
||
2. Clica sabor (ex.: Calabresa) → sheet.
|
||
3. Escolhe `sizeCode` (broto|pequena|media|grande|gigante|familia).
|
||
4. Sistema lê `maxFlavors` / `priceFactor` / `diameterCm` / `slices` de `PizzaSizeBase`.
|
||
5. Seleciona sabores até o limite; se exceder → erro UI.
|
||
6. Preço unitário = `max(priceCents dos sabores) * priceFactor`.
|
||
7. Opcional: borda (`Bordas especiais`).
|
||
8. Confirma → linha no pedido: `Pizza Grande 40cm · SaborA / SaborB`.
|
||
9. `POST /api/orders` com items (`sizeCode`, `halfHalf`, notes).
|
||
|
||
### Tabela de regras (congelada)
|
||
|
||
| sizeCode | cm | fatias | max sabores | fator |
|
||
|----------|----|--------|-------------|-------|
|
||
| broto | 25 | 4 | 1 | 0,55 |
|
||
| pequena | 30 | 6 | 1 | 0,70 |
|
||
| media | 35 | 8 | 2 | 0,85 |
|
||
| grande | 40 | 12 | 3 | 1,00 |
|
||
| gigante | 45 | 16 | 4 | 1,20 |
|
||
| familia | 50 | 20 | 4 | 1,35 |
|
||
|
||
---
|
||
|
||
## 4. Processo — CRUD gestor
|
||
|
||
1. Gestor com capability `menu.manage`.
|
||
2. `POST /api/menu/categories` / `PATCH` / soft-delete.
|
||
3. `POST /api/menu/items` com `priceCents`, `halfHalf`, estações, `stockMode`.
|
||
4. `GET /api/menu/ficha-meta` para BOM/ingredientes.
|
||
5. Item inativo (`active=false`) some do PDV, permanece no banco.
|
||
|
||
---
|
||
|
||
## 5. Sequência (dev)
|
||
|
||
```text
|
||
Login → GET /menu → render cats
|
||
→ tap pizza → sheet(size, flavors)
|
||
→ calc priceForSize
|
||
→ add line → POST /orders
|
||
→ KDS tickets por MenuItemStation
|
||
```
|
||
|
||
## 6. Aceite / gaps
|
||
- ✅ Seed Joinville + tamanhos
|
||
- ✅ Cálculo split no PDV
|
||
- ⚠️ UI Admin CRUD
|
||
- ⏳ PWA público `/c/:slug`
|
||
- 🐛 Validar PDV vazio intermitente
|
||
|
||
|
||
## 7. Notas de implementação (dev)
|
||
|
||
- `halfHalf=true` no MenuItem ⇒ sheet de tamanho no PDV (mesmo para pizza “inteira”).
|
||
- Broto legado: categoria `active=false`; não use SKU Broto separado.
|
||
- Preço: sempre derive de `priceCents` (Grande) × `PizzaSizeBase.priceFactor`.
|
||
- Após `POST /orders`, conferir `OrderTicket` gerados via estações do item.
|
||
- Se PDV listar vazio: checar JWT `storeId`, `GET /api/menu` no Network e `active` das categories.
|