obsidian-vault/Pizza-Digital/docs/PRD/PRD-CARDAPIO-JOINVILLE.md

4.2 KiB
Raw Permalink Blame History

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)

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.