From 62a20a257318ece2380b2ee11fbe18819c2fb475 Mon Sep 17 00:00:00 2001 From: Roger Date: Mon, 9 Nov 2026 19:45:11 +0000 Subject: [PATCH] Add Pizza-Digital docs to Obsidian vault and sync with VM125. Mirror all SPECs and construction docs from pizza-digital, add hub/sidebar links and bidirectional sync scripts. --- Pizza-Digital/.last-sync-pull | 1 + Pizza-Digital/.last-sync-push | 1 + Pizza-Digital/Pizza-Digital.md | 40 + Pizza-Digital/README.md | 29 + Pizza-Digital/SPEC.md | 724 ++++++++++++++++++ Pizza-Digital/SYNC-PIZZA-DIGITAL.md | 40 + Pizza-Digital/docs/API-WHATSAPP-INBOUND.md | 135 ++++ Pizza-Digital/docs/CARDAPIO-JOINVILLE.md | 67 ++ Pizza-Digital/docs/CHANGELOG-SPEC.md | 113 +++ Pizza-Digital/docs/FICHAS-TECNICAS-PESO.md | 111 +++ Pizza-Digital/docs/MIGRATION-ACCESS-MATRIX.md | 38 + Pizza-Digital/docs/PROMPT-CENTRAL-PEDIDOS.md | 161 ++++ Pizza-Digital/docs/RELEASE-v0.0.0.md | 28 + Pizza-Digital/docs/SPEC-CANAIS-DIGITAIS.md | 114 +++ Pizza-Digital/docs/SPEC-CARDAPIO.md | 197 +++++ .../docs/SPEC-CITYFOODS-VIDEO-PARITY.md | 142 ++++ .../docs/SPEC-DESPACHO-CITYFOODS-PARITY.md | 397 ++++++++++ Pizza-Digital/docs/SPEC-ESTOQUE.md | 262 +++++++ Pizza-Digital/docs/SPEC-FASE1.md | 232 ++++++ Pizza-Digital/docs/SPEC-KDS-CLAIM.md | 79 ++ .../docs/SPEC-KDS-PIZZAIOLO-MODELO-B.md | 221 ++++++ .../docs/SPEC-LACUNAS-VIDEO-SIRIFOOD.md | 107 +++ Pizza-Digital/docs/SPEC-MATRIZ-ACESSO.md | 169 ++++ Pizza-Digital/docs/SPEC-SIM-OPS-DEMO.md | 68 ++ Pizza-Digital/docs/SPEC-USUARIOS.md | 88 +++ Pizza-Digital/docs/SPEC.md | 724 ++++++++++++++++++ .../docs/SSH-CLAUDE-CLOUD-HANDOFF.md | 85 ++ Pizza-Digital/docs/SYNC-PIZZA-DIGITAL.md | 40 + .../docs/data/fichas-pizzas-peso.json | 595 ++++++++++++++ Pizza-Digital/scripts/sync_obsidian.sh | 31 + _sidebar.md | 13 + 31 files changed, 5052 insertions(+) create mode 100644 Pizza-Digital/.last-sync-pull create mode 100644 Pizza-Digital/.last-sync-push create mode 100644 Pizza-Digital/Pizza-Digital.md create mode 100644 Pizza-Digital/README.md create mode 100644 Pizza-Digital/SPEC.md create mode 100644 Pizza-Digital/SYNC-PIZZA-DIGITAL.md create mode 100644 Pizza-Digital/docs/API-WHATSAPP-INBOUND.md create mode 100644 Pizza-Digital/docs/CARDAPIO-JOINVILLE.md create mode 100644 Pizza-Digital/docs/CHANGELOG-SPEC.md create mode 100644 Pizza-Digital/docs/FICHAS-TECNICAS-PESO.md create mode 100644 Pizza-Digital/docs/MIGRATION-ACCESS-MATRIX.md create mode 100644 Pizza-Digital/docs/PROMPT-CENTRAL-PEDIDOS.md create mode 100644 Pizza-Digital/docs/RELEASE-v0.0.0.md create mode 100644 Pizza-Digital/docs/SPEC-CANAIS-DIGITAIS.md create mode 100644 Pizza-Digital/docs/SPEC-CARDAPIO.md create mode 100644 Pizza-Digital/docs/SPEC-CITYFOODS-VIDEO-PARITY.md create mode 100644 Pizza-Digital/docs/SPEC-DESPACHO-CITYFOODS-PARITY.md create mode 100644 Pizza-Digital/docs/SPEC-ESTOQUE.md create mode 100644 Pizza-Digital/docs/SPEC-FASE1.md create mode 100644 Pizza-Digital/docs/SPEC-KDS-CLAIM.md create mode 100644 Pizza-Digital/docs/SPEC-KDS-PIZZAIOLO-MODELO-B.md create mode 100644 Pizza-Digital/docs/SPEC-LACUNAS-VIDEO-SIRIFOOD.md create mode 100644 Pizza-Digital/docs/SPEC-MATRIZ-ACESSO.md create mode 100644 Pizza-Digital/docs/SPEC-SIM-OPS-DEMO.md create mode 100644 Pizza-Digital/docs/SPEC-USUARIOS.md create mode 100644 Pizza-Digital/docs/SPEC.md create mode 100644 Pizza-Digital/docs/SSH-CLAUDE-CLOUD-HANDOFF.md create mode 100644 Pizza-Digital/docs/SYNC-PIZZA-DIGITAL.md create mode 100644 Pizza-Digital/docs/data/fichas-pizzas-peso.json create mode 100755 Pizza-Digital/scripts/sync_obsidian.sh diff --git a/Pizza-Digital/.last-sync-pull b/Pizza-Digital/.last-sync-pull new file mode 100644 index 0000000..813461d --- /dev/null +++ b/Pizza-Digital/.last-sync-pull @@ -0,0 +1 @@ +2026-11-09T19:44:56Z diff --git a/Pizza-Digital/.last-sync-push b/Pizza-Digital/.last-sync-push new file mode 100644 index 0000000..4fc0ab5 --- /dev/null +++ b/Pizza-Digital/.last-sync-push @@ -0,0 +1 @@ +2026-11-09T19:44:43Z diff --git a/Pizza-Digital/Pizza-Digital.md b/Pizza-Digital/Pizza-Digital.md new file mode 100644 index 0000000..743ee35 --- /dev/null +++ b/Pizza-Digital/Pizza-Digital.md @@ -0,0 +1,40 @@ +# Pizza Digital — Hub Obsidian + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **SPEC** | [[SPEC]] · v0.3.2 | +| **Host** | big1 · VM **125** · `10.10.10.125` · user `mini` | +| **URL** | https://pizza.itecnologys.com | +| **Vault** | `Pizza-Digital/` | +| **Fonte VM** | `/home/mini/pizza-digital/` | +| **Sync** | [[SYNC-PIZZA-DIGITAL]] | +| **Última sync** | 2026-11-09T19:44:42Z | + +## Specs + +- [[SPEC]] — Spec mãe +- [[docs/SPEC-FASE1]] — MVP Cozinha Digital +- [[docs/SPEC-CANAIS-DIGITAIS]] — Canais digitais +- [[docs/SPEC-CARDAPIO]] — Cardápio +- [[docs/SPEC-ESTOQUE]] — Estoque / BOM +- [[docs/SPEC-MATRIZ-ACESSO]] — Matriz de acesso +- [[docs/SPEC-USUARIOS]] — Usuários / sessão +- [[docs/SPEC-KDS-CLAIM]] — KDS claim +- [[docs/SPEC-KDS-PIZZAIOLO-MODELO-B]] — KDS pizzaiolo Modelo B +- [[docs/SPEC-DESPACHO-CITYFOODS-PARITY]] — Despacho CityFoods +- [[docs/SPEC-CITYFOODS-VIDEO-PARITY]] — Paridade vídeos +- [[docs/SPEC-LACUNAS-VIDEO-SIRIFOOD]] — Lacunas Sirifood +- [[docs/SPEC-SIM-OPS-DEMO]] — Demo /sim + +## Documentos de construção + +- [[README]] — Bootstrap Docker +- [[docs/CHANGELOG-SPEC]] — Changelog +- [[docs/RELEASE-v0.0.0]] — Release +- [[docs/CARDAPIO-JOINVILLE]] — Pesquisa Joinville +- [[docs/FICHAS-TECNICAS-PESO]] — Fichas (g) +- [[docs/API-WHATSAPP-INBOUND]] — WhatsApp inbound +- [[docs/PROMPT-CENTRAL-PEDIDOS]] — Prompt Cursor +- [[docs/MIGRATION-ACCESS-MATRIX]] — Migração ACS +- [[docs/SSH-CLAUDE-CLOUD-HANDOFF]] — Handoff SSH diff --git a/Pizza-Digital/README.md b/Pizza-Digital/README.md new file mode 100644 index 0000000..b9b57c5 --- /dev/null +++ b/Pizza-Digital/README.md @@ -0,0 +1,29 @@ +# Pizza Digital — Fase 1 bootstrap Docker + +## Subir tudo (na VM 125) + +```bash +cd ~/pizza-digital +cp -n .env.example .env +docker compose down +# parar coming-soon se estiver na :3000 +docker rm -f pizza-coming-soon 2>/dev/null || true +docker compose up --build -d +docker compose ps +curl -sS http://127.0.0.1:3000/api/health +``` + +## Serviços + +| Container | Porta | Função | +|-----------|-------|--------| +| pizza-gateway | **3000** | Nginx → web + `/api` | +| pizza-api | 4000 | NestJS + Socket.io | +| pizza-web | (interno) | React build | +| pizza-postgres | — | Postgres 16 | +| pizza-redis | — | Redis 7 | + +## Apps web + +- `/` home + status +- `/pdv` `/kds/:station` `/despacho` `/admin` (scaffold) diff --git a/Pizza-Digital/SPEC.md b/Pizza-Digital/SPEC.md new file mode 100644 index 0000000..8a7ac92 --- /dev/null +++ b/Pizza-Digital/SPEC.md @@ -0,0 +1,724 @@ +# SPEC — Pizza Digital + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Versão do SPEC** | **0.3.2** | +| **Status** | Fase 1 cozinha no ar · **Prioridade seguinte: entrada por canais digitais** | +| **Data** | 2026-07-23 · rev. matriz acesso 2026-10-19 | +| **Owner** | Roger / iTecnologys | +| **Host Dev** | Proxmox BIG · VMID **125** · `10.10.10.125` · user `mini` | +| **URL pública** | `https://pizza.itecnologys.com` (legado SPEC: pizza.myvexx.com) | +| **Detalhe Fase 1** | [`docs/SPEC-FASE1.md`](docs/SPEC-FASE1.md) | +| **Canais digitais** | [`docs/SPEC-CANAIS-DIGITAIS.md`](docs/SPEC-CANAIS-DIGITAIS.md) · §5.5 | +| **Cardápio** | [`docs/SPEC-CARDAPIO.md`](docs/SPEC-CARDAPIO.md) · RF-MEN · Joinville | +| **Estoque** | [`docs/SPEC-ESTOQUE.md`](docs/SPEC-ESTOQUE.md) · RF-EST · BOM ↔ menu | +| **Matriz de acesso** | [`docs/SPEC-MATRIZ-ACESSO.md`](docs/SPEC-MATRIZ-ACESSO.md) · `/acessos` · RF-ACS | +| **Sequência oficial** | §15 — S0…S9 · Track C · Track M/E · Matriz ACS | +| **Referência principal** | CityFoods — Cozinha Digital + Cardápio Digital (`@cityfoods_app`) | +| **KDS / estações** | kitchenOS + restaurant-pos + este SPEC | +| **Rotas / dispatch** | **rusterando** = referência oficial (ORS, driver tour) — AGPL: modelo operacional, não fork do core | +| **Repos open-source** | kitchenOS, rusterando, URY Mosaic, MaSoVa, Tasty-Station | + +--- + +## 1. Visão + +**Pizza Digital** é uma plataforma de operação de delivery para pizzarias, restaurantes e outras cozinhas, com foco em **Cozinha Digital (KDS)**: eliminar comanda de papel, organizar produção por estações, reduzir erros e acelerar despacho. + +Não é um marketplace. É o **sistema operacional da cozinha + delivery próprio**, no mesmo espírito da CityFoods: + +> Cozinha Digital não é sobre robôs — é inteligência e fluxo à prova de falhas, do pedido à entrega. + +### 1.1 Problema + +Operações de delivery perdem tempo e clientes por: + +- Comandas de papel (perda, rasura, leitura ruim) +- Pedidos de vários canais em telas diferentes +- Falta de priorização (pedido novo não sai junto de outros prontos) +- Erros de observação / montagem +- Motoboys mal roteados (ida e volta na mesma região) +- Zero métrica de tempo por estação + +### 1.2 Promessa + +Uma plataforma única que: + +1. Recebe pedidos (PDV, cardápio digital, marketplaces) +2. Distribui por **estações de cozinha** (KDS) +3. Identifica pedidos por **etiquetas** +4. Prioriza e agrupa saída +5. Despacha com **gestão de rotas** +6. Mede **tempo e produtividade** em tempo real + +### 1.3 Posicionamento + +| | CityFoods (referência) | Pizza Digital (nossa) | +|--|------------------------|------------------------| +| Foco | Delivery multi-tipo | **Pizzaria primeiro**, depois outras cozinhas | +| Diferencial citado | “Primeira Cozinha Digital” | Cozinha Digital + templates de estação por tipo de negócio | +| Mercado | SC / BR SaaS | Começar local / controlado, evoluir para SaaS multi-tenant | + +--- + +## 2. Objetivos e métricas de sucesso + +### 2.1 Objetivos de produto (MVP) + +- Substituir comanda de papel por KDS touch +- Operar **1 pizzaria piloto** de ponta a ponta (pedido → pronto → despacho) +- Reduzir erros de montagem / observação +- Medir tempo médio por estação e por pedido + +### 2.2 KPIs (piloto) + +| KPI | Meta inicial (90 dias) | +|-----|------------------------| +| Erros de pedido (reclamação / refazer) | −50% vs baseline | +| Tempo médio pedido → pronto | −20% | +| Pedidos sem papel na cozinha | 100% no piloto | +| Atraso de despacho (> SLA) | −30% | +| Adoção diária do KDS | ≥ 95% dos pedidos passam pelo KDS | + +### 2.3 Não-objetivos (MVP) + +- App consumidor nativo (iOS/Android) completo +- Marketplace próprio +- Contabilidade / ERP completo +- Multi-país / multi-moeda +- IA generativa na cozinha + +--- + +## 3. Personas e papéis + +| Papel | Quem | Precisa | +|-------|------|---------| +| **Operador KDS** | Cozinheiro / pizzaiolo | Tela grande, bump rápido, ver observações | +| **Expedição** | Embalador / conferente | Pedido completo, etiqueta, agrupar saída | +| **Caixa / PDV** | Atendente | Lançar pedido balcão / telefone | +| **Despachante** | Quem libera motoboy | Agrupar por região, priorizar | +| **Entregador** | Motoboy | Lista de entregas, status, navegação básica | +| **Gestor** | Dono / gerente | Métricas, cardápio, usuários, relatórios | +| **Admin SaaS** (fase 2) | iTecnologys | Multi-loja, billing, suporte | + +--- + +## 4. Escopo por fases + +### Fase 0 — Fundação (esta SPEC + bootstrap) + +- Repositório, monorepo, CI mínimo +- Modelo de domínio e eventos +- Ambiente local (Docker) + +### Fase 1 — MVP Cozinha Digital (core) ✅ em andamento + +> Detalhe completo: [`docs/SPEC-FASE1.md`](docs/SPEC-FASE1.md) · Host: VM **125** `10.10.10.125` + +- Pedidos manuais (PDV web) +- KDS multi-estação (pizzaria) +- Status: `novo → em_preparo → pronto → despachado → entregue` +- Priorização / reordenação de fila +- Etiquetas (impressão ESC/POS ou PDF fallback) +- Painel gestor básico (tempos) +- Stack: NestJS + React + Postgres + Redis + Socket.io (Docker na VM 125) + +### Fase 2 — Delivery operacional (após KDS estável) + +> Prioridade de construção: **KDS multi-estação primeiro**; delivery/rotas só depois do piloto de cozinha (S5). +> Referência oficial de dispatch: **[rusterando](https://github.com/holg/rusterando)** (kitchen → driver tour → ORS). Detalhe: §15.1 e §15.2. + +- Gate `order.ready` → fila de despacho +- Agrupar por bairro (manual + sugestão) + atribuir motoboy + reordenar saída (“2 cliques” CityFoods) +- OpenRouteService (padrão rusterando) para ordem da rota +- Mapa no despacho · PWA entregador · tracking +- Pagamento/comissão por entrega · fechamento de caixa básico + +### Fase 3 — Canais digitais e SaaS (PRIORIDADE pós-S5) + +> Track **C** (canais) começa em paralelo assim que o piloto de cozinha (S5) estiver estável — **não esperar o fim de S6–S8**. +> Ver §5.5 e [`docs/SPEC-CANAIS-DIGITAIS.md`](docs/SPEC-CANAIS-DIGITAIS.md). + +- **C1** Cardápio digital público (PWA) + checkout (Pix / dinheiro na entrega) +- **C1** Pedidos digitais no quadro `/pedidos` com badge de origem +- **C2** Integração iFood (webhook → pedido normalizado) +- Multi-tenant / multi-loja +- Relatórios operacionais avançados +- Templates de cozinha: pizzaria, hamburgueria, sushi, dark kitchen + +--- + +## 5. Conceito operacional — Cozinha Digital + +### 5.1 Estações (template Pizzaria) + +| Estação | Responsabilidade | Exemplos de itens | +|---------|------------------|-------------------| +| **Massa** | Abrir / preparar base | Massa, borda | +| **Montagem** | Molho, queijo, toppings | Sabores, adicionais | +| **Forno** | Assar | Pizza em forno | +| **Expedição** | Embalar, conferir, etiquetar | Pedido completo | +| **Bebidas** (opcional) | Separar frios | Refrigerante, água | + +**Princípio KDS (CityFoods — claim exclusivo):** + +1. A **fila chega para todas** as estações. +2. Uma estação **escolhe e puxa (PEGAR)** um ou mais pedidos para si. +3. Com o claim, **as outras estações ficam bloqueadas** de pegar aquele pedido. +4. Só a estação dona dá **BUMP** (pedido pronto). + +Regras de mapeamento (histórico / tickets): + +- Cada item do cardápio mapeia para **1+ estações** (referência de produção) +- Claim é ao nível do **pedido completo**, não por item + +### 5.2 Template futuro — outras cozinhas + +| Tipo | Estações padrão | +|------|-----------------| +| Hamburgueria | Grelha · Fritura · Montagem · Expedição | +| Sushi | Corte · Montagem · Expedição | +| Dark kitchen genérica | Prep · Quente · Frios · Expedição | + +### 5.3 Etiquetas + +Cada pedido (ou item, configurável) gera etiqueta com: + +- Código curto do pedido (ex: `#A42`) +- Canal (PDV / iFood / Cardápio) +- Cliente (nome parcial) +- Itens resumidos + observações em destaque +- Horário / timer SLA +- QR opcional para conferência + +Objetivo: eliminar grampear comanda ilegível na embalagem. + +### 5.4 Priorização (inspirado CityFoods “2 cliques”) + +O despachante/gestor pode: + +- Reordenar fila de saída +- Agrupar pedidos da mesma região para sair juntos +- Marcar pedido como **urgente** (sobresai visualmente no KDS) + +### 5.5 Entrada por canais digitais (PRIORIDADE) + +> Detalhe de produto: [`docs/SPEC-CANAIS-DIGITAIS.md`](docs/SPEC-CANAIS-DIGITAIS.md) · RFs: RF-CAN, RF-PED-08/09, RF-MEN-04/06/07, RF-INT-01/03/05 + +**Problema:** pedidos chegam por telefone, WhatsApp e iFood em telas diferentes — a loja digita de novo no PDV e erra. + +**Promessa:** o cliente pede no canal digital; a loja vê **o mesmo Order** no quadro `/pedidos` e a cozinha trata igual a pedido de balcão. + +#### Dimensões (substituem `channel` misturado) + +| Dimensão | Valores | Uso na UI | +|----------|---------|-----------| +| `origin` | `pdv` · `cardapio` · `ifood` · `whatsapp` · `telefone` | Badge no card (CARDÁPIO / IFOOD / …) | +| `fulfillment` | `entrega` · `retirada` · `mesa` | Faixa ENTREGA / RETIRADA (CityFoods) | + +#### Canais de entrada → um quadro + +```text +Cardápio PWA /c/:slug ─┐ +WhatsApp (deep-link) ─┼─► Order Ingest (schema único) +iFood webhook ─┤ │ +PDV / telefone (staff) ─┘ ▼ + /pedidos (badge origem + fulfillment) + │ + ▼ + KDS · Etiqueta · Despacho +``` + +#### Track C (após piloto S5; C1 pode prototipar em paralelo) + +| Track | Entrega | +|-------|---------| +| **C1** | Cardápio público + checkout (Pix / pagar na entrega) → `/pedidos` ≤ 500 ms | +| **C2** | Webhook iFood → normalizador + idempotência | + +**Regra de ouro:** nenhum canal tem UI de cozinha própria — tudo passa pelo Order bus e pelo `/pedidos`. + +--- + +## 6. Fluxos principais + +### 6.1 Fluxo feliz — delivery pizzaria (canal digital ou PDV) + +```text +[Cliente] Cardápio /c/:slug | WhatsApp | iFood +[Staff] PDV /pedidos (+ Novo) + → Order Ingest normaliza (origin + fulfillment + itens) + → Aparece em /pedidos (badge origem · faixa ENTREGA/RETIRADA) + → Roteia itens → estações KDS + → Operadores dão claim/bump por estação + → Expedição confere + imprime etiqueta (origem + fulfillment) + → Pedido = PRONTO + → Despacho atribui motoboy / agrupa rota + → Entregador: saiu → entregue + → Métricas gravadas (tempos por etapa) +``` + +### 6.2 Estados do pedido + +```text +rascunho → confirmado → em_producao → pronto → em_rota → entregue + ↓ + cancelado +``` + +Estados de **item na estação**: + +```text +na_fila → em_preparo → concluido | cancelado +``` + +### 6.3 Diagrama lógico + +```text +Canais Core Operação +───── ──── ──────── +PDV ──────┐ +Cardápio ─┼─► Order Ingest ─► Router ─► KDS Stations +iFood ────┘ │ │ │ + │ │ ▼ + │ └──► Labels / Print + ▼ + Order Store ◄── WebSocket fan-out + │ + ▼ + Dispatch / Routes ─► Driver PWA + │ + ▼ + Analytics +``` + +--- + +## 7. Requisitos funcionais + +### 7.1 Pedidos (RF-PED) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-PED-01 | Criar pedido no PDV (itens, adicionais, obs, tipo: delivery/balcão/mesa) | Sim | +| RF-PED-02 | Schema único de pedido independente do canal | Sim | +| RF-PED-03 | Editar pedido até entrar em produção (política configurável) | Sim | +| RF-PED-04 | Cancelar pedido com motivo | Sim | +| RF-PED-05 | Observações em destaque no KDS | Sim | +| RF-PED-06 | Número sequencial diário + código curto legível | Sim | +| RF-PED-07 | Ingestão via webhook (iFood etc.) | Track C2 | +| RF-PED-08 | Campos `origin` + `fulfillment` (em vez de channel misturado) | Track C1 | +| RF-PED-09 | Pedido digital aparece no `/pedidos` em tempo real (WS) | Track C1 | + +### 7.2 KDS / Cozinha Digital (RF-KDS) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-KDS-01 | Tela KDS por estação (filtro de área) | Sim | +| RF-KDS-02 | Atualização em tempo real (WebSocket, sem polling) | Sim | +| RF-KDS-03 | Timer visual (verde / amarelo / vermelho por SLA) | Sim | +| RF-KDS-04 | Bump item / bump pedido na estação | Sim | +| RF-KDS-05 | Multi-estação no mesmo pedido | Sim | +| RF-KDS-06 | Som/alerta opcional de novo ticket | Sim | +| RF-KDS-07 | Modo tela cheia / tablet / TV | Sim | +| RF-KDS-08 | Offline degradado (fila local + sync) | Fase 2 | + +### 7.3 Etiquetas (RF-ETQ) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-ETQ-01 | Gerar etiqueta ao concluir expedição (ou sob demanda) | Sim | +| RF-ETQ-02 | Impressão térmica ESC/POS | Sim (com fallback PDF) | +| RF-ETQ-03 | Layout configurável por loja | Fase 2 | + +### 7.4 Despacho e rotas (RF-ROT) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-ROT-01 | Lista de pedidos prontos para despacho | Fase 1 (lista simples) | +| RF-ROT-02 | Atribuir entregador | Fase 2 | +| RF-ROT-03 | Agrupar por bairro / proximidade | Fase 2 | +| RF-ROT-04 | Reordenar saída (priorização) | Fase 1 | +| RF-ROT-05 | App/PWA entregador com status | Fase 2 | +| RF-ROT-06 | Cálculo de pagamento de entregadores | Fase 3 | + +### 7.5 Cardápio e PDV (RF-MEN) + +> Detalhe: [`docs/SPEC-CARDAPIO.md`](docs/SPEC-CARDAPIO.md) · pesquisa [`docs/CARDAPIO-JOINVILLE.md`](docs/CARDAPIO-JOINVILLE.md) +> Ponte estoque: [`docs/SPEC-ESTOQUE.md`](docs/SPEC-ESTOQUE.md) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-MEN-01 | CRUD categorias, itens, preços, adicionais | Sim | +| RF-MEN-02 | Mapear item → estações KDS | Sim | +| RF-MEN-03 | Tempo SLA por categoria/item | Sim | +| RF-MEN-04 | Cardápio digital público (PWA cliente) | **Track C1** | +| RF-MEN-05 | Combos / meia-a-meia (pizzaria) | Fase 1 (meia-a-meia) | +| RF-MEN-06 | Horário de funcionamento / loja aberta-fechada no cardápio | Track C1 | +| RF-MEN-07 | Áreas de entrega + taxa por bairro no cardápio | Track C1 | +| RF-MEN-08 | Tamanhos com preço (`broto`/`grande`/…) | Track M1 | +| RF-MEN-09 | Pausar item (manual ou estoque baixo) | Track M1 | +| RF-MEN-10 | Ficha técnica (BOM) ligada ao item | Track M1 ↔ EST | +| RF-MEN-11 | Bebida `stockMode=sku` 1:1 | Track M1 | +| RF-MEN-12 | Vitamina/suco natural via BOM | Track M1 | + +### 7.6 Gestão e métricas (RF-GES) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-GES-01 | Dashboard: pedidos abertos, atrasados, tempo médio | Sim | +| RF-GES-02 | Tempo médio por estação | Sim | +| RF-GES-03 | Relatório de fluxo do pedido (timestamps) | Sim | +| RF-GES-04 | Usuários, papéis, permissões | Sim | +| RF-GES-05 | Multi-loja / tenant | Fase 3 | + +### 7.7 Integrações (RF-INT) + +| ID | Requisito | Fase | +|----|-----------|------| +| RF-INT-01 | Webhook genérico de pedido | Track C1 | +| RF-INT-02 | iFood (ou agregador BR) | Track C2 | +| RF-INT-03 | Pagamento online (Pix / dinheiro na entrega) | Track C1 | +| RF-INT-04 | Impressoras de rede | 1 | +| RF-INT-05 | Deep-link WhatsApp → cardápio da loja | Track C1 | + +### 7.8 Canais digitais (RF-CAN) + +| ID | Requisito | Track | +|----|-----------|-------| +| RF-CAN-01 | PWA cardápio público por loja (`/c/:slug`) | C1 | +| RF-CAN-02 | Carrinho cliente → Order com `origin=cardapio` | C1 | +| RF-CAN-03 | Escolha fulfillment entrega/retirada no cardápio | C1 | +| RF-CAN-04 | Taxa / área de entrega no checkout | C1 | +| RF-CAN-05 | Pedido digital no `/pedidos` ≤ 500 ms (WS) | C1 | +| RF-CAN-06 | Badge de origem no card (CARDÁPIO / IFOOD / …) | C1 | +| RF-CAN-07 | Webhook iFood → normalizador | C2 | +| RF-CAN-08 | Idempotência / dedupe de pedidos externos | C2 | + +### 7.9 Estoque (RF-EST) + +> Detalhe: [`docs/SPEC-ESTOQUE.md`](docs/SPEC-ESTOQUE.md) · dispara a partir do Order · pausa o Cardápio + +| ID | Requisito | Track | +|----|-----------|-------| +| RF-EST-01 | CRUD insumos (unidade, min, custo) | E1 | +| RF-EST-02 | Ficha técnica por MenuItem | E1 | +| RF-EST-03 | Baixa automática no pedido (`em_producao`) | E1 | +| RF-EST-04 | Entrada compra / ajuste manual | E1 | +| RF-EST-05 | Alerta estoque mínimo (WS) | E1 | +| RF-EST-06 | Pausa automática do item no cardápio | E2 | +| RF-EST-07 | Estorno em cancelamento elegível | E1 | +| RF-EST-08 | CMV teórico por dia/item | E2 | +| RF-EST-13 | Ficha por pizza com qty em **gramas** (38 tipos) | E1 | +| RF-EST-14 | Catálogo `fichas-pizzas-peso.json` | E1 | + +**Regra de ouro:** Cardápio declara `stockMode` (`bom`\|`sku`\|`none`); Estoque executa a baixa. +**Pizzas:** baixa por **peso (g)** por tipo — [`docs/FICHAS-TECNICAS-PESO.md`](docs/FICHAS-TECNICAS-PESO.md). + +--- + +## 8. Requisitos não-funcionais + +| ID | Tema | Requisito | +|----|------|-----------| +| RNF-01 | Latência KDS | Novo pedido visível na tela ≤ **500 ms** (LAN saudável) | +| RNF-02 | Disponibilidade | MVP single-node OK; alvo produção 99.5% | +| RNF-03 | Concorrência | Suportar pico de **60 pedidos/hora** no piloto | +| RNF-04 | UX cozinha | Touch-first, botões grandes, alto contraste, legível a 1–2 m | +| RNF-05 | Segurança | Auth JWT/session; senhas hashed; HTTPS; RBAC | +| RNF-06 | Auditoria | Log de mudanças de status com user + timestamp | +| RNF-07 | Observabilidade | Logs estruturados + healthcheck | +| RNF-08 | Idioma | PT-BR nativo | +| RNF-09 | Deploy | Docker Compose local; caminho claro para VPS/Proxmox | +| RNF-10 | LGPD | Dados de cliente mínimos; retenção configurável | + +--- + +## 9. Arquitetura alvo + +### 9.1 Princípios + +1. **Order bus / schema único** — todo canal vira o mesmo `Order` +2. **Event-driven para cozinha** — WebSocket fan-out por loja/estação +3. **Station routing** — regras no servidor, não na UI +4. **PWA** — KDS e entregador rodam no browser/tablet +5. **Começar simples** — 1 processo API + Postgres + Redis; extrair microsserviços só se necessário + +### 9.2 Stack sugerida (do zero) + +| Camada | Tecnologia | Motivo | +|--------|------------|--------| +| Frontend | React + Vite + TypeScript (PWA) | KDS/PDV touch, ecossistema forte | +| Realtime | Socket.io ou native WS | Padrão dos KDS de referência | +| API | NestJS ou Fastify (Node/TS) | Tipagem compartilhada, rapidez | +| DB | PostgreSQL | Pedidos, auditoria, relatórios | +| Cache / pubsub | Redis | Tickets abertos + fan-out | +| Filas (fase 2+) | BullMQ | Webhooks, impressão, retries | +| Print | ESC/POS (node) + PDF fallback | Etiquetas térmicas | +| Infra local | Docker Compose | Postgres + Redis + API + Web | + +Referências de forma (não copy-paste cego): + +- **kitchenOS** → ingestão + KDS + Redis stream +- **Tasty-Station** → Socket.io POS↔KDS +- **rusterando** → **referência oficial** pizzaria + rotas / ORS / driver tour (modelo, não fork AGPL) +- **URY Mosaic** → multi-kitchen KDS + +### 9.3 Apps da plataforma + +| App | URL mental | Usuário | +|-----|------------|---------| +| `web-pdv` | `/pdv` | Caixa | +| `web-kds` | `/kds/:station` | Cozinha | +| `web-dispatch` | `/despacho` | Despachante | +| `web-admin` | `/admin` | Gestor | +| `web-driver` | `/entregador` | Motoboy (fase 2) | +| `web-menu` | `/m/:slug` | Cliente (fase 3) | + +Monorepo sugerido: + +```text +pizza-digital/ +├── SPEC.md # este documento +├── docs/ +│ ├── domain.md # glossário e regras +│ ├── api.md # contratos HTTP/WS +│ └── ui-kds.md # wireframes textuais +├── apps/ +│ ├── web/ # PDV + KDS + Admin (rotas) +│ └── api/ +├── packages/ +│ ├── domain/ # types Order, Station, events +│ └── escpos/ # impressão +└── docker-compose.yml +``` + +--- + +## 10. Modelo de domínio (MVP) + +### 10.1 Entidades + +- **Store** — loja +- **User** — usuário + role +- **Station** — estação KDS +- **Category / Product / Modifier** — cardápio ([SPEC-CARDAPIO](docs/SPEC-CARDAPIO.md)) +- **ProductStationRule** — produto → estações +- **Recipe / RecipeLine** — ficha técnica BOM ([SPEC-ESTOQUE](docs/SPEC-ESTOQUE.md)) +- **Ingredient** — insumo com saldo +- **StockMovement** — entrada/saída/ajuste +- **Order** — pedido +- **OrderItem** — item do pedido (+ modifiers, notes) +- **StationTicket** — fatia do item/pedido na estação +- **LabelPrintJob** — job de etiqueta +- **Courier** (fase 2) — entregador +- **DispatchBatch** (fase 2) — lote de rota + +### 10.2 Campos críticos de `Order` + +```text +id, store_id, code, channel, type (delivery|pickup|dine_in), +status, customer_name, customer_phone, address, +notes, priority, promised_at, +accepted_at, production_started_at, ready_at, +dispatched_at, delivered_at, canceled_at, +created_at, updated_at +``` + +### 10.3 Eventos (WebSocket) + +```text +order.created +order.updated +order.canceled +ticket.upserted +ticket.bumped +order.ready +order.dispatched +order.delivered +label.printed +``` + +Rooms: `store:{id}`, `store:{id}:station:{stationId}` + +--- + +## 11. Regras de negócio — pizzaria + +1. **Meia-a-meia**: 1 pizza = 2 sabores; preço = maior sabor (configurável); aparece no KDS como um item com 2 metades. +2. **Borda / adicionais**: modifiers ligados à montagem (ou massa, se borda recheada). +3. **Tempo SLA padrão** (editável): Massa 3 min · Montagem 5 · Forno 8 · Expedição 3 — total alvo ~20 min. +4. **Pedido delivery sem endereço** não confirma. +5. **Bump na expedição** só liberado se estações anteriores concluídas (ou override gestor). +6. **Prioridade urgente** não pula forno fisicamente — só sobe na fila visual e alerta. + +--- + +## 12. UX — KDS (requisitos de interface) + +- Cards grandes por pedido; código `#A42` enorme +- Observação do cliente em **cor de alerta** +- Timer no canto; vira amarelo/vermelho por threshold +- Botão único: **PRONTO** (item) / **TUDO PRONTO** (estação) +- Badge do canal (PDV / iFood / …) +- Sem cards decorativos desnecessários — densidade operacional +- Funciona em tablet paisagem e TV 32"+ + +--- + +## 13. Segurança e permissões (RBAC) + +| Recurso | Operador KDS | Expedição | PDV | Despacho | Gestor | +|---------|--------------|-----------|-----|----------|--------| +| Ver KDS da sua estação | ✓ | ✓ | — | — | ✓ | +| Bump tickets | ✓ | ✓ | — | — | ✓ | +| Criar pedido | — | — | ✓ | — | ✓ | +| Cancelar pedido | — | — | ✓* | — | ✓ | +| Despachar | — | ✓ | — | ✓ | ✓ | +| Cardápio / users | — | — | — | — | ✓ | +| Métricas | — | — | — | leitura | ✓ | + +\*cancelar só antes de produção, salvo permissão. + +--- + +## 14. Critérios de aceite do MVP + +O MVP está **aceito** quando, em ambiente piloto: + +1. Atendente cria pedido no PDV com 2 pizzas (1 meia-a-meia) + bebida + observação +2. Tickets aparecem nas estações corretas em < 1 s +3. Cada estação conclui seus itens; expedição só libera no fim +4. Etiqueta é gerada/impressa +5. Gestor reordena/prioritiza um pedido pronto +6. Dashboard mostra tempos por estação daquele pedido +7. Zero uso de papel na cozinha durante o teste supervisionado + +--- + +## 15. Sequência oficial de implementação (S0–S9) + +> **Congelado 2026-07-23 (SPEC 0.2.1).** +> Ordem: **KDS por estação antes de delivery/rotas.** +> Ciclo inspirado no rusterando: `cart/PDV → kitchen board → driver tour → ORS`. +> Não forkamos o binário Rust/AGPL; reutilizamos o **modelo operacional** no stack Nest/React. + +### 15.1 Mapa de sprints + +| Sprint | Bloco (rusterando) | Entrega Pizza Digital | Status | +|--------|--------------------|------------------------|--------| +| **S0** | Foundation | Repo + Docker Compose + domain types + SPEC | ✅ | +| **S1** | Auth + menu admin | Auth JWT + Store + Cardápio + mapa item→estações | ✅ (`pizza.myvexx.com`) | +| **S2** | Cart → order | **PDV** + criar pedido + state machine `confirmado→em_producao→pronto` | ✅ | +| **S3** | Kitchen board | **KDS multi-estação** realtime (bump, timers, rooms Socket.io) | ✅ | +| **S4** | Ready / expedição | Etiquetas + fila `pronto` + priorização + dashboard tempos | pendente | +| **S5** | Piloto cozinha | 1 pizzaria estável sem papel (aceite §14) | pendente | +| **S6** | Driver tour (manual) | Despacho: fila só `pronto` → agrupar bairro → motoboy → reordenar | pendente | +| **S7** | **ORS** | Gestor de Rotas: `DispatchBatch` + mapa permanente + heurística; ORS com `ORS_API_KEY` | ✅ parcial (ORS opcional) | +| **S8** | Driver + push | `/motoboy` + rota + GPS ping (+ push staff depois) | ✅ parcial (sem PIN/offline) | +| **S9 / Track C1** | Public menu / pay | **Cardápio digital** + checkout (Pix / dinheiro) → quadro `/pedidos` | **PRIORIDADE pós-S5** | +| **Track C2** | Agregadores | iFood webhook → Order normalizado | após C1 | + +**Gate entre mundos (obrigatório):** +`order.ready` → fila despacho → batch → ORS *(só a partir de S6/S7)*. + +### 15.2 Módulo Rotas (CityFoods → Pizza Digital) + +**Referência oficial de dispatch:** [rusterando](https://github.com/holg/rusterando) (AGPL-3.0). + +| Subfase | Capacidade | Sprint | +|---------|------------|--------| +| MVP rotas | Fila só pedidos `pronto` | S6 | +| MVP rotas | Agrupar por bairro (manual + sugestão) | S6 | +| MVP rotas | Atribuir motoboy | S6 | +| MVP rotas | Reordenar saída (“2 cliques” CityFoods) | S6 | +| Ampliação | OpenRouteService — ordem ótima da tour | S7 | +| Ampliação | Mapa no despacho | S7 | +| Ampliação | PWA entregador + tracking | S8 | +| Ampliação | Pagamento / comissão por entrega | S8–S9 | + +Papéis rusterando → Pizza Digital: Admin≈gestor · Kitchen≈kds/estações · Driver≈entregador · Customer≈canal cardápio (S9). + +### 15.3 Referências por domínio + +| Domínio | Fonte | +|---------|--------| +| KDS / estações / bump | Este SPEC + kitchenOS / Tasty-Station | +| Despacho / ORS / driver tour | **rusterando** (oficial) + CityFoods Gestor de Rotas | +| UX “2 cliques” priorização | CityFoods | + +### 15.4 Design UI + +Status: **ainda não congelado.** S1 tem UI funcional (login + admin cardápio). +Antes de S3: wireframes PDV → KDS (touch) → Despacho. Design system em documento separado (`docs/DESIGN.md`) quando existir. + +--- + +## 16. Riscos + +| Risco | Mitigação | +|-------|-----------| +| Cozinha rejeita tela (hábito do papel) | UX bump mínima + treinamento 1 turno + impressão híbrida opcional | +| Impressora térmica incompatível | Driver ESC/POS + fallback PDF/A4 | +| Pico de pedidos derruba WS | Redis pubsub + rooms por loja; load test 60/h | +| Escopo inchando (virar iFood) | Freeze: MVP = cozinha digital, não marketplace | +| Integração iFood cedo demais | Só após piloto estável | + +--- + +## 17. Glossário + +| Termo | Significado | +|-------|-------------| +| **KDS** | Kitchen Display System — tela digital da cozinha | +| **Bump** | Marcar item/pedido como pronto na estação | +| **Estação** | Setor de produção (massa, forno, …) | +| **Ticket** | Representação do trabalho de um item/pedido numa estação | +| **SLA** | Tempo alvo até alerta de atraso | +| **Despacho** | Liberação do pedido pronto para entregador | +| **Canal** | Origem do pedido (PDV, cardápio, iFood, …) | +| **Cozinha Digital** | Operação completa sem comanda de papel + métricas | +| **BOM / Ficha técnica** | Lista de insumos por item do cardápio | +| **stockMode** | `bom` · `sku` · `none` — como o item consome estoque | +| **CMV** | Custo da mercadoria vendida (teórico via BOM) | + +--- + +## 18. Decisões em aberto (para fechar com Roger) + +1. Stack final: **NestJS** ✅ (congelado Fase 1) +2. Piloto: qual pizzaria / volume médio de pedidos/dia? +3. Impressora térmica já definida (marca/modelo)? +4. MVP inclui **mesa/comanda salão** ou só delivery + balcão? +5. Hospedagem inicial → **Proxmox BIG VM125** (`10.10.10.125`) ✅ +6. Domínio → **`https://pizza.myvexx.com`** ✅ +7. Sequência S0–S9 + rusterando = ref. rotas → **congelado §15** ✅ +8. Design UI/UX (wireframes KDS/PDV/despacho) — em aberto + +--- + +## 19. Referências + +- CityFoods Cozinha Digital — https://abre.ai/cityfoods / https://cityfoods.com.br/ +- CityFoods Instagram — https://www.instagram.com/cityfoods_app/ +- kitchenOS — https://github.com/rajeshselvam02/kitchenOS +- rusterando (**ref. oficial dispatch/ORS**) — https://github.com/holg/rusterando +- URY / Mosaic — https://github.com/ury-erp/ury +- MaSoVa — https://github.com/SVamseekar/masova-platform +- Tasty-Station POS — https://github.com/hey-Zayn/Tasty-Station-POS + +--- + +## 20. Próximo passo imediato + +1. **S4** — Etiquetas + priorização + dashboard tempos (fechar cozinha) +2. **S5** — Piloto pizzaria (cozinha estável) +3. **Track C1 — entrada digital** — cardápio público → `/pedidos` (§5.5) ← **foco de produto** +4. **Track E1 / M1** — Estoque + ficha técnica ligados ao cardápio Joinville +5. Em paralelo ou depois: S6–S8 (rotas) · Track C2 (iFood) + +--- + +*Documento vivo. Mudanças de escopo devem atualizar versão (ex.: 0.2.0 → 0.2.1) e registrar changelog em `docs/CHANGELOG-SPEC.md`.* diff --git a/Pizza-Digital/SYNC-PIZZA-DIGITAL.md b/Pizza-Digital/SYNC-PIZZA-DIGITAL.md new file mode 100644 index 0000000..cb561d4 --- /dev/null +++ b/Pizza-Digital/SYNC-PIZZA-DIGITAL.md @@ -0,0 +1,40 @@ +# Sync — Pizza Digital ↔ Obsidian + +**Última sync:** 2026-11-09T19:44:42Z +**Direção padrão:** VM125 → Vault (pull) · Vault → VM125 (push) + +## Mapa + +| Origem (VM125) | Destino (CT130 vault) | +|----------------|------------------------| +| `/home/mini/pizza-digital/SPEC.md` | `Pizza-Digital/SPEC.md` | +| `/home/mini/pizza-digital/README.md` | `Pizza-Digital/README.md` | +| `/home/mini/pizza-digital/docs/**` | `Pizza-Digital/docs/**` | + +## Comandos + +No **CT130** (hub): + +```bash +# VM → Vault +/opt/ligbox-spec-hub/scripts/sync-pizza-digital.sh pull + +# Vault → VM +/opt/ligbox-spec-hub/scripts/sync-pizza-digital.sh push +``` + +Na **VM125**: + +```bash +# Vault → VM (atualiza docs locais a partir do Obsidian) +~/pizza-digital/scripts/sync_obsidian.sh pull + +# VM → Vault +~/pizza-digital/scripts/sync_obsidian.sh push +``` + +## Notas + +- SSH: CT130 `root` → `mini@10.10.10.125` (chave `id_ed25519` spec-hub) +- Não sincroniza código (`apps/`, `packages/`, `.env`) — só documentação +- Após pull no vault, fazer commit no repo `obsidian-vault` se quiser versionar diff --git a/Pizza-Digital/docs/API-WHATSAPP-INBOUND.md b/Pizza-Digital/docs/API-WHATSAPP-INBOUND.md new file mode 100644 index 0000000..4790d0c --- /dev/null +++ b/Pizza-Digital/docs/API-WHATSAPP-INBOUND.md @@ -0,0 +1,135 @@ +# API de entrada — WhatsApp + +| Campo | Valor | +|-------|--------| +| Versão | 0.1 | +| Data | 2026-10-19 | +| Origin no pedido | `whatsapp` | +| Badge na Central | **WHATSAPP** | + +Primeira API de canal digital: qualquer bot / n8n / Make / Evolution API envia JSON autenticado e o pedido cai em `/pedidos` com a mesma fila do PDV. + +--- + +## 1. Auth + +Header obrigatório: + +```http +X-Api-Key: +``` + +(ou `Authorization: Bearer `) + +Variável no servidor: `INBOUND_API_KEY` (ver `.env.example`). + +--- + +## 2. Criar pedido (recomendado) + +```http +POST /api/inbound/whatsapp +Content-Type: application/json +X-Api-Key: pizza-inbound-805353 +``` + +```json +{ + "storeSlug": "pizza-digital-piloto", + "externalId": "wamid.HBgLNTU0Nzk5ODg3NzY2FQIAEhggEXAMPLE", + "customerName": "Ana Silva Santos", + "customerPhone": "5547999887766", + "fulfillment": "entrega", + "bairro": "Centro", + "address": "Rua das Palmeiras, 120", + "notes": "sem cebola", + "items": [ + { "name": "Calabresa", "quantity": 1, "sizeCode": "grande" }, + { "name": "Coca-Cola 2L", "quantity": 1 } + ] +} +``` + +| Campo | Obrigatório | Notas | +|-------|-------------|--------| +| `storeSlug` | sim | slug da loja | +| `externalId` | recomendado | idempotência (`wa:`) — reenvio não duplica | +| `customerName` | sim | nome completo | +| `customerPhone` | sim | WhatsApp do cliente | +| `fulfillment` | não | `entrega` (default) · `retirada` · `mesa` | +| `bairro` / `address` | entrega | ao menos um | +| `items[].menuItemId` | um dos dois | id do menu | +| `items[].name` | um dos dois | match fuzzy no cardápio | +| `items[].sizeCode` | não | `broto`…`familia` (preço × fator) | + +Resposta: mesmo payload de `POST /api/orders` (code, items, origin=`whatsapp`). + +--- + +## 3. Webhook Meta Cloud API (opcional) + +| Método | Rota | Uso | +|--------|------|-----| +| `GET` | `/api/inbound/whatsapp/meta` | Verificação (`WHATSAPP_VERIFY_TOKEN`) | +| `POST` | `/api/inbound/whatsapp/meta` | Eventos de mensagem | + +Configure no Meta: +- Callback URL: `https:///api/inbound/whatsapp/meta` +- Verify token: valor de `WHATSAPP_VERIFY_TOKEN` +- Loja default: `WHATSAPP_STORE_SLUG` + +### Formato de texto aceito no chat + +```text +PEDIDO +Nome: Ana Silva +Telefone: 47999887766 +Entrega +Bairro: Centro +Endereço: Rua das Palmeiras, 120 +1x Calabresa +1x Coca-Cola 2L +Obs: sem cebola +``` + +Mensagens que não começam com `PEDIDO` / `#PEDIDO` são ignoradas (200 OK, sem criar pedido). + +--- + +## 4. Fluxo típico com n8n + +```text +WhatsApp (Evolution / Meta) + → n8n parseia intenção / menu + → POST /api/inbound/whatsapp (JSON estruturado) + → Central /pedidos (badge WHATSAPP + som) + → KDS +``` + +--- + +## 5. Smoke test + +```bash +curl -sS -X POST https://pizza.myvexx.com/api/inbound/whatsapp \ + -H "Content-Type: application/json" \ + -H "X-Api-Key: pizza-inbound-805353" \ + -d '{ + "storeSlug":"pizza-digital-piloto", + "externalId":"test-wa-001", + "customerName":"Ana Silva Santos", + "customerPhone":"47999887766", + "fulfillment":"entrega", + "bairro":"Centro", + "address":"Rua Teste, 1", + "items":[{"name":"Calabresa","quantity":1,"sizeCode":"grande"}] + }' +``` + +--- + +## 6. Próximos passos + +- Validar `X-Hub-Signature-256` do Meta +- Resposta automática ao cliente (pedido #code + tracking) +- Catálogo rápido via botões WhatsApp (interactive messages) diff --git a/Pizza-Digital/docs/CARDAPIO-JOINVILLE.md b/Pizza-Digital/docs/CARDAPIO-JOINVILLE.md new file mode 100644 index 0000000..b36415d --- /dev/null +++ b/Pizza-Digital/docs/CARDAPIO-JOINVILLE.md @@ -0,0 +1,67 @@ +# Cardápio Joinville — pesquisa e modelo + +| Campo | Valor | +|-------|--------| +| Região | Joinville / SC | +| Data | 2026-10-19 | +| Fontes | Super Pizza, Tempero Napolitano, Positano, Casa di Francisca, Gullas, A Brasileira, Lisboa | +| **SPEC Cardápio** | [`SPEC-CARDAPIO.md`](SPEC-CARDAPIO.md) | +| **SPEC Estoque** | [`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md) (BOM / baixas) | + +## Tamanhos Pizza Digital (piloto) + +| Tamanho | Diâmetro | Fatias | ≈ pratos | Sabores máx. | Papel comercial | +|---------|----------|--------|----------|--------------|-----------------| +| **Broto** | 25 cm | 4 | 1 | **1** | Individual | +| **Pequena** | 30 cm | 6 | 1,5 | **1** | 1–2 pessoas | +| **Média** | 35 cm | 8 | 2 | **2** | Casal / split | +| **Grande** | 40 cm | 12 | 2,5 | **3** | Mais vendido | +| **Gigante** | 45 cm | 16 | 3 | **4** | Família grande | +| **Família** | 50 cm | 20 | 3,5 | **4** | Máximo | + +**Split:** preço = maior sabor × fator do tamanho. Escolha no PDV ao montar o pedido. + +### Referência de mercado Joinville (pesquisa) + +| Tamanho | Diâmetro | Fatias | Sabores típicos | Papel comercial | +|---------|----------|--------|-----------------|-----------------| +| **Broto** | 25 cm | 4–6 | 1 | Individual / doce | +| Pequena / Média | 30 cm | 8 | 1–2 | Casal | +| Grande / Família* | 35–40 cm | 10–12 | 2–3 | **Mais vendido** | +| Gigante | 45 cm | 16 | 3–4 | Família grande | + +\*Nomenclatura varia: em algumas casas “Grande” = 35 cm e “Família” = 40 cm; em outras “Grande” = 40 cm. + +**Padrão Pizza Digital (piloto):** preço base = **Grande 40 cm / 12 fatias**. Tamanho e split escolhidos no pedido (não há SKU Broto separado no cardápio ativo). + +## Sabores mais comercializados (frequência nos cardápios) + +### Tradicionais (alta rotação) +Calabresa · Mussarela · Frango Catupiry · Portuguesa · 4 Queijos · Marguerita · Bacon · Pepperoni · Lombo Catupiry · Baiana · Napolitana · Presunto · Palmito · Brócolis · Toscana · Moda da Casa · Atum · Crocante · Estrogonofe de Frango · Vegetariana + +### Regionais / SC (diferencial Joinville) +- **Joinville** — mussarela, alho, parmesão, pimentão +- **Linguiça Blumenau** — mussarela, linguiça Blumenau, catupiry +- **Aipim com Bacon** — purê de aipim, bacon +- **Catarina** — calabresa ralada, ovos, cebola + +### Doces (pedido frequente pós-salgada / broto) +Chocolate preto · Chocolate branco · Banana c/ canela · Prestígio · Romeu e Julieta · Morango c/ chocolate + +## Faixa de preço observada (2024–2026) + +- Broto: ~R$ 39–56 +- Grande/Família 40 cm: ~R$ 66–96 (tradicionais) +- Gigante 45 cm: ~R$ 90–102+ +- Borda Catupiry/Cheddar: ~R$ 12–20 + +Preços no seed = média competitiva Joinville (não cópia de um cardápio). + +## Acompanhamentos — bebidas + +| Categoria | Itens | +|-----------|--------| +| **Refrigerantes** | Coca / Zero, Guaraná / Zero, Pepsi, Fanta, Sprite, Sukita · 2L, 1,5L, 600ml, lata · água c/ e s/ gás | +| **Sucos** | Del Valle (laranja, uva, pêssego, abacaxi, maracujá, manga, goiaba) + naturais feitos na hora | +| **Energéticos** | Red Bull, Monster, TNT, Fusion, Burn, Flying Horse | +| **Vitaminas** | Leite + gelo + base de frutas (banana, morango, abacate, mamão, manga, açaí, maracujá, coco, mistas, c/ ovo, c/ aveia) | diff --git a/Pizza-Digital/docs/CHANGELOG-SPEC.md b/Pizza-Digital/docs/CHANGELOG-SPEC.md new file mode 100644 index 0000000..fdfaea9 --- /dev/null +++ b/Pizza-Digital/docs/CHANGELOG-SPEC.md @@ -0,0 +1,113 @@ +# Changelog — SPEC Pizza Digital + +## 0.6.0 — 2026-10-22 + +- **Demo Operação:** [`SPEC-SIM-OPS-DEMO.md`](SPEC-SIM-OPS-DEMO.md) · UI `/sim` · API `/api/sim/*` +- Motor: entrada Aplicativo/WhatsApp/Telefone · KDS pizzaiolos · estufa · rotas · ETA entrega +- 15 motoboys SIM · até 2000 ops/dia · prep cap 165 min · Joinville · Limpar dados teste + +## 0.5.0 — 2026-10-22 + +- **Paridade 3 vídeos CityFoods:** [`SPEC-CITYFOODS-VIDEO-PARITY.md`](SPEC-CITYFOODS-VIDEO-PARITY.md) **v0.1.0** +- Gap: mapa full-bleed, CTA **Ir para entrega**, rastreio multi-motoboy, pin some ao entregar, filtro forno por sabor +- UI manda sobre painel lateral do Gestor de Rotas (dados/API `/api/routes/*` permanecem) +- P0 desta release: rebuild `/despacho` mapa-first (RF-VID-01…08) + +## 0.4.0 — 2026-10-22 + +- **Gestor de Rotas (CityFoods):** [`SPEC-DESPACHO-CITYFOODS-PARITY.md`](SPEC-DESPACHO-CITYFOODS-PARITY.md) **v0.2.0** +- Fluxo: Montar rota → Despachar → mapa permanente → Motoboy (a caminho/entregue) → fechar +- Dados: `DispatchBatch` / `DispatchStop` · `Order.batchId` · ping `Driver.lastLat/Lng` +- API `/api/routes/*` · UI `/despacho` + `/motoboy` · SPEC mãe §15 S7/S8 → ✅ parcial +- P0–P2 despacho/estufa/frota/zonas/ops documentados no baseline; próximo = iFood (P3) + +## 0.3.4 — 2026-10-19 + +- Origem **Cardápio** renomeada na UI para **Aplicativo** (enum interno `cardapio`) +- **API WhatsApp inbound v0.1:** `POST /api/inbound/whatsapp` + webhook Meta `GET/POST …/meta` +- Auth `X-Api-Key` (`INBOUND_API_KEY`) · idempotência `Order.inboundKey` +- Doc: [`API-WHATSAPP-INBOUND.md`](API-WHATSAPP-INBOUND.md) + +## 0.3.3 — 2026-10-19 + +- **Tamanhos + split no pedido:** Broto / Pequena / Média / Grande / Gigante / Família +- Regras: Broto+Pequena = 1 sabor · Média ≤2 · Grande ≤3 · Gigante+Família ≤4 +- cm / fatias / referência em pratos de mesa · `GET /api/menu/sizes` · sheet PDV +- Preço = maior sabor × fator do tamanho (base = Grande 40cm) +- Seed: sabores sem sufixo G; SKUs Broto legados desativados + +## 0.3.2 — 2026-10-19 + +- **Matriz de Acesso MVP:** [`SPEC-MATRIZ-ACESSO.md`](SPEC-MATRIZ-ACESSO.md) +- UI `/acessos` · API `/api/access/*` · templates ≠ vínculos efetivos +- Caps: `menu.*` · `staff.*` · `permissions.manage` · `owners.manage` · `finance.view` +- Auditoria proposta `AccessAuditLog` · migração `20261019190000_access_matrix` + +## 0.3.1 — 2026-10-19 + +- SPECs irmãs: [`SPEC-CARDAPIO.md`](SPEC-CARDAPIO.md) ↔ [`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md) +- Ponte obrigatória: `stockMode` (`bom`/`sku`/`none`) + ficha técnica (BOM) +- RF-MEN-08…12 · **RF-EST-01…08** (Track M/E) +- Domínio: Recipe, Ingredient, StockMovement +- Pesquisa regional mantida em [`CARDAPIO-JOINVILLE.md`](CARDAPIO-JOINVILLE.md) +- **Quantização por peso:** 38 pizzas com BOM em gramas — [`FICHAS-TECNICAS-PESO.md`](FICHAS-TECNICAS-PESO.md) + [`data/fichas-pizzas-peso.json`](data/fichas-pizzas-peso.json) · RF-EST-13…15 · RF-MEN-15 + +## 0.3.0 — 2026-10-19 + +- **Prioridade de produto:** entrada por canais digitais (pós-piloto S5) +- Novo capítulo **§5.5** — origin × fulfillment, quadro único `/pedidos`, Track C1/C2 +- Documento satélite [`docs/SPEC-CANAIS-DIGITAIS.md`](SPEC-CANAIS-DIGITAIS.md) +- RFs: RF-CAN-01…08 · RF-PED-08/09 · RF-MEN-04/06/07 · RF-INT-01/03/05 +- Fase 3 e S9 alinhados a Track C (cardápio → `/pedidos`; iFood = C2) +- Fluxo §6.1: canais digitais + PDV → Order Ingest → `/pedidos` → KDS +- **Implementado (Central):** `Order.origin`/`fulfillment`, badges + filtros + som no `/pedidos`, `POST /api/orders/demo-digital` + +## 0.2.4 — 2026-07-23 + +- **KDS claim exclusivo (CityFoods):** fila comum → PEGAR → outras estações bloqueadas +- API: `POST /api/kds/:station/claim/:orderId`, `POST /api/kds/:station/orders/:orderId/bump` +- Campos: `Order.claimedStationId` / `claimedAt` +- UI: secções Fila / Comigo / Bloqueados + +## 0.2.3 — 2026-07-23 + +- **S3 entregue:** KDS multi-estação realtime +- API: `GET /api/kds/:station`, `POST /api/kds/tickets/:id/bump` +- Expedição bloqueada até outras estações concluírem +- Auto `order.ready` quando todos os tickets fecham +- UI `/kds/:station` + Socket.io (`ticket.bumped`, timers SLA) + +## 0.2.2 — 2026-07-23 + +- **S2 entregue:** PDV + Order/OrderItem/OrderTicket + state machine +- API: `POST/GET /api/orders`, `PATCH .../status`, `PATCH .../urgent` +- WS: `order.created` / `order.updated` / `order.ready` / `order.canceled` +- Gate: `em_producao → pronto` gera `order.ready` (ponte despacho) +- UI PDV em `https://pizza.myvexx.com/pdv` + +## 0.2.1 — 2026-07-23 + +- Sequência oficial **S0–S9** congelada (§15) +- Prioridade: **KDS multi-estação antes** de delivery/rotas +- **rusterando** marcado como referência oficial de dispatch / ORS / driver tour +- Capítulo §15.2 Módulo Rotas (CityFoods → Pizza Digital) +- Gate: `order.ready` → fila despacho → batch → ORS (S6+) +- Design UI: explícito como ainda não congelado (§15.4) +- S1 marcado ✅; próximo = S2 (PDV) + +## 0.2.0 — 2026-07-23 + +- Ampliação Fase 1 (`docs/SPEC-FASE1.md`) +- Infra: VM 125 `pizza-digital` no Proxmox BIG (`10.10.10.125`) +- DNS: `pizza.myvexx.com` → CNAME tunnel Cloudflare (proxied) +- Traefik CT114: `pizza.myvexx.com` → `http://10.10.10.125:3000` +- Stack Fase 1 congelada (NestJS + React + Postgres + Redis + Socket.io) +- Host de desenvolvimento documentado no SPEC principal + +## 0.1.0 — 2026-07-23 + +- Draft inicial do zero +- Baseado na Cozinha Digital CityFoods (`@cityfoods_app`) +- Referências: kitchenOS, rusterando, URY Mosaic, MaSoVa, Tasty-Station +- Escopo MVP: PDV + KDS multi-estação (pizzaria) + etiquetas + métricas +- Fases 2–3: rotas, cardápio público, iFood, multi-tenant diff --git a/Pizza-Digital/docs/FICHAS-TECNICAS-PESO.md b/Pizza-Digital/docs/FICHAS-TECNICAS-PESO.md new file mode 100644 index 0000000..393b401 --- /dev/null +++ b/Pizza-Digital/docs/FICHAS-TECNICAS-PESO.md @@ -0,0 +1,111 @@ +# Fichas técnicas — quantização por **peso (g)** + +| Campo | Valor | +|-------|--------| +| **Fonte máquina** | [`data/fichas-pizzas-peso.json`](data/fichas-pizzas-peso.json) | +| **SPEC** | [`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md) §4 · [`SPEC-CARDAPIO.md`](SPEC-CARDAPIO.md) | +| **Versão** | 0.1.0 · 2026-10-19 | +| **Pizzas cobertas** | **38** (20 trad. G + 5 broto + 6 especiais + 7 doces) | + +--- + +## 1. Regra de quantização + +```text +baixa_insumo_g = RecipeLine.qty_g × OrderItem.quantity +meia-a-meia = 0,5 × BOM_sabor_A + 0,5 × BOM_sabor_B +borda = BOM própria (catupiry/cheddar) somada à pizza +``` + +- Unidade canônica dos toppings/massa: **gramas (g)** +- Ovo: **un** (exceção) +- Bebidas continuam `stockMode=sku` (não entram neste catálogo) + +--- + +## 2. Base por tamanho + +| Tamanho | Massa | Molho | Mussarela base | Fator vs G | +|---------|------:|------:|---------------:|-----------:| +| **Grande 40 cm** | 480 g | 90 g | 200 g | 1,00 | +| **Broto 25 cm** | 200 g | 40 g | 85 g | ≈ 0,42 | + +Peso total aproximado da pizza montada (antes do forno): **~770–1050 g (G)** · **~320–430 g (Broto)**. + +--- + +## 3. Resumo — Tradicionais Grande (20) + +| # | Pizza | Peso ≈ | Massa | Mussarela | Topping principal | +|---|-------|-------:|------:|----------:|-------------------| +| 1 | Calabresa G | 930 g | 480 | 200 | calabresa 120 g + cebola 40 g | +| 2 | Mussarela G | 772 g | 480 | 200 | — | +| 3 | Frango Catupiry G | 992 g | 480 | 180 | frango 150 + catupiry 90 | +| 4 | Portuguesa G | 1012 g | 480 | 180 | presunto 100 + 2 ovos + cebola/azeitona | +| 5 | 4 Queijos G | 912 g | 480 | 120 | provolone 80 + gorgonzola 60 + catupiry 100 | +| 6 | Marguerita G | 852 g | 480 | 180 | tomate 80 + parmesão 20 + manjericão 8 | +| 7 | Bacon G | 892 g | 480 | 200 | bacon 120 | +| 8 | Pepperoni G | 922 g | 480 | 200 | pepperoni 120 + pimentão 30 | +| 9 | Lombo Catupiry G | 962 g | 480 | 180 | lombo 120 + catupiry 90 | +| 10 | Baiana G | 902 g | 480 | 180 | calabresa moída 130 + pimenta 20 | +| 11 | Napolitana G | 842 g | 480 | 180 | tomate 70 + parmesão 20 | +| 12 | Presunto G | 872 g | 480 | 200 | presunto 100 | +| 13 | Palmito G | 892 g | 480 | 200 | palmito 120 | +| 14 | Brócolis G | 942 g | 480 | 180 | brócolis 120 + bacon 70 | +| 15 | Toscana G | 922 g | 480 | 180 | calabresa moída 130 + cebola 40 | +| 16 | Moda da Casa G | 1002 g | 480 | 180 | presunto+tomate+milho+ervilha | +| 17 | Atum G | 932 g | 480 | 180 | atum 140 + cebola 40 | +| 18 | Crocante G | 922 g | 480 | 200 | bacon 100 + batata palha 50 | +| 19 | Estrogonofe Frango G | 1002 g | 480 | 160 | estrogonofe 250 + batata palha 50 | +| 20 | Vegetariana G | 972 g | 480 | 180 | brócolis+palmito+milho+ervilha | + +## 4. Broto (5) · Especiais SC (6) · Doces (7) + +Detalhe linha a linha no JSON. Destaques especiais Joinville: + +| Pizza | Peso ≈ | Destaque (g) | +|-------|-------:|--------------| +| Joinville G | 852 | alho 20 · parmesão 30 · pimentão 40 | +| Linguiça Blumenau G | 982 | linguiça 140 · catupiry 90 | +| Aipim c/ Bacon G | 1012 | aipim 200 · bacon 120 | +| Catarina G | 942 | calabresa moída 120 · 2 ovos | +| Aipim Blumenau G | 1052 | aipim 200 · linguiça 140 | +| Coração G | 972 | coração 140 · catupiry 80 | + +--- + +## 5. Exemplo de baixa no pedido + +Pedido: **1× Calabresa G + 1× Coca 2L** + +| Insumo | Movimento | +|--------|-----------| +| Massa | −480 g | +| Molho | −90 g | +| Mussarela | −200 g | +| Calabresa | −120 g | +| Cebola | −40 g | +| Orégano | −2 g | +| Coca 2L (sku) | −1 un | + +Meia-a-meia **Calabresa / Frango Catupiry** (1 pizza G): + +| Insumo | Cálculo | +|--------|---------| +| Massa / molho | 100% da base G (1 disco) | +| Mussarela | 0,5×200 + 0,5×180 = **190 g** | +| Calabresa | 0,5×120 = **60 g** | +| Frango | 0,5×150 = **75 g** | +| Catupiry | 0,5×90 = **45 g** | +| Cebola | 0,5×40 = **20 g** | + +--- + +## 6. Próximo passo E1 + +1. Importar `ingredientsCatalog` → tabela `Ingredient` +2. Importar `recipes[]` → `Recipe` + `RecipeLine` +3. Ligar `menuItemId` do seed +4. Baixa automática em `em_producao` + +Pesos são **padrão piloto Joinville** — ajustáveis por loja na UI de ficha técnica. diff --git a/Pizza-Digital/docs/MIGRATION-ACCESS-MATRIX.md b/Pizza-Digital/docs/MIGRATION-ACCESS-MATRIX.md new file mode 100644 index 0000000..77c3b00 --- /dev/null +++ b/Pizza-Digital/docs/MIGRATION-ACCESS-MATRIX.md @@ -0,0 +1,38 @@ +# Migração — Matriz de Acesso + +**Não executar em produção sem revisão.** + +## Lab / VM125 + +```bash +cd ~/pizza-digital +# 1. Atualizar código (já inclui prisma/migrations/20261019190000_access_matrix) +docker compose build api web +docker compose up -d api web +# entrypoint já roda: prisma migrate deploy + seed +docker logs pizza-api --tail 40 +``` + +## Verificação + +```bash +# login gestor +curl -s -X POST http://127.0.0.1:3000/api/auth/login \ + -H 'Content-Type: application/json' \ + -d '{"email":"gestor@pizza.local","password":"805353","storeSlug":"pizza-digital-piloto"}' + +# matriz (Bearer token) +curl -s http://127.0.0.1:3000/api/access/matrix -H "Authorization: Bearer $TOKEN" + +# pdv não deve salvar matriz (403) +``` + +## Rollback (lab) + +```sql +DROP TABLE IF EXISTS "AccessAuditLog"; +DROP TABLE IF EXISTS "AccessProfileTemplate"; +DROP TABLE IF EXISTS "UserCapability"; +ALTER TABLE "User" DROP COLUMN IF EXISTS "profileTemplateKey"; +DELETE FROM "_prisma_migrations" WHERE migration_name = '20261019190000_access_matrix'; +``` diff --git a/Pizza-Digital/docs/PROMPT-CENTRAL-PEDIDOS.md b/Pizza-Digital/docs/PROMPT-CENTRAL-PEDIDOS.md new file mode 100644 index 0000000..cff49d3 --- /dev/null +++ b/Pizza-Digital/docs/PROMPT-CENTRAL-PEDIDOS.md @@ -0,0 +1,161 @@ +# Context Prompt — Central de Pedidos (Cursor) + +> Cole o bloco **PROMPT** abaixo num chat Cursor (Agent) no repo `pizza-digital`. +> Spec: `SPEC.md` §5.5 · `docs/SPEC-CANAIS-DIGITAIS.md` · RF-PED / RF-CAN +> Código base: `apps/web/src/PedidosPage.tsx` · rota `/pedidos` + +--- + +## PROMPT (copiar daqui) + +```text +# Missão +Construir / evoluir o módulo **Central de Pedidos** do produto **Pizza Digital** +(rota `/pedidos`) — o quadro operacional único da loja, estilo CityFoods. + +PT-BR em toda UI e comentários de produto. Código TS em inglês. + +# Contexto do produto +Pizza Digital = SO da cozinha + delivery próprio (não é marketplace). +Referência UX: CityFoods — Cozinha Digital + quadro Pedidos (`@cityfoods_app`). + +A Central de Pedidos é o **hub pós-login**: +- Staff entra → `/pedidos` (não /pdv) +- CTA **+ Novo** → PDV (`/pdv`) para digitar pedido +- Pedidos de **qualquer canal** (PDV, cardápio digital, WhatsApp, iFood, telefone) + caem no **mesmo Order** e aparecem neste quadro +- Daqui a loja vai para KDS, etiqueta e despacho + +Regra de ouro (SPEC §5.5): +> Nenhum canal tem UI de cozinha própria — tudo passa pelo Order bus e pelo `/pedidos`. + +# Dimensões de pedido (alvo — migrar do `channel` legado) + +origin: "pdv" | "cardapio" | "ifood" | "whatsapp" | "telefone" +fulfillment: "entrega" | "retirada" | "mesa" + +Legado atual (Prisma `OrderChannel`): + delivery → origin=pdv, fulfillment=entrega + balcao → origin=pdv, fulfillment=retirada + pdv → origin=pdv, fulfillment=mesa + +UI do card: +- Faixa colorida = fulfillment (ENTREGA / RETIRADA / MESA) + código curto +- Badge secundário = origin (CARDÁPIO / IFOOD / WHATSAPP / PDV / TELEFONE) + +# Stack (não trocar) +- Monorepo: apps/api (NestJS + Prisma + Socket.io) · apps/web (React + Vite + TS) +- DB: PostgreSQL · Redis · Docker na VM 125 +- Auth: JWT via `useAuth` / `authFetch` +- Realtime: Socket.io eventos `order.created` | `order.updated` | `order.ready` | `ticket.bumped` +- Host: https://pizza.itecnologys.com · path /pedidos + +# Arquivos-chave (ler antes de editar) +- SPEC.md (§5.5, §6.1, §7.1 RF-PED, §7.8 RF-CAN, §10) +- docs/SPEC-CANAIS-DIGITAIS.md +- apps/web/src/PedidosPage.tsx ← tela Central +- apps/web/src/PdvPage.tsx ← + Novo +- apps/web/src/App.tsx ← login → /pedidos +- apps/web/src/styles.css ← classes .pedidos-* +- apps/web/src/channelLabel.ts +- apps/api/prisma/schema.prisma ← Order ainda tem `channel` +- apps/api (módulo orders + gateway WS) + +# Estado atual (já no ar) +- Skin CityFoods: header Pedidos / + Novo, chips de tags, grid de cards, aside Histórico +- WS refresh no board +- Filtro ACTIVE = confirmado | em_producao | pronto +- channelTag mapeia delivery/balcao/pdv → ENTREGA/RETIRADA/MESA (ainda via `channel`) +- Marcar pronto, link etiqueta, link KDS +- Ainda NÃO há: origin/fulfillment no schema, badge de origem, som de novo pedido digital, + filtros por fulfillment/origin, cancelar/editar no card, som + highlight de pedido novo + +# O que construir nesta sessão (Central de Pedidos — pacote board) + +## P0 — modelo + API +1. Migrar Order: adicionar `origin` + `fulfillment` (manter `channel` deprecated ou mapear na leitura) +2. Create/list orders aceitam e devolvem origin/fulfillment +3. Compat: PDV continua mandando channel legado → API normaliza para origin+fulfillment +4. WS payload inclui origin + fulfillment + +## P1 — UI Central `/pedidos` +1. Card: faixa = fulfillment; badge = origin (CARDÁPIO / IFOOD / …) +2. Som + flash no card quando `order.created` (especialmente origin digital) +3. Filtros: Todos | Entrega | Retirada | Mesa + opcional por origin +4. Ordenação: horário (default) | nº pedido | urgente primeiro +5. Histórico lateral: últimos prontos/em_rota/entregue com origin +6. Ações no card: ✓ pronto (se em_producao) · etiqueta · abrir KDS · (depois) cancelar +7. Empty state: “Nenhum pedido” + CTA + Novo +8. Touch-first, legível a 1 m; pt-BR; R$ via money.ts + +## P2 — ponte canais (não implementar cardápio inteiro aqui) +- Garantir que POST /api/orders com origin=cardapio|ifood|whatsapp aparece no board ≤ 500 ms +- Badge correto; fulfillment escolhido pelo cliente +- Stub: endpoint/fixture que simula pedido digital para testar o board sem PWA + +# UX CityFoods a preservar +- Faixa ENTREGA/RETIRADA no topo do card +- Tags de sabor/components sob o item +- “Preparo tardio” se em_producao > 20 min +- Histórico com check à direita +- + Novo em destaque no header +- Sem dashboard genérico; um quadro operacional + +# Critérios de aceite +1. Login → /pedidos abre o quadro +2. + Novo → /pdv → criar pedido → volta a aparecer no quadro via WS sem F5 +3. Pedido simulado origin=cardapio mostra badge CARDÁPIO + faixa ENTREGA/RETIRADA +4. Latência visual ≤ 500 ms após order.created +5. KDS e etiqueta continuam funcionando a partir do mesmo Order +6. Mobile/tablet: cards usáveis com dedo; sem regressão no PDV + +# Fora de escopo nesta sessão +- PWA cardápio /c/:slug completo (Track C1.1–C1.3 — sessão separada) +- Webhook iFood real (C2) +- Rotas / ORS / motoboy +- Redesign do KDS +- Multi-tenant SaaS + +# Como trabalhar +1. Ler SPEC §5.5 + PedidosPage + schema Order +2. Mudar uma fatia por vez (schema → API → UI → fixture digital) +3. Não inventar libs novas; seguir padrões Nest/React do repo +4. Depois de UI: rebuild web se deploy Docker; validar /pedidos +5. Atualizar docs/SPEC-CANAIS-DIGITAIS.md se fechar um critério C1.x + +Comece pelo gap origin/fulfillment (API + badges no PedidosPage). Mostre o plano curto e implemente. +``` + +--- + +## Prompt curto (follow-up / hillclimb) + +```text +Continua a Central de Pedidos (/pedidos) no Pizza Digital. +SPEC §5.5 · origin×fulfillment · badge origem · WS ≤500ms · CityFoods skin. +Não faça o cardápio PWA agora — só o board + normalização do Order. +Arquivo principal: apps/web/src/PedidosPage.tsx + orders API + prisma Order. +``` + +--- + +## Mapa mental (1 tela) + +```text + ┌─────────────────────────────────────────┐ +Canais → │ CENTRAL DE PEDIDOS /pedidos │ → KDS + │ + Novo → PDV │ → Etiqueta + │ cards: fulfillment + badge origin │ → Despacho + └─────────────────────────────────────────┘ +``` + +## Checklist de sessão + +| # | Item | Done | +|---|------|------| +| 1 | `origin` + `fulfillment` no Prisma/API | ☐ | +| 2 | Badges no card PedidosPage | ☐ | +| 3 | Som/flash `order.created` | ☐ | +| 4 | Filtros fulfillment | ☐ | +| 5 | Fixture pedido digital | ☐ | +| 6 | Aceite WS ≤ 500 ms | ☐ | diff --git a/Pizza-Digital/docs/RELEASE-v0.0.0.md b/Pizza-Digital/docs/RELEASE-v0.0.0.md new file mode 100644 index 0000000..ef24db5 --- /dev/null +++ b/Pizza-Digital/docs/RELEASE-v0.0.0.md @@ -0,0 +1,28 @@ +# Pizza Digital v0.0.0 + +**Data:** 2026-07-23 +**Produto:** Cozinha Digital / PDV / KDS / Expedição / Despacho (piloto) +**URL:** https://pizza.myvexx.com +**VM runtime:** 125 (`pizza-digital` · `10.10.10.125`) +**Vault Obsidian (BIG1 VM123):** `/root/obsidian-infra/ligbox-ops-platform/docs/pizza-digital/` + +## Remotes GitHub (v0.0.0) + +| Remote | Repo | Nota | +|--------|------|------| +| Itecnologys | `itecnologys-tech/pizza-digital` | repo oficial org | +| MyVexx mirror | `itecnologys/pizza-digital-myvexx` | mirror produto myvexx.com (sem org myvexx no token) | + +Tag: **`v0.0.0`** + +## Módulos nesta versão + +- PDV — canais Entrega / Balcão / Mesa · IP origem +- KDS — Massa / Montagem / Forno / Bebidas · PEGAR · BUMP · IMPRIMIR +- Expedição — só pedidos `pronto` (sem PEGAR) · SAIR +- Despacho — fila `pronto` → `em_ruta` +- Etiqueta — código · hash · componentes da pizza · IP + +## Spec + +Ver `SPEC.md` §15 (S0–S3 ✅ · S4 parcial · S6+ rotas pendente). diff --git a/Pizza-Digital/docs/SPEC-CANAIS-DIGITAIS.md b/Pizza-Digital/docs/SPEC-CANAIS-DIGITAIS.md new file mode 100644 index 0000000..9bc83ec --- /dev/null +++ b/Pizza-Digital/docs/SPEC-CANAIS-DIGITAIS.md @@ -0,0 +1,114 @@ +# SPEC — Entrada por Canais Digitais + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Capítulo** | SPEC §5.5 · RF-CAN · Track C | +| **Versão** | 0.3.0 | +| **Data** | 2026-10-19 | +| **Owner** | Roger / iTecnologys | +| **Referência UX** | CityFoods Cardápio Digital + quadro Pedidos | +| **Cardápio (catálogo)** | [`SPEC-CARDAPIO.md`](SPEC-CARDAPIO.md) | +| **Estoque** | [`SPEC-ESTOQUE.md`](SPEC-ESTOQUE.md) — disponibilidade no PWA | + +--- + +## 1. Objetivo + +Fazer o cliente pedir **sem passar pelo balcão**, e a loja ver o pedido no mesmo quadro operacional (`/pedidos`) que já usa para PDV/KDS. + +**Promessa:** menos telefone · menos erro de digitação · mais ticket digital · mesma cozinha. + +--- + +## 2. Modelo de dados (alvo) + +### 2.1 Dimensões + +```ts +origin: "pdv" | "cardapio" | "ifood" | "whatsapp" | "telefone" +fulfillment: "entrega" | "retirada" | "mesa" +``` + +### 2.2 Migração do `channel` atual + +| Legado `channel` | `origin` | `fulfillment` | +|------------------|----------|---------------| +| `delivery` | `pdv` | `entrega` | +| `balcao` | `pdv` | `retirada` | +| `pdv` | `pdv` | `mesa` | + +Novos pedidos digitais: `origin=cardapio` (ou `ifood`) + fulfillment escolhido pelo cliente. + +--- + +## 3. Track C — sprints + +| ID | Entrega | Dependência | +|----|---------|-------------| +| **C1.1** | Rotas públicas `/c/:slug` (cardápio PWA) | Menu API + loja aberta/fechada | +| **C1.2** | Carrinho + checkout (entrega/retirada, bairro, taxa) | Áreas de entrega | +| **C1.3** | Pagamento: Pix (stub/real) + “pagar na entrega” | Gateway Pix | +| **C1.4** | Create Order `origin=cardapio` → WS → `/pedidos` | Schema origin/fulfillment | +| **C1.5** | Badge origem + som no board | UI `/pedidos` | +| **C1.6** | Deep-link WhatsApp (`wa.me` → `/c/:slug`) | C1.1 | +| **C1.7** | API inbound WhatsApp (`POST /api/inbound/whatsapp`) | ✅ v0.1 — ver [`API-WHATSAPP-INBOUND.md`](API-WHATSAPP-INBOUND.md) | +| **C2.1** | Adaptador iFood (webhook + assinatura) | Conta loja iFood | +| **C2.2** | Normalizador iFood → Order + idempotência | C1.4 | + +**Gate:** C1 só começa com S3 estável; idealmente após S5 (piloto). Pode prototipar C1.1 em paralelo. + +--- + +## 4. Fluxo C1 — cardápio digital + +```text +Cliente abre /c/pizza-digital-piloto + → vê cardápio (categorias / pizzas / bebidas) + → monta carrinho (meia-a-meia / borda / obs) + → escolhe Entrega ou Retirada + → se Entrega: bairro + endereço + taxa + → pagamento (Pix ou na entrega) + → POST /api/orders { origin: "cardapio", fulfillment, items, ... } + → loja: card novo em /pedidos (ENTREGA/RETIRADA + badge CARDÁPIO) + → KDS recebe como qualquer pedido +``` + +--- + +## 5. UX quadro `/pedidos` (já no ar) + +| Elemento CityFoods | Nosso | +|--------------------|--------| +| Faixa ENTREGA / RETIRADA | `fulfillment` | +| Tags de sabor | components + notes | +| Preparo tardio | SLA > 20 min | +| Histórico | pedidos recentes | +| **Novo:** badge origem | APLICATIVO / IFOOD / PDV / WHATSAPP | + +--- + +## 6. Fora de escopo (C1) + +- App nativo iOS/Android consumidor +- Marketplace multi-loja Pizza Digital +- Fidelidade / cashback (fase posterior) +- Chat in-app com a loja + +--- + +## 7. Critérios de aceite C1 + +1. Cliente finaliza pedido no cardápio sem login de staff. +2. Pedido aparece no `/pedidos` em ≤ 500 ms (WS) com badge **CARDÁPIO**. +3. KDS processa igual a pedido PDV. +4. Etiqueta mostra origem + fulfillment. +5. Loja fechada → cardápio bloqueia checkout com mensagem clara. + +--- + +## 8. Próximo passo de implementação + +1. ~~Migrar modelo `origin` + `fulfillment` (API + UI badges).~~ ✅ Central `/pedidos` + `POST /api/orders/demo-digital` +2. Scaffold PWA `/c/:slug`. +3. Ligar create order cardápio público → board (já validável via fixture). diff --git a/Pizza-Digital/docs/SPEC-CARDAPIO.md b/Pizza-Digital/docs/SPEC-CARDAPIO.md new file mode 100644 index 0000000..6240668 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-CARDAPIO.md @@ -0,0 +1,197 @@ +# 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.* diff --git a/Pizza-Digital/docs/SPEC-CITYFOODS-VIDEO-PARITY.md b/Pizza-Digital/docs/SPEC-CITYFOODS-VIDEO-PARITY.md new file mode 100644 index 0000000..a491a96 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-CITYFOODS-VIDEO-PARITY.md @@ -0,0 +1,142 @@ +# SPEC — Paridade UI/ops com 3 vídeos CityFoods + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Documento** | Gap analysis + requisitos de UI idêntica em operação | +| **Versão** | 0.1.0 | +| **Data** | 2026-10-22 | +| **Alinha com** | SPEC-DESPACHO-CITYFOODS-PARITY v0.2 · SPEC.md §15 | +| **Vídeos** | [Overview yVtc](https://www.youtube.com/watch?v=yVtcgJt0L_Q) · [Forno Jxau](https://www.youtube.com/watch?v=JxauDKXTJr0) · [Despacho+mapa KDcD](https://www.youtube.com/watch?v=KDcDTGEw11A) | + +--- + +## 0. Objetivo + +Reconstruir a superfície operacional para **a mesma interface e operacionalidade** mostrada nos três vídeos — não só “capacidade de backend”. +Prioridade absoluta do produto: **mapa como bloco inteiro na tela**, capaz de rastrear todas as entregas/motoboys ao vivo. + +--- + +## 1. Narrativa por vídeo (transcrição) + +### V1 — Overview Siciliana / Mangue Hills (`yVtcgJt0L_Q`) + +- Despacho = “coração operacional”: frota casa/rua, canal, nº pedido, tempos, reordenação → KDS. +- Cores de estágio: vermelho (novo) · laranja (produção) · amarelo (forno). +- Rotas inteligentes: pedidos próximos saem juntos. +- Cotação iFood vs frota própria (fora de escopo imediato — P3). +- Conferência/estufa com etiqueta → aparece no despacho. +- Ranking ao vivo (pizzas/noite, tempo médio) + funil (produção/forno/estufa/rua). + +### V2 — Forno / filtro por sabor (`JxauDKXTJr0`) + +- KDS forno touch: filtrar por **sabor** (ex.: calabresa → Napoli) para achar a comanda na noite cheia. +- Confirmar item encontrado → bump; quando todos os itens do pedido estão ok → pedido **pronto** para despacho. +- Impressão opcional por etapa. + +### V3 — Despacho + rastreio (`KDcDTGEw11A`) — **âncora do mapa** + +1. Tela de despacho: pedidos **verdes** = prontos aguardando entregador (tempo / atraso). +2. Selecionar N pizzas que formam uma “roda” (rota). +3. Escolher entregador do dia. +4. Botão **Ir para entrega** → pedidos saem da visualização de prontos e vão para o app do entregador. +5. Mapa de rastreio: **todas as entregas de todos os entregadores** em tempo real + pin do motoboy. +6. Filtro por entregador (ex.: Steve) → só as paradas dele. +7. Entregue no app → pin **some** do mapa; última entrega → motoboy apto a voltar à casa. +8. Relatório noite: entregas, km, ranking pizzaiolo (já coberto em `/ops`). + +--- + +## 2. Gap vs Pizza Digital (baseline pós-Gestor de Rotas) + +| Capacidade vídeo | Baseline | Gap | +|------------------|----------|-----| +| Pedidos verdes prontos no despacho | Estufa/pronto com cards | ⚠️ Visual não “verde pronto” unificado | +| Seleção + “Ir para entrega” (1 clique) | Montar rota → Despachar (2 passos) | ❌ CTA e fluxo do vídeo | +| Mapa **bloco inteiro** / viewport dominante | Painel lateral sticky ~320px | ❌ Layout | +| Rastreio **todos** motoboys + paradas | Mapa foca 1 rota ativa | ❌ Multi-rota / multi-driver | +| Filtro mapa por entregador | Tabs de rota | ⚠️ Parcial | +| Pin some ao entregar | Stop `entregue` ainda desenhado | ❌ Deve omitir pin | +| App motoboy navegar Maps/Waze | Deep-link Google | ✅ parcial | +| Forno filtro por sabor | KDS claim/bump sem filter sabor | ❌ V2 (próximo) | +| Ranking / funil | `/ops` | ✅ | +| iFood cotação | — | ❌ P3 | + +--- + +## 3. Requisitos novos (UI parity) + +### Tela D2 — Despacho mapa-first (`/despacho`) + +| ID | Requisito | Pri | +|----|-----------|-----| +| RF-VID-01 | Layout: **mapa = bloco dominante full-bleed** (≥ ~55–70% do viewport útil); lista de prontos + frota em dock lateral/inferior, sem esconder o mapa | P0 | +| RF-VID-02 | Lista unificada de **prontos verdes** (estufa + pronto sem rota); tempo e atraso visíveis | P0 | +| RF-VID-03 | Seleção múltipla + entregador + CTA primário **Ir para entrega** (= montar batch + despachar na rua numa ação) | P0 | +| RF-VID-04 | Ao Ir para entrega: pedidos saem da lista; entram no app motoboy; pins aparecem no mapa | P0 | +| RF-VID-05 | Mapa mostra **todas** as rotas `na_rua` (e preview/montada) + pins de **todos** os motoboys com GPS | P0 | +| RF-VID-06 | Chip/filtro por entregador: “todos” ou 1 motoboy — atualiza pins | P0 | +| RF-VID-07 | Parada `entregue` **não** desenha pin (some do rastreio) | P0 | +| RF-VID-08 | Socket: `route.*` / `driver.ping` / `order.delivered` redesenham o mapa sem reload manual | P0 | +| RF-VID-09 | Manter atalhos avançados (Preview / Montar sem despachar) em menu secundário | P1 | +| RF-VID-10 | KDS forno: filtro por sabor (chips) + confirmar item — ver RF-FOR-* | P1 | + +### Tela KDS Forno (V2) + +| ID | Requisito | Pri | +|----|-----------|-----| +| RF-FOR-01 | Em `/kds/forno` (ou estação forno): chips de sabores presentes na fila | P1 | +| RF-FOR-02 | Selecionar 1–N sabores filtra tickets que contenham esses itens | P1 | +| RF-FOR-03 | Confirmar item encontrado (bump parcial) até pedido completo → pronto/estufa | P1 | + +--- + +## 4. Layout alvo (D2) + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Nav · Despacho · ao vivo · Motoboy · Ops │ +├──────────────┬──────────────────────────────────────────────┤ +│ Prontos ● N │ │ +│ [✓] #003 … │ MAPA FULL-BLEED │ +│ [✓] #013 … │ (todas entregas + motoboys) │ +│ [✓] #004 … │ │ +│ │ │ +│ Motoboy ▾ │ │ +│ [Ir p/ entrega] │ +│ Frota chips │ Filtro mapa: Todos | Steve | … │ +└──────────────┴──────────────────────────────────────────────┘ +``` + +Mobile: mapa no topo (min-height ~45vh); dock de prontos abaixo. + +--- + +## 5. Aceite (definição de pronto desta SPEC) + +1. Abrir `/despacho` → mapa ocupa o bloco principal da tela (não coluna estreita). +2. Selecionar 2+ pedidos verdes + motoboy → **Ir para entrega** → somem da lista e aparecem no mapa + `/motoboy`. +3. Com 2 motoboys na rua, o mapa mostra paradas/pins de ambos; filtrar um esconde o outro. +4. Marcar entregue no motoboy → pin correspondente some imediatamente (socket). +5. SPEC-DESPACHO v0.2 continua válida para modelo de dados; este doc manda na **UI**. + +--- + +## 6. Fora de escopo imediato + +- Cotação iFood / marketplace (V1 / P3) +- Cópia pixel-perfect de assets proprietários CityFoods +- Offline motoboy / PIN + +--- + +## 7. Sequência de construção + +1. **Agora:** RF-VID-01…08 — rebuild `/despacho` mapa-first + Ir para entrega +2. Em seguida: RF-FOR-01…03 — filtro forno +3. Depois: P3 iFood + +--- + +*Cruzamento das 3 transcrições × SPEC-DESPACHO v0.2 × pizza.itecnologys.com — 2026-10-22.* diff --git a/Pizza-Digital/docs/SPEC-DESPACHO-CITYFOODS-PARITY.md b/Pizza-Digital/docs/SPEC-DESPACHO-CITYFOODS-PARITY.md new file mode 100644 index 0000000..63ab652 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-DESPACHO-CITYFOODS-PARITY.md @@ -0,0 +1,397 @@ +# SPEC — Paridade Despacho / Delivery (referência CityFoods) + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Documento** | Paridade operacional com CityFoods (expedição + delivery + admin) | +| **Versão** | 0.2.1 | +| **Data** | 2026-10-22 | +| **Status impl.** | P0–P2 + Gestor · UI mapa-first em [`SPEC-CITYFOODS-VIDEO-PARITY.md`](SPEC-CITYFOODS-VIDEO-PARITY.md) | +| **Vídeo referência** | [Siciliana yVtc](https://www.youtube.com/watch?v=yVtcgJt0L_Q) · [Forno Jxau](https://www.youtube.com/watch?v=JxauDKXTJr0) · [Despacho KDcD](https://www.youtube.com/watch?v=KDcDTGEw11A) | +| **Case** | ~R$ 1M/mês · ~70% iFood · hub multi-marca · entrega < 30 min | +| **Alinha com** | SPEC §15 S4→S8 · SPEC-FASE1 §6 · rusterando (rotas) | +| **Fora deste doc** | Cardápio público PWA (C1), fidelidade, caixa completo | + +--- + +## 0. Objetivo + +Elevar o Pizza Digital do estado atual (**lista de prontos + SAIR**) ao nível das telas CityFoods do vídeo: + +1. **Despacho** como cockpit (não só fila) +2. **Conferência / estufa** como etapa formal +3. **Frota** (casa / rua) + atribuição +4. **Rotas** e, depois, marketplace / app entregador +5. **Admin** de zonas, ranking e relatórios operacionais + +**Princípio de sequência (já na SPEC mãe):** +KDS estável → despacho rico → frota → ORS → app motoboy → iFood. + +--- + +## 1. Estado atual (baseline Pizza Digital) + +| Tela / capacidade | Rota / artefato | Status | +|-------------------|-----------------|--------| +| Central de pedidos | `/pedidos` | ✅ | +| KDS multi-estação | `/kds/:station` | ✅ | +| Expedição (KDS) | `/kds/expedicao` · → ESTUFA | ✅ | +| Etiqueta | `/etiqueta/:id` | ✅ | +| Despacho cockpit | `/despacho` | ✅ P0–P2 | +| Frota / motoboy | `/despacho` frota + `/api/drivers` | ✅ | +| Conferência / estufa | `Order.estufaAt` + Expedição | ✅ | +| **Gestor de Rotas** | `DispatchBatch` · mapa permanente | ✅ (heurística; ORS com `ORS_API_KEY`) | +| App entregador | `/motoboy` | ✅ parcial (sem PIN próprio / offline) | +| Zonas / taxas | `/admin/zonas` | ✅ CRUD (PDV ainda não aplica taxa auto) | +| Ranking / funil / relatório | `/ops` | ✅ | +| iFood no fluxo | — | ❌ P3 / C2 | + +--- + +## 2. Narrativa das telas necessárias (SPEC por tela) + +Cada subsecção = **uma tela** (ou superfície). Inclui: papel, o que o CityFoods mostra no vídeo, requisitos RF, dados, eventos, aceite. + +--- + +### Tela A — Cockpit de Despacho (`/despacho`) + +**Papel:** operador de despacho / gestor de turno. +**No vídeo:** “coração operacional” — lista de pedidos + frota + tempos + cores + reordenação. + +#### A.1 O que deve mostrar + +| Coluna / bloco | Descrição | +|----------------|-----------| +| Frota (topo ou lateral) | Motoboys em **casa** / **rua**, nº de entregas ativas | +| Pedido | Código, canal (PDV / App / iFood / WhatsApp), fulfillment | +| Tempos | Hora do pedido · tempo na loja · tempo desde `pronto` / estufa | +| Estágio visual | Cor: novo · em produção · forno · estufa · em rota | +| Destino | Bairro + endereço curto | +| Itens resumidos | Qtd × sabor (sem ficha completa) | +| Ações | Selecionar · atribuir motoboy · SAIR / batch | + +#### A.2 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-DSP-01 | Listar pedidos em `pronto` e `na_estufa` em tempo real (Socket.io) | S4 / S6 | +| RF-DSP-02 | Exibir canal, código, tempos (pedido / loja / estufa) | S4 | +| RF-DSP-03 | Faixa de cor por estágio de produção espelhado do KDS | S4 | +| RF-DSP-04 | Seleção múltipla de pedidos para batch de saída | S6 | +| RF-DSP-05 | Atribuir entregador antes ou no SAIR | S6 | +| RF-DSP-06 | SAIR → status `em_rota` + `dispatchedAt` + entregadorId | S6 | +| RF-DSP-07 | Reordenar fila de prioridade; persistir; refletir no KDS | S6 | + +#### A.3 Aceite + +- Despachante vê, sem sair da tela, **quem está livre**, **o que está na estufa** e **há quanto tempo**. +- Um clique em SAIR com motoboy atribuído remove o pedido da fila de prontos. + +--- + +### Tela B — Conferência / Estufa + +**Papel:** conferente (login próprio). +**No vídeo:** identifica sabor → imprime etiqueta → pedido vai para estufa no despacho. + +#### B.1 Fluxo + +``` +KDS BUMP (forno) → fila Conferência + → conferente confirma sabores / qtds + → imprime etiqueta (código + sabores) + → status na_estufa + → aparece no Cockpit Despacho como “estufa” +``` + +#### B.2 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-CNF-01 | Tela `/conferencia` ou modo dedicado em `/kds/expedicao` | S4 | +| RF-CNF-02 | Login com papel/área `conferente` ou `despacho` | S4 | +| RF-CNF-03 | Checklist de itens/sabores antes de confirmar | S4 | +| RF-CNF-04 | Confirmar → print etiqueta + `na_estufa` + evento `order.ready` enriquecido | S4 | +| RF-CNF-05 | Pedidos na estufa ordenáveis por tempo parado (alerta visual) | S4 | + +#### B.3 Modelo de estado (ampliação) + +``` +em_producao → pronto_cozinha → na_estufa → em_rota → entregue +``` + +> Compatibilidade: enquanto `na_estufa` não existir no enum, usar `pronto` + flag `holdingStation=estufa` **ou** migrar enum (preferível). + +#### B.4 Aceite + +- Sem etiqueta confirmada, pedido **não** entra na fila “estufa” do despacho. +- Tempo parado na estufa é visível e alarmante após N minutos (config loja). + +--- + +### Tela C — Frota / Motoboys + +**Papel:** despacho + gestor. +**No vídeo:** quem está na casa, quem está na rua, quantas entregas cada um carrega. + +#### C.1 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-FRT-01 | Cadastro de entregador (nome, telefone, ativo, loja) | S6 | +| RF-FRT-02 | Status operacional: `casa` \| `rua` \| `offline` | S6 | +| RF-FRT-03 | Contagem de entregas abertas (`em_rota`) por entregador | S6 | +| RF-FRT-04 | Painel no cockpit: lista viva atualizada por socket | S6 | +| RF-FRT-05 | Histórico simples: entregas do turno / canceladas | S6–S8 | + +#### C.2 Aceite + +- Ao despachar, só aparecem entregadores `casa` (ou regra configurável). +- Quando o app marcar entrega, status volta a `casa` se fila zerada. + +--- + +### Tela D — Gestor de Rotas (mapa + batch) + +**Papel:** despacho. +**No vídeo / City Pro:** montar rota → despachar → acompanhar no mapa (não só preview). + +#### D.0 Fluxo canónico (implementado) + +``` +Estufa (seleção ± bairro) + → Preview mapa (opcional) + → Montar rota → DispatchBatch status=montada + → Atribuir motoboy + → Despachar rota ✈ → status=na_rua · orders em_rota · 1ª parada a_caminho + → Motoboy: a caminho / entregue por parada (+ ping GPS) + → Última entrega → status=fechada · motoboy casa +``` + +Mapa **full-bleed** (bloco dominante) em `/despacho` — ver RF-VID-01…08 em SPEC-CITYFOODS-VIDEO-PARITY: todas as rotas/motoboys, filtro por entregador, pin some ao entregar, CTA **Ir para entrega**. + +#### D.1 Requisitos + +| ID | Requisito | Sprint | Status | +|----|-----------|--------|--------| +| RF-ROT-01 | Agrupar seleção por bairro (manual + sugestão) | S6 | ✅ | +| RF-ROT-02 | Sugerir ordem de visita (heurística bairro/CEP) | S6 | ✅ | +| RF-ROT-03 | Otimizar tour com ORS (OpenRouteService) | S7 | ✅ opcional (`ORS_API_KEY`; senão heurística) | +| RF-ROT-04 | Mapa no despacho com pontos + polyline da rota | S7 | ✅ | +| RF-ROT-04b | Entidade rota persistida `montada` \| `na_rua` \| `fechada` | S7 | ✅ `DispatchBatch` + `DispatchStop` | +| RF-ROT-04c | Despachar rota (não só SAIR solto) | S7 | ✅ `POST /api/routes/:id/dispatch` | +| RF-ROT-04d | Tracking motoboy no mapa (ping) | S7–S8 | ✅ `POST /api/routes/:id/ping` + `driver.ping` | +| RF-ROT-05 | Comparar custo: frota própria vs marketplace | S7–C2 | ❌ (após iFood) | + +#### D.2 Aceite + +- Batch de 2+ pedidos gera rota com código (`Rdd-nn`), ordem e motoboy. +- Mapa continua visível enquanto a rota está `montada` / `na_rua`. +- ORS só quando `ORS_API_KEY` + pedidos com geo/bairro resolvível (Joinville centroides MVP). + +**API:** `/api/routes/*` · **UI:** `/despacho` · **Ref.:** rusterando (modelo, sem fork AGPL). + +--- + +### Tela E — App / PWA Entregador + +**Papel:** motoboy. +**No vídeo / Play Store CityFoods:** mapa, detalhes, iniciar e concluir entrega. + +#### E.1 Requisitos + +| ID | Requisito | Sprint | Status | +|----|-----------|--------|--------| +| RF-MOT-01 | Login entregador (role/área) | S8 | ⚠️ parcial (escolhe motoboy na UI; sem PIN) | +| RF-MOT-02 | Lista / rota ativa do motoboy | S8 | ✅ `/motoboy` + `GET /api/routes/driver/:id` | +| RF-MOT-03 | Mapa da rota + deep-link GPS | S8 | ✅ | +| RF-MOT-04 | Marcar “a caminho” / “entregue” | S8 | ✅ por parada da rota | +| RF-MOT-05 | Ping de posição (opt-in geolocation) | S8 | ✅ | +| RF-MOT-06 | Offline tolerante (fila local → sync) | S8+ | ❌ | + +#### E.2 Aceite + +- `entregue` via parada da rota; cockpit atualiza progresso e frota. +- Última parada fecha a rota e devolve motoboy a `casa` se sem fila. + +--- + +### Tela F — Ranking de produção (ao vivo) + +**Papel:** gestor / pizzaiolo. +**No vídeo:** Cláudio 95, Gil 86… pizzas/noite + tempo médio; remanejo por atraso. + +#### F.1 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-RNK-01 | Bumps KDS creditados ao usuário logado na estação | S4–S5 | +| RF-RNK-02 | Ranking do turno: pizzas, tempo médio, atrasos | S5 | +| RF-RNK-03 | Tela TV/gestor só leitura (opcional) | S5 | +| RF-RNK-04 | Alerta “operador em zona de atraso” (N pedidos late) | S5 | + +#### F.2 Aceite + +- Dois logins na mesma estação não misturam contagem. +- Ranking zera/rotaciona por turno (config). + +--- + +### Tela G — Funil operacional (métricas) + +**Papel:** gestor. +**No vídeo:** tempos produção / forno / estufa / rua; identificar gargalo. + +#### G.1 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-FUN-01 | Timestamps: created, claimed, bumped por estação, estufa, dispatched, delivered | S4 | +| RF-FUN-02 | Dashboard: p50/p90 por etapa do funil (dia/turno) | S5 | +| RF-FUN-03 | Contagem de cancelamentos do período | S5 | +| RF-FUN-04 | Distinto do Pooling comercial (faturado/ticket) — ou abas no mesmo painel gestor | S5 | + +#### G.2 Aceite + +- Gestor responde em < 10 s: “o gargalo agora é forno ou estufa ou rua?”. + +--- + +### Tela H — Admin: zonas de entrega + +**Papel:** gestor (Setup). +**No CityFoods (site):** áreas/bairros + taxas diferenciadas. + +#### H.1 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-ZON-01 | CRUD zona: nome, polígono ou lista de bairros, taxa cents, tempo estimado | S6–S9 | +| RF-ZON-02 | PDV/cardápio aplica taxa pelo bairro/CEP | S9 / C1 | +| RF-ZON-03 | Despacho usa zona para agrupamento | S6 | +| RF-ZON-04 | Fora de zona → bloqueio ou taxa especial | S9 | + +--- + +### Tela I — Admin: relatórios de entrega / equipe + +**Papel:** gestor. +**No CityFoods City Pro / Cozinha Digital:** relatórios entregas + entregadores + operação cozinha. + +#### I.1 Requisitos + +| ID | Requisito | Sprint | +|----|-----------|--------| +| RF-REL-01 | Relatório entregas: qtd, tempo médio, cancelados, por canal | S8 | +| RF-REL-02 | Relatório entregadores: entregas, tempo rua, ranking | S8 | +| RF-REL-03 | Relatório cozinha: pizza/hora, atrasos por estação | S5 | +| RF-REL-04 | Export CSV (padrão estoque) | S5–S8 | + +--- + +### Tela J — Integração marketplace (iFood) no despacho + +**Papel:** sistema + despacho. +**No vídeo:** cotação API iFood vs própria; ~70% do fluxo. + +#### J.1 Requisitos + +| ID | Requisito | Track | +|----|-----------|-------| +| RF-IFO-01 | Webhook iFood → Order normalizado (idempotente) | C2 | +| RF-IFO-02 | Pedido aparece na Central + KDS + Despacho com canal iFood | C2 | +| RF-IFO-03 | Cotação frete marketplace vs frota (UI no batch) | C2 + S7 | +| RF-IFO-04 | Sync status pronto/despachado quando política da loja exigir | C2 | + +> Só após Central + despacho interno estáveis (gate SPEC mãe). + +--- + +## 3. Mapa tela → sprint (resumo executivo) + +| Tela | Nome | Sprint / track | Prioridade | Status | +|------|------|----------------|------------|--------| +| **A** | Cockpit Despacho | S4 → S6 | P0 | ✅ | +| **B** | Conferência / Estufa | S4 | P0 | ✅ | +| **C** | Frota | S6 | P1 | ✅ | +| **D** | Gestor de Rotas / mapa | S6 → S7 | P1–P2 | ✅ (ORS opcional) | +| **E** | App entregador | S8 | P2 | ✅ parcial | +| **F** | Ranking produção | S5 | P1 | ✅ `/ops` | +| **G** | Funil operacional | S4–S5 | P1 | ✅ `/ops` | +| **H** | Zonas / taxas | S6–S9 | P2 | ✅ CRUD · ❌ taxa no PDV | +| **I** | Relatórios | S5–S8 | P2 | ✅ básico | +| **J** | iFood no fluxo | C2 | P3 | ❌ | + +--- + +## 4. Dados (implementados) + +``` +Driver { id, storeId, name, phone, active, opsStatus, lastLat?, lastLng?, lastPingAt? } +Order + estufaAt, driverId, lat?, lng?, batchId?, dispatchedAt, deliveredAt, priority +DeliveryZone { storeId, name, neighborhoods[], feeCents, etaMinutes, active } +DispatchBatch { + id, storeId, code, driverId?, status: montada|na_rua|fechada, + method, distanceKm?, depotLat, depotLng, polyline?, + plannedAt, dispatchedAt?, closedAt? +} +DispatchStop { + id, batchId, orderId, seq, lat, lng, + status: pendente|a_caminho|entregue, arrivedAt?, deliveredAt? +} +``` + +Ranking usa `PrepTask` (claimedBy + completedAt). Funil usa timestamps do `Order`. + +--- + +## 5. Eventos Socket + +| Evento | Quando | Quem escuta | +|--------|--------|-------------| +| `order.ready` | Estufa / pronto | Despacho | +| `order.priority` | Reordenação | KDS + Despacho | +| `order.dispatched` | Despacho de rota / SAIR | Despacho + Frota | +| `order.delivered` | Entregue | Despacho + Relatórios | +| `driver.updated` | casa/rua | Cockpit frota | +| `driver.ping` | GPS motoboy | Mapa despacho | +| `route.updated` | Montar / parada / fecho | Despacho + Motoboy | +| `route.dispatched` | Despachar rota | Despacho + Motoboy | +| `route.closed` | Rota fechada | Despacho + Frota | + +--- + +## 6. Critério de “mesmo nível” (definição de pronto) + +Paridade operacional com o vídeo **atingida** quando: + +1. ✅ Cockpit A em `/despacho` +2. ✅ Conferência B + estufa alimentam o cockpit +3. ✅ Frota C no SAIR / despacho de rota +4. ✅ Gestor de Rotas D: montar → despachar → mapa permanente +5. ✅ Funil G + ranking F em `/ops` +6. ✅ App motoboy E consome a **mesma rota** + +**Ainda pode faltar** para paridade comercial City Pro: ORS com chave em produção, PIN/offline motoboy, taxa zona no PDV, iFood cotação, multi-loja — P2+/P3. + +--- + +## 7. Fora de escopo (neste SPEC) + +- Programa de fidelidade CityFoods +- Pop-ups / destaques cardápio +- Caixa completo / fechamento de turno financeiro +- Multi-tenant white-label +- Fork ou cópia de UI proprietária CityFoods (só modelo operacional) + +--- + +## 8. Próximo passo + +1. **P3 / C2 — iFood** no fluxo (webhook → Central/KDS/Despacho) +2. Polimento: `ORS_API_KEY` em prod · taxa zona no PDV · PIN motoboy · CSV relatórios + +--- + +*Documento derivado do cruzamento vídeo yVtcgJt0L_Q × pizza.itecnologys.com × SPEC §15. v0.2.0 — Gestor de Rotas documentado pós-implementação.* diff --git a/Pizza-Digital/docs/SPEC-ESTOQUE.md b/Pizza-Digital/docs/SPEC-ESTOQUE.md new file mode 100644 index 0000000..13f365f --- /dev/null +++ b/Pizza-Digital/docs/SPEC-ESTOQUE.md @@ -0,0 +1,262 @@ +# 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`](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. + +```text +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`) por `MenuItem` (+ 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 + +```text +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** `stockMode` na 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`](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`](data/fichas-pizzas-peso.json) · resumo [`FICHAS-TECNICAS-PESO.md`](FICHAS-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 + +```text +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 + +1. Unidade canônica por insumo; conversões (kg↔g) só na UI de entrada. +2. Custo médio móvel em compras: atualiza `Ingredient.costCents`. +3. Meia-a-meia: duas half-recipes; se faltar um sabor, cardápio bloqueia o par. +4. Bebidas `sku`: não passam por montagem/forno; só estação `bebidas` + expedição. +5. Vitaminas/sucos naturais: baixa no mesmo momento do pedido; se fruta acabou → pausa só o sabor. +6. `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 + +1. Cadastro de mussarela / calabresa / Coca 2L com saldo e mínimo. +2. Ficha Calabresa G salva e listável a partir do MenuItem. +3. Criar pedido com 1× Calabresa G reduz mussarela em 200 g (ou qty da ficha). +4. Criar pedido com 1× Coca 2L reduz 1 un do SKU. +5. Cancelar pedido em produção devolve saldo. +6. Mussarela em 0 → itens BOM que usam mussarela pausam (E2) ou alerta (E1). +7. 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. diff --git a/Pizza-Digital/docs/SPEC-FASE1.md b/Pizza-Digital/docs/SPEC-FASE1.md new file mode 100644 index 0000000..d95072f --- /dev/null +++ b/Pizza-Digital/docs/SPEC-FASE1.md @@ -0,0 +1,232 @@ +# SPEC Fase 1 — MVP Cozinha Digital (detalhamento) + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Fase** | 1 — MVP Cozinha Digital | +| **Versão** | 0.2.0 (ampliação da SPEC 0.1) | +| **Data** | 2026-07-23 | +| **Host de desenvolvimento** | Proxmox BIG · VM **125** · `pizza-digital` · `10.10.10.125` | +| **SO** | Debian 12 | +| **Users** | `mini` (sudo) · `root` | +| **Referência rotas (fase 2)** | **rusterando** (oficial) + CityFoods Gestor de Rotas / ORS | +| **Referência KDS** | CityFoods Cozinha Digital + kitchenOS / restaurant-pos | +| **Sequência** | SPEC §15 — S0…S9 · KDS antes de rotas · S1 ✅ · próximo S2 | + +--- + +## 1. Objetivo da Fase 1 + +Entregar uma **Cozinha Digital operacional** para pizzaria piloto: + +1. PDV web cria pedido +2. KDS multi-estação (Massa → Montagem → Forno → Expedição) +3. Timers / priorização +4. Etiqueta (ESC/POS ou PDF) +5. Dashboard de tempos +6. Gate para despacho (`order.ready`) — **sem** otimização de rotas ainda (Fase 2) + +**Fora da Fase 1:** app motoboy, ORS, iFood, cardápio público, multi-loja. + +--- + +## 2. Infraestrutura (BIG) + +| Item | Valor | +|------|--------| +| Hypervisor | Proxmox BIG (`big1-proxmox` / `95.216.14.162`) | +| VMID | **125** | +| Nome | `pizza-digital` | +| Bridge | `vmbr4000` | +| IP LAN | `10.10.10.125/24` | +| Gateway | `10.10.10.1` (pfSense) | +| DNS | `8.8.8.8` / `8.8.4.4` | +| vCPU | 4 | +| RAM | 8 GB | +| Disco | 80 GB | +| Cloud-init | `local:snippets/vm125-pizza-digital-init.yaml` | +| Hardening | fail2ban (sshd) · PasswordAuthentication yes · PermitRootLogin yes | +| Stack base | Docker + Compose · Node 22 · pnpm · Git · build-essential · nginx · redis-tools · psql client | + +### 2.1 Acesso + +```text +# Via Proxmox BIG (LAN) +ssh mini@10.10.10.125 + +# Ou jump pelo host +ssh big1-proxmox +ssh mini@10.10.10.125 +``` + +SSH externo (porta NAT tipo 2525) **não** está nesta fase — exige regra no pfSense + FORWARD no Proxmox (**não** alterar `/etc/network/interfaces` sem confirmação ×3). + +### 2.2 Workspace na VM + +```text +/home/mini/pizza-digital/ # monorepo +/opt/pizza-digital/ # dados / runtime +/opt/pizza-digital/data/ # volumes locais +``` + +--- + +## 3. Entregáveis Fase 1 (Definition of Done) + +| # | Entregável | Critério de aceite | +|---|------------|--------------------| +| 1 | Monorepo bootstrap | `apps/api`, `apps/web`, `packages/domain`, `docker-compose.yml` | +| 2 | Auth + RBAC mínimo | login gestor / pdv / kds | +| 3 | Cardápio | CRUD item + modifiers + meia-a-meia + mapa → estações | +| 4 | PDV | criar pedido delivery/balcão com observação | +| 5 | Order state machine | confirmado → em_producao → pronto | +| 6 | KDS multi-estação | 4 telas; WebSocket ≤ 500 ms | +| 7 | Timers SLA | verde/amarelo/vermelho | +| 8 | Priorização | reordenar / marcar urgente | +| 9 | Etiquetas | imprimir ou PDF com `#CODE` + obs | +| 10 | Dashboard | tempo médio por estação + fluxo do pedido | +| 11 | Seed pizzaria | dados demo (sabores, bordas, bebidas) | + +--- + +## 4. Estações (template Pizzaria) — freeze Fase 1 + +| Código | Nome | SLA default | Recebe | +|--------|------|-------------|--------| +| `massa` | Massa | 3 min | base / borda | +| `montagem` | Montagem | 5 min | molho, queijo, toppings, meia-a-meia | +| `forno` | Forno | 8 min | pizza assando | +| `expedicao` | Expedição | 3 min | embalagem + etiqueta + conferência | +| `bebidas` | Bebidas (opc.) | 2 min | refrigerante / água | + +Regra: pedido só `pronto` quando **todas** as estações obrigatórias do pedido concluírem; expedição é a última. + +--- + +## 5. Stack Fase 1 (congelada para implementação) + +| Camada | Escolha | +|--------|---------| +| API | **NestJS** + TypeScript | +| Web | **React** + Vite + TypeScript (PWA) | +| Realtime | **Socket.io** | +| DB | **PostgreSQL 16** (Docker) | +| Cache/pubsub | **Redis 7** (Docker) | +| ORM | Prisma | +| Print | ESC/POS node + PDF fallback | +| Deploy local | Docker Compose na VM 125 | + +Referências de implementação: + +- KDS realtime → kitchenOS / Tasty-Station +- Estações → restaurant-pos +- Rotas (só na Fase 2) → rusterando + OpenRouteService + +--- + +## 6. Apps / rotas web (Fase 1) + +| App | Path | Papel | +|-----|------|-------| +| PDV | `/pdv` | Caixa | +| KDS | `/kds/:station` | Cozinha | +| Despacho (lista) | `/despacho` | Fila `pronto` (sem ORS) | +| Admin | `/admin` | Cardápio, users, métricas | + +--- + +## 7. Eventos WebSocket (Fase 1) + +```text +order.created +order.updated +order.canceled +ticket.upserted +ticket.bumped +order.ready +label.printed +``` + +Rooms: `store:{id}` · `store:{id}:station:{stationId}` + +--- + +## 8. Módulo Rotas — ponte para Fase 2 (não implementar agora) + +Contrato mínimo que a Fase 1 **já deve expor**: + +```text +order.status === 'pronto' + → aparece em /despacho + → payload inclui: address, bairro, lat/lng (se houver), priority +``` + +Na Fase 2 (CityFoods-like): + +1. Agrupar por bairro +2. Atribuir motoboy +3. ORS tour (rusterando) +4. PWA entregador + tracking +5. Comissão entregador + +--- + +## 9. Sprints Fase 1 (na VM 125) + +| Sprint | Dias úteis | Entrega | +|--------|------------|---------| +| **S0** | 1 | Repo + Docker Compose (Postgres/Redis) + domain types | +| **S1** | 2 | Auth + Store + Cardápio + estações | +| **S2** | 2 | Pedidos PDV + state machine | +| **S3** | 3 | KDS realtime multi-estação + timers | +| **S4** | 2 | Etiquetas + priorização + dashboard | +| **S5** | 2 | Seed + teste piloto + hardening | + +--- + +## 10. Segurança na VM (Fase 1) + +- [x] fail2ban sshd +- [x] PasswordAuthentication + root (padrão iTecnologys / cloud-init) +- [ ] UFW: allow 22/tcp, 80/tcp, 443/tcp, 3000/tcp (dev) — aplicar após bootstrap +- [ ] Não expor Postgres/Redis na WAN +- [ ] Secrets em `.env` (não commit) + +--- + +## 11. Checklist de bootstrap VM 125 + +- [x] VM criada e running (VMID 125, Debian 12, 4 vCPU / 8 GB / 80 GB) +- [x] Ping `10.10.10.125` a partir do BIG (`vmbr4000`) +- [x] SSH `root@` / `mini@10.10.10.125` OK (senha `805353`) +- [x] Docker Compose S0: `postgres` + `redis` + `api` + `web` + `gateway` (:3000) UP +- [x] `GET /api/health` → redis up · `GET /` → 200 +- [x] `fail2ban` sshd ativo +- [x] Docker + Compose + Node 22 + pnpm + TypeScript/tsx +- [x] `sshd -T` → `passwordauthentication yes` · `permitrootlogin yes` +- [x] Workspace `/home/mini/pizza-digital` com SPEC +- [ ] Monorepo código (S0) — próximo passo +- [ ] SSH externo NAT (porta dedicada) — opcional; **não** altera `/etc/network/interfaces` sem 3 confirmações + +### Acesso rápido + +```bash +# Do Windows (ProxyJump) +ssh pizza-digital + +# Do BIG Proxmox (necessário route helper se dual-bridge) +/usr/local/bin/pizza-digital-route.sh +ssh mini@10.10.10.125 +``` + +--- + +## 12. Changelog desta ampliação + +### 0.2.0 — 2026-07-23 + +- Detalhamento completo da **Fase 1** +- Infra VM 125 no Proxmox BIG documentada +- Stack congelada (NestJS + React + Postgres + Redis + Socket.io) +- Definition of Done e sprints S0–S5 +- Ponte explícita para Gestor de Rotas (Fase 2 / CityFoods + rusterando) diff --git a/Pizza-Digital/docs/SPEC-KDS-CLAIM.md b/Pizza-Digital/docs/SPEC-KDS-CLAIM.md new file mode 100644 index 0000000..6d481e1 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-KDS-CLAIM.md @@ -0,0 +1,79 @@ +# SPEC — KDS PEGAR (claim) + pooling + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Capítulo** | RF-KDS claim exclusivo | +| **Versão** | 0.2.0 | +| **Data** | 2026-10-20 | +| **Owner** | Roger / iTecnologys | +| **UI** | `/kds/:station` | +| **API** | `POST /api/kds/:station/claim/:orderId` | + +--- + +## 1. Objetivo + +Na fila comum, o operador **não puxa o ticket no primeiro clique**. PEGAR abre confirmação com: + +- **nome do operador** logado +- **nome da dona da pizza** (cliente) +- pedido `#código`, estação, itens + +Só `Confirmar — é minha` dispara o claim exclusivo. + +--- + +## 2. Regras + +1. Fila comum: todos veem tickets `view=pool` (`canClaim`). +2. Clique PEGAR → modal; **não** chama a API ainda. +3. Confirmar → `POST …/claim/:orderId` (uma estação ganha; demais veem bloqueado). +4. Cancelar / clique fora fecha o modal. +5. Ticket mostra pill **Pizza de {primeiro nome}**. +6. Contadores: fila comum / comigo / outras estações. + +--- + +## 3. Pooling (tempo real + backup) + +| Fonte | Comportamento | +|-------|----------------| +| Socket.io | `order.*` / `ticket.bumped` com debounce 180 ms | +| Poll backup | 10 s se socket ao vivo; **3 s** se offline | +| Aba oculta | não polla | +| Foco / visível | recarrega na hora | + +RF-KDS-02 (WS sem polling) continua o caminho feliz; o poll é **rede de segurança** para TV/tablet com socket instável. + +--- + +## 4. Copy da confirmação + +```text +Confirmar PEGAR +Pedido #A12 +{Operador} vai pegar a pizza de {Dona}? + +Operador {user.name} +Dona da pizza {shortCustomerName} +Estação Massa | Montagem | … +Itens 1× Calabresa G, … + +[Cancelar] [Confirmar — é minha] +``` + +--- + +## 5. Requisitos + +| ID | Requisito | Status | +|----|-----------|--------| +| RF-KDS-C1 | Claim exclusivo por estação (0.2.4) | **feito** | +| RF-KDS-C2 | Modal PEGAR com operador + dona da pizza | **feito** | +| RF-KDS-C3 | Poll backup + debounce + visibility | **feito** | +| RF-KDS-C4 | Contadores de pool / mine / locked | **feito** | + +--- + +*Satélite. Claim original: CHANGELOG 0.2.4. Produto pai: SPEC.md v0.3.5.* diff --git a/Pizza-Digital/docs/SPEC-KDS-PIZZAIOLO-MODELO-B.md b/Pizza-Digital/docs/SPEC-KDS-PIZZAIOLO-MODELO-B.md new file mode 100644 index 0000000..4fd4ca4 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-KDS-PIZZAIOLO-MODELO-B.md @@ -0,0 +1,221 @@ +# SPEC — KDS Operacional do Pizzaiolo (Modelo B) + +**Status:** aprovado para implementação +**Produto:** Pizzaza / Pizza Digital +**Data:** 2026-10-25 +**Referência visual:** mock operacional (cards por pizza, áreas coloridas, botões grandes) +**Arquitetura de fluxo:** Modelo B — montagem e forno com responsáveis independentes + +--- + +## 1. Objetivo + +Separar completamente: + +| Camada | Público | Natureza | +|--------|---------|----------| +| **KDS operacional (pizzaiolo)** | Role `kds` (e futuros papéis montagem/forno) | Estação de produção touch, unidade = **pizza** | +| **KDS / Ops administrativo** | `gestor` / admin | Kanban de pedidos, Pooling, faturamento, metas | + +O pizzaiolo **não** vê Pedidos, PDV, Pooling financeiro, relatórios nem configurações. +Esconder menu não basta: rotas e APIs administrativas devem rejeitar role `kds`. + +--- + +## 2. Unidade de trabalho + +- **Unidade:** `PrepTask` (uma pizza / unidade de preparo). +- Um pedido com N pizzas pode ter pizzas em estágios e com pizzaiolos diferentes. +- Marcar pizza pronta **não** marca o pedido pronto. +- Pedido só segue para liberação quando **todas** as pizzas e itens obrigatórios (bebidas, sobremesas, adicionais) forem conferidos na expedição. + +Identificação no card: `#PEDIDO · Pizza seq/total` (ex.: `#N16 · Pizza 1/2`). + +--- + +## 3. Estados da pizza (máquina oficial) + +``` +disponivel + │ PEGAR + ▼ +em_montagem (responsável montagem) + │ FINALIZAR MONTAGEM + ▼ +aguardando_forno (sem forneiro ainda) + │ ASSUMIR FORNO (+ INICIAR FORNO) + ▼ +no_forno (responsável forno) + │ MARCAR PRONTA + ▼ +pronta → fila de expedição (automático) +``` + +### Transições e botões + +| Botão | De → Para | Efeito | +|-------|-----------|--------| +| **PEGAR** | disponivel → em_montagem | Atribui montagem ao usuário; inicia cronômetro da etapa; atômico no servidor | +| **DEVOLVER À FILA** | em_montagem → disponivel | Remove responsável; preserva tempo já consumido; motivo opcional/obrigatório conforme config | +| **FINALIZAR MONTAGEM** / **ENVIAR AO FORNO** | em_montagem → aguardando_forno | Encerra tempo de montagem; **não** atribui forneiro | +| **ASSUMIR FORNO** | aguardando_forno → no_forno (aguardando início) | Identifica forneiro | +| **INICIAR FORNO** | (opcional explícito) marca `fornoStartedAt` | Se omitido na v1, ASSUMIR FORNO também inicia cocção | +| **MARCAR PRONTA** | no_forno → pronta | Encerra cocção; envia à expedição automaticamente | +| **TRANSFERIR** | qualquer etapa atribuída | Troca responsável com motivo; histórico preservado | +| **REPORTAR PROBLEMA** | qualquer | Registra ocorrência sem apagar histórico | + +### Dupla atribuição + +Claim/assume é **atômico** (`UPDATE … WHERE status = X AND claimedById IS NULL`). +Se dois usuários competirem, só um vence; o outro recebe conflito 409. + +--- + +## 4. Layout do KDS pizzaiolo (Kanban operacional) + +Quatro colunas Kanban (cards grandes, touch), unidade = pizza: + +1. **Disponíveis** — sem responsável → **PEGAR** +2. **Montagem** — em preparo → **ENVIAR AO FORNO** (+ Devolver) +3. **Forno** — fila (`aguardando_forno`) + cocção (`no_forno`) → **PRONTA → CHECAGEM** + Qualquer pizzaiolo/forneiro autorizado pode marcar pronta (não só quem assumiu). +4. **Checagem** — pizzas já prontas na 1ª checagem; daí seguem para **expedição** (etiqueta/embalagem/liberação) + +### Fluxo Forno → Checagem → Expedição + +`PRONTA` na coluna Forno encerra a cocção e coloca a pizza na **primeira checagem**. +Quando todas as pizzas do pedido estiverem prontas, o pedido fica `pronto` (pronto cozinha) para a expedição conferir, etiquetar e liberar. + +### O que NÃO aparece para o pizzaiolo + +- Pooling / faturamento / ticket médio +- Coluna Expedição administrativa +- Links Pedidos, PDV, Setup, Demo, Relatórios +- Indicadores financeiros + +### Header operacional + +`Pizzaza` · badge `KDS` · `Cozinha` · AO VIVO · hora · nome do usuário +Sem menu administrativo completo. + +### Rodapé + +“Pizzas concluídas recentemente” — thumbs + código + horário + nome (não pedidos financeiros). + +--- + +## 5. Conteúdo do card (pizza) + +- Código do pedido + fração `seq/total` +- Badge do estágio + tempo total (vermelho se atraso) +- Tempo da etapa atual (quando atribuída) +- Foto realista do produto +- Nome / tamanho / componentes (metades, bordas) +- Canal / cliente / bairro (compacto) +- Responsável montagem e/ou forno (iniciais + nome), sem sobrescrever o outro +- Botão primário full-width da etapa + secundário (Devolver / Transferir / Ver pedido) +- Trilho lateral grosso na cor da **etapa** (não do canal financeiro): azul disponível, laranja montagem, âmbar fila forno, vermelho forno, verde pronta + +Ordenação: prioridade operacional (atraso, tempo de espera, horário prometido) — não só `createdAt`. + +--- + +## 6. Cronômetros + +- **Tempo total:** `now - order.arrivedAt|queuedAt` (servidor) +- **Tempo da etapa:** `now - stageStartedAt` da etapa atual +- Cálculo sempre a partir de timestamps do servidor (sobrevive a refresh/offline) + +Metas por etapa (config gestor, defaults): + +| Etapa | Meta sugerida | +|-------|----------------| +| Montagem | 6–8 min | +| Espera forno | 3 min | +| Forno | 8–12 min | +| Total produção | 15–20 min | + +Atraso: destaque visual automático ao ultrapassar meta. + +--- + +## 7. Perfis e permissões + +| Ação | Pizzaiolo montagem | Forneiro | Expedição | Gestor | +|------|--------------------|----------|-----------|--------| +| Ver disponíveis / pegar | Sim | Não* | Não | Sim | +| Finalizar montagem | Sim (suas) | Não | Não | Sim | +| Assumir / marcar forno | Não* | Sim | Não | Sim | +| Ver responsável | Sim | Sim | Sim | Sim | +| Transferir | Restrito | Restrito | — | Sim | +| Pooling / Pedidos / PDV | Não | Não | Não | Sim | +| Conferir / etiqueta / liberar | Não | Não | Sim | Sim | + +\* Mesmo usuário pode ter as duas caps; registros de montagem e forno permanecem separados. + +### Backend + +- `GET/POST` administrativos (`/api/orders/stats/today`, etc.) → 403 para `kds` +- `GET /pedidos` (web) → redirect `/kds/producao` para role `kds` +- Nav: pizzaiolo só vê KDS (+ logout) + +--- + +## 8. Expedição (fora da tela do pizzaiolo) + +Ao **MARCAR PRONTA**, a pizza entra na fila de expedição automaticamente. +Expedição: conferência, etiqueta, embalagem, liberação do **pedido completo** (todas as pizzas + itens). +O pizzaiolo não usa botão “Enviada p/ Expedição” manual. + +--- + +## 9. API proposta (v1) + +Prefixo: `/api/kds/pizzas` + +| Método | Rota | Descrição | +|--------|------|-----------| +| GET | `/board` | Board pizzaiolo: disponíveis, montagem, aguardando_forno, no_forno, recentes | +| POST | `/:taskId/pegar` | disponivel → em_montagem | +| POST | `/:taskId/devolver` | em_montagem → disponivel (+ motivo) | +| POST | `/:taskId/enviar-forno` | em_montagem → aguardando_forno | +| POST | `/:taskId/assumir-forno` | aguardando_forno → no_forno | +| POST | `/:taskId/marcar-pronta` | no_forno → pronta (+ evento expedição) | +| POST | `/:taskId/transferir` | troca responsável (+ motivo) | + +Eventos socket: `pizza.updated`, `pizza.claimed`, `pizza.ready` (payload por PrepTask). + +### Modelo de dados (evolução) + +`PrepTaskStatus`: adicionar `aguardando_forno`, `no_forno` (manter `em_preparo` = em_montagem). +Campos: `montagemById`, `montagemStartedAt`, `montagemDoneAt`, `fornoById`, `fornoStartedAt`, `fornoDoneAt`. +Histórico: tabela `PrepTaskEvent` (opcional v1.1) ou log append-only. + +--- + +## 10. Critérios de aceitação + +1. Role `kds` não vê menus/admin/Pooling/Pedidos; URL `/pedidos` redireciona; API stats 403. +2. Tela abre direto na produção autorizada. +3. Cards são **pizzas**, com foto, 2 cronômetros e responsável(is). +4. PEGAR / ENVIAR AO FORNO / ASSUMIR FORNO / MARCAR PRONTA seguem a máquina de estados. +5. Fila aguardando forno ≠ cocção iniciada. +6. Dupla atribuição impossível (409). +7. MARCAR PRONTA encaminha à expedição sem liberar pedido incompleto. +8. Touch: botões grandes, contraste alto, atualização realtime. +9. Modelo B: montagem e forno com responsáveis e tempos independentes. + +--- + +## 11. Fora de escopo desta etapa + +- QR/etiqueta individual por pizza (recomendado v2) +- Relatórios de produtividade detalhados (gestor) +- App nativo; esta SPEC é web touch KDS + +--- + +## 12. Relação com o KDS gestor + +O Kanban de pedidos (Novo / Montagem / Forno / Prontas / Expedição) permanece disponível **apenas** para gestor/admin como visão de supervisão. +Não é a home do pizzaiolo. diff --git a/Pizza-Digital/docs/SPEC-LACUNAS-VIDEO-SIRIFOOD.md b/Pizza-Digital/docs/SPEC-LACUNAS-VIDEO-SIRIFOOD.md new file mode 100644 index 0000000..e27a958 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-LACUNAS-VIDEO-SIRIFOOD.md @@ -0,0 +1,107 @@ +# SPEC-LACUNAS-VIDEO-SIRIFOOD — Pizza Digital vs operação filmada + +| Campo | Valor | +|-------|--------| +| **ID** | SPEC-LACUNAS-VIDEO-SIRIFOOD | +| **Versão** | 0.3.7 | +| **Data** | 2026-10-20 | +| **Owner** | Roger / iTecnologys | +| **Referência** | [Vídeo Siciliana / SiriFood](https://www.youtube.com/watch?v=yVtcgJt0L_Q) | +| **Ambiente comparado** | `https://pizza.itecnologys.com` · VM 125 · piloto Joinville | +| **Status** | Diagnóstico · **não implementado** (exceto o que já está no ar, marcado abaixo) | + +## 0. Leitura do vídeo + +Operação **Siciliana Palhoça** (SiriFood): ~R$ 1 mi/mês, ~70% iFood, três lojas no mesmo sistema. O **despacho** é o coração: produção, forno, conferência, estufa, motoboy e cotação iFood giram em torno dessa tela. + +Case citado: ~13 cancelamentos/mês e entrega abaixo de 30 min. + +## 1. O que o piloto já cobre + +- KDS multi-estação (massa, montagem, forno, expedição, bebidas) +- PEGAR com nome do operador e do cliente — **no pedido inteiro**, não na pizza +- PDV + filtros de origem (aplicativo, iFood, WhatsApp, telefone, PDV) +- Etiqueta **por pedido** e impressão no BUMP +- Despacho = lista ao vivo de prontos + botão despachar +- Estoque com baixa pela ficha técnica +- 20 funcionários com nome composto e 8 perfis (gestor, atendente, administrativo, pizzaiolo, conferente, expedição, estoque, motoboy) +- Navegação SAGE em todas as páginas + +Isso **não** equivale à operação do vídeo. Cargo no login ≠ recurso ao vivo. + +## 2. Lacunas (RF-GAP) + +Cada linha é trabalho a construir. P0 = sem isso o vídeo continua irreconhecível na nossa UI. + +| ID | No vídeo | No Pizza Digital hoje | Prioridade | +|----|----------|------------------------|------------| +| **RF-GAP-01** | 3 lojas no mesmo sistema | 1 loja piloto | P2 | +| **RF-GAP-02** | Despacho centro de comando (canal, nº, tempo na casa, estatísticas) | Lista de prontos + despachar | P0 | +| **RF-GAP-03** | Motoboy ao vivo: casa / rua / nº de entregas | 3 logins, sem status | P0 | +| **RF-GAP-04** | Reordenar fila no despacho e o KDS segue na hora | FIFO + PEGAR; gestor não reordena | P0 | +| **RF-GAP-05** | Semáforo: vermelho chegou · laranja fazendo · amarelo no forno | Status de pedido + estações, sem esse semáforo no despacho | P0 | +| **RF-GAP-06** | Agrupar pedidos da mesma região para saírem do forno juntos | Não existe (rotas = S6 / rusterando) | P1 | +| **RF-GAP-07** | Cotação iFood API: iFood × própria × sob demanda × frota | Canal iFood só como filtro | P1 | +| **RF-GAP-08** | Conferência com login próprio: identifica sabor, imprime etiqueta, marca estufa | Perfil Conferente cadastrado; fluxo inexistente | P0 | +| **RF-GAP-09** | Estufa com pedidos etiquetados à espera do motoboy | Sem estado `na_estufa` | P0 | +| **RF-GAP-10** | Ranking ao vivo: pizzas/noite, tempo/pizza, média do pizzaiolo | Sem produtividade por operador | P1 | +| **RF-GAP-11** | Gráfico de atraso + remanejar gente para o gargalo | Pooling = faturamento, não gargalo | P1 | +| **RF-GAP-12** | Mapa do pedido: chegou → produção → forno → estufa → rua → entregue | Sem linha do tempo por estágio | P1 | +| **RF-GAP-13** | Tempos: produção, forno, parado na estufa, na rua | SLA por estação no KDS; sem dwell estufa/rua | P1 | +| **RF-GAP-14** | Preparo item a item (sabor conferido) | PEGAR trava o **pedido inteiro** | P0 | +| **RF-GAP-15** | Etiqueta com nº + sabores na conferência | 1 etiqueta/pedido; falta 01/N por pizza + etiqueta-mãe | P0 | +| **RF-GAP-16** | Despacho híbrido (frota, casa, iFood Entrega) | Não iniciado | P2 | +| **RF-GAP-17** | KPI: cancelamentos, entrega < 30 min | Só faturado hoje | P1 | + +## 3. P0 — o que construir primeiro ( Specs de trabalho ) + +### 3.1 Preparo colaborativo — RF-GAP-14 / RF-GAP-15 + +Níveis: **Pedido** (prazo) · **Tarefa de preparo** (pizza física + responsável) · **Expedição** (conjunto). + +- Modal PEGAR lista pizzas (metades, observações). Operador marca as que assume. +- Ação: `Assumir N pizzas`. Aceite comercial ≠ atribuição de preparo. +- Trava atômica **da tarefa**, não do pedido. Dois PEGAR na mesma pizza: um ganha. +- Transferência explícita + histórico. Fechar modal não libera pizza em preparo. +- Pedido só `pronto` quando todas as tarefas obrigatórias concluem. +- Etiqueta por pizza `PEDIDO #024 · PIZZA 02/04` + etiqueta-mãe de conferência. +- Reimprimir não cria tarefa nem baixa estoque de novo. +- A partir de 3 pizzas: sugerir “Dividir preparo com a equipe”. +- Forno conjunto (janela de entrada) = evolução, não MVP. + +### 3.2 Conferência + estufa — RF-GAP-08 / RF-GAP-09 + +- Login conferente (já existe Patrícia Nunes / Rafael Costa). +- Conferir sabor → imprimir etiqueta da tarefa → marcar **na estufa**. +- Despacho só oferece motoboy o que está na estufa. + +### 3.3 Despacho comando — RF-GAP-02 / RF-GAP-03 / RF-GAP-04 / RF-GAP-05 + +- Colunas: canal, nº, tempo na casa, estado (vermelho/laranja/amarelo), motoboy. +- Lista motoboys: em casa / na rua / carga. +- Arrastar/reordenar fila **propaga no KDS**. + +## 4. Fora do P0 (não misturar) + +P1: rotas por região, ranking, gargalo, KPIs, cotação iFood. +P2: multi-loja, frota híbrida. + +Programação automática do forno: depois do P0. + +## 5. Relação com a sequência S0–S9 + +| Sequência atual | Impacto desta SPEC | +|-----------------|-------------------| +| S3 KDS estações | Estender tickets para **tarefa por pizza** | +| S4 etiquetas | Etiqueta por tarefa + conferência | +| S5 despacho | Vira centro de comando (hoje é fila) | +| S6 rotas / rusterando | RF-GAP-06 / 16 | +| Canais digitais / iFood | RF-GAP-07 deixa de ser só filtro | + +## 6. Critério de “estamos no nível do vídeo” + +1. Dois pizzaiolos assumem pizzas do mesmo pedido sem um ficar com tudo. +2. Conferente imprime 01/N e o despacho vê o pedido na estufa. +3. Gestor reordena a fila e a cozinha vê a nova ordem. +4. Motoboy aparece como casa/rua, não só como usuário. +5. iFood na hora do despacho é cotação, não só um selo vermelho. \ No newline at end of file diff --git a/Pizza-Digital/docs/SPEC-MATRIZ-ACESSO.md b/Pizza-Digital/docs/SPEC-MATRIZ-ACESSO.md new file mode 100644 index 0000000..eddbc7f --- /dev/null +++ b/Pizza-Digital/docs/SPEC-MATRIZ-ACESSO.md @@ -0,0 +1,169 @@ +# SPEC — Matriz de Acesso (MVP) + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Versão** | 0.1.0 | +| **Data** | 2026-10-19 | +| **Owner** | Roger / iTecnologys | +| **UI** | `/acessos` · Admin | +| **API** | `/api/access/*` | +| **Protótipo visual** | `acessos.html` (Codex 2026-10-19) — referência UX, **não** localStorage como auth | +| **Regras** | `matriz-de-acesso-mvp.md` + este doc | + +--- + +## 1. Objetivo + +Gestão de **permissões administrativas** do tenant (loja) selecionado: matriz Perfil × Capacidade, com rascunho, dependências, salvamento atômico e auditoria. + +**Fora desta entrega:** Cozinha, Balcão, Despacho, Delivery, Estoque operacional · suporte irrestrito multi-tenant. + +--- + +## 2. Modelo no Pizza Digital (estado real do repo) + +| Conceito PRD / proposta | Implementação atual | +|-------------------------|---------------------| +| Tenant / loja | `Store` | +| Identity global | **Proposta futura.** MVP: credencial em `User` (email+hash) por loja | +| Vínculo funcionário–tenant | `User` (`storeId` + email único na loja) | +| Sessão | JWT com `sub`, `storeId`, `role` operacional — **capabilities são reconsultadas no Core** | +| Core = fonte da verdade | API Nest + Postgres | +| Admin (borda) | App React `apps/web` | + +`UserRole` (`gestor` \| `pdv` \| `kds` \| `despacho`) permanece para **operação** (PDV/KDS). A matriz MVP **não** define essas permissões operacionais. + +--- + +## 3. Capacidades (propostas → contrato MVP) + +| ID | Área | Label PT-BR | +|----|------|-------------| +| `menu.view` | Cardápio | Visualizar cardápio | +| `menu.manage` | Cardápio | Editar cardápio | +| `staff.view` | Equipe | Visualizar funcionários | +| `staff.manage` | Equipe | Gerenciar funcionários comuns | +| `permissions.manage` | Equipe | Gerenciar permissões | +| `owners.manage` | Equipe | Gerenciar Donos | +| `finance.view` | Financeiro | Visualizar dashboard financeiro | + +**Nota:** “Consultar auditoria do cardápio” **acompanha** `menu.view` — não é capability separada. + +--- + +## 4. Perfis = **modelos de preenchimento** (não contrato fechado do PRD) + +O PRD nomeia só **Dono**. Gestor / Cardápio / Financeiro são **propostas** desta implementação. + +| Capacidade | Dono | Gestor | Cardápio | Financeiro | +|------------|:----:|:------:|:--------:|:----------:| +| `menu.view` | ■ | ■ | ■ | | +| `menu.manage` | ■ | ■ | ■ | | +| `staff.view` | ■ | ■ | | | +| `staff.manage` | ■ | ■ | | | +| `permissions.manage` | ■ | | | | +| `owners.manage` | ■ | | | | +| `finance.view` | ■ | ■ | | ■ | + +### Como o modelo é aplicado ao vínculo + +1. Matriz edita a **definição do modelo** por tenant (`AccessProfileTemplate`). +2. Permissões **efetivas** ficam em `UserCapability` (vínculo). +3. **Aplicar modelo** a um ou mais usuários é ação **explícita** (`POST /api/access/apply-template`) — nunca silenciosa ao salvar a matriz. +4. `User.profileTemplateKey` guarda só o último modelo aplicado (rótulo); mudar o template **não** atualiza quem já o usou até reaplicar. + +--- + +## 5. Dependências (Core valida o conjunto completo) + +1. `menu.manage` ⇒ exige `menu.view` +2. `staff.manage` \| `permissions.manage` \| `owners.manage` ⇒ exigem `staff.view` +3. `staff.manage` **não** implica promover, conceder permissões nem gerir Donos +4. Autor só concede capacidades que **ele próprio** possui (reconsulta Core) +5. `permissions.manage` **não** implica `owners.manage` +6. Último vínculo com `owners.manage` ativo **não** pode perder essa cap / ser desativado +7. Mudança de template/perfil que deixaria o tenant sem Dono → rejeitada **atomicamente** +8. Admin de tenant **não** altera email global nem define/redefine senhas (fora deste módulo; documentado) +9. `User.active=false` ⇒ sem acesso àquele tenant + +UI explica auto-concessão/remoção de dependentes; servidor revalida no save. + +--- + +## 6. API + +| Método | Rota | Auth | +|--------|------|------| +| `GET` | `/api/access/me` | sessão — caps efetivas (sempre do DB) | +| `GET` | `/api/access/matrix` | `permissions.manage` | +| `PUT` | `/api/access/matrix` | `permissions.manage` · body + `If-Match` / `version` | +| `POST` | `/api/access/apply-template` | `permissions.manage` (+ caps do autor) | + +`PUT` salva só **templates**. Efetivos mudam só via apply (ou seed inicial). + +--- + +## 7. Persistência + +- `AccessProfileTemplate` — storeId + key + capabilities[] +- `UserCapability` — userId + capability (efetivo) +- `User.profileTemplateKey` — opcional +- `AccessAuditLog` — autor, storeId, target, before/after, createdAt (**proposta** se PRD não fechou auditoria) + +--- + +## 8. UI `/acessos` + +- Colunas: perfis · Linhas: permissões agrupadas por área +- Busca · filtros área/perfil · célula toggle · contagem pendente +- Descartar / Salvar · loading / erro / sucesso / vazio / 403 +- Responsivo, teclado, toque · PT-BR + +localStorage **não** autoriza. + +--- + +## 9. Migração (instruções — não rodar em produção sem revisão) + +```bash +# lab / VM125 +cd pizza-digital +docker compose exec api npx prisma migrate deploy +docker compose exec api npx prisma db seed # backfill caps do gestor +``` + +Arquivo: `apps/api/prisma/migrations/20261019190000_access_matrix/` + +--- + +## 10. Decisões pendentes + +1. Separar Identity global (email único multi-loja) do vínculo — ainda acoplado em `User`. +2. Colunas da matriz: só modelos vs. também funcionários (2ª vista). +3. Auditoria de permissões: proposta implementada; confirmar se PRD exige retenção/export. +4. Conjunto Dono “protegido” na UI vs. editável no Core — MVP: editável no Core com regra do último Dono; UI marca coluna Dono como sensível. +5. Aprovar nomes Gestor/Cardápio/Financeiro no PRD oficial. + +--- + +## 11. Critérios de aceite → cobertura + +| Critério | Como | +|----------|------| +| Autorizado consulta matriz | `GET /matrix` + UI | +| Sem permissão: UI + API | Guard `permissions.manage` | +| Busca/filtros não perdem rascunho | state local React | +| Descartar restaura | reload snapshot | +| Salvar só após 2xx | PUT atômico | +| Falha preserva rascunho | não limpa draft | +| Dependências FE+BE | `normalizeCapabilities` | +| Sem escalada via staff | staff.manage ≠ permissions/owners | +| Revogação na próxima ação | `/access/me` + guards | +| Sem ficar sem Dono | check atômico | +| Sem cross-tenant | sempre `storeId` da sessão | +| Teclado/tablet/mobile | UI matrix | + +--- + +*Documento vivo. Versão produto pai: SPEC.md.* diff --git a/Pizza-Digital/docs/SPEC-SIM-OPS-DEMO.md b/Pizza-Digital/docs/SPEC-SIM-OPS-DEMO.md new file mode 100644 index 0000000..aea5c76 --- /dev/null +++ b/Pizza-Digital/docs/SPEC-SIM-OPS-DEMO.md @@ -0,0 +1,68 @@ +# SPEC — Simulação de Operação (Demo) + +| Campo | Valor | +|-------|--------| +| **Versão** | 0.1.0 | +| **Data** | 2026-10-22 | +| **Rota UI** | `/sim` (menu · só gestor) | +| **API** | `/api/sim/*` | +| **Marca pedido** | `inboundKey` prefixo `sim:` · notes `[SIM-OPS]` | + +--- + +## 0. Objetivo + +Rotina automática que **demonstra o ciclo completo** da pizzaria em Joinville: + +entrada (Aplicativo / WhatsApp / Telefone) → Central → KDS (pizzaiolos) → estufa → despacho → motoboy → entrega. + +Liga / desliga no menu. Limpa dados de teste em tempo real. + +--- + +## 1. Parâmetros + +| Parâmetro | Valor | +|-----------|--------| +| Cidade | Joinville / SC (bairros do mapa) | +| Intervalo entrada | 2–6 min (mantém fila visível; almoço/jantar mais curto) | +| Cap ops/dia | 2000 (create/claim/bump/estufa/rota/entrega) | +| Prep máximo | **165 min** — só força se travar | +| Confecção | **14–18 min** simples · **18–25 min** complexa (média pizzaria) | +| Estufa | 4–7 min · Entrega 14–25 min · ciclo ~35–55 min | +| Motoboys | 15 com **nomes reais** (Steve Rogers, Ricardo Alves…) | +| Pizzaiolos | 8 KDS com nomes (Cláudio, Gil, Cristiane…) | +| Pizza | simples / complexa · até 4 sabores · **sempre** com complemento (borda/extra) | +| Pedidos | 1–N itens; múltiplos por “onda” | +| ETA entrega | 8–18 min após despacho (distância); então `entregue` | + +Faixas busy (America/Sao_Paulo): **11:30–14:00** e **18:00–22:30**. + +--- + +## 2. API + +| Método | Path | Efeito | +|--------|------|--------| +| GET | `/api/sim/status` | ligado?, stats, nextIngressAt | +| POST | `/api/sim/start` | seed frota/pizzaiolos · liga tick · burst inicial | +| POST | `/api/sim/stop` | para tick (dados ficam) | +| POST | `/api/sim/purge` | apaga pedidos/rotas/drivers SIM · zera stats | + +--- + +## 3. App Motoboy (decisão) + +- **Agora:** `/motoboy` PWA continua o app do entregador; a simulação alimenta as mesmas rotas/API e conclui entregas com ETA (você vê pins sumirem no despacho). +- **Depois (não bloqueia demo):** PIN do motoboy, offline queue, deep-link Waze/Google configurável, push — SPEC-DESPACHO E. + +--- + +## 4. Aceite + +1. Gestor liga em `/sim` → pedidos entram sozinhos nos canais. +2. KDS mostra pizzaiolos pegando tasks. +3. Despacho: verdes → rotas → mapa. +4. Entregas fecham sozinhas no ETA. +5. Desliga para o tick. +6. Limpar remove só dados `sim:` sem derrubar cardápio/seed real. diff --git a/Pizza-Digital/docs/SPEC-USUARIOS.md b/Pizza-Digital/docs/SPEC-USUARIOS.md new file mode 100644 index 0000000..7066a6a --- /dev/null +++ b/Pizza-Digital/docs/SPEC-USUARIOS.md @@ -0,0 +1,88 @@ +# SPEC — Usuários, senha e sessão + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Capítulo** | Equipe / Auth | +| **Versão** | 0.1.0 | +| **Data** | 2026-10-20 | +| **Owner** | Roger / iTecnologys | +| **UI** | `/admin/equipe` · `/forgot-password` · `/reset-password` | +| **API** | `/api/users` · `/api/auth/*` | +| **Irmã** | [`SPEC-MATRIZ-ACESSO.md`](SPEC-MATRIZ-ACESSO.md) | + +--- + +## 1. Objetivo + +Cadastrar quem opera a loja (PDV, KDS, despacho, gestor), com senha forte, bloqueio por tentativa, recuperação e vínculo à matriz de acesso. + +--- + +## 2. Modelo + +```text +User + email, name, role (gestor|pdv|kds|despacho) + jobTitle, areas[], phone, active + passwordHash, mustChangePassword, passwordChangedAt + failedLoginAttempts, lockedUntil, lastLoginAt + profileTemplateKey ← rótulo do último modelo aplicado + +AuthToken password_reset | invite | email_verify +PushDevice token web/app +NotifyLog email | push (auditoria de envio) +UserCapability fonte da verdade no vínculo (SPEC matriz) +``` + +--- + +## 3. Requisitos + +| ID | Requisito | Status | +|----|-----------|--------| +| RF-USR-01 | CRUD equipe (`staff.view` / `staff.manage`) | **feito** | +| RF-USR-02 | Cargo (`jobTitle`) + áreas (`pdv`, `kds`, `despacho`, `admin`, …) | **feito** | +| RF-USR-03 | Política de senha: ≥8, maiúscula, minúscula, dígito; sem o local-part do e-mail | **feito** | +| RF-USR-04 | Lockout: 5 falhas → 15 min (`lockedUntil`) | **feito** | +| RF-USR-05 | Recuperação por e-mail (link 60 min) + stub push | **feito** (SMTP produção opcional) | +| RF-USR-06 | Convite / `mustChangePassword` no primeiro acesso | **feito** | +| RF-USR-07 | Aplicar template da matriz no cadastro | **feito** | +| RF-USR-08 | Gestor extra exige `owners.manage` | **feito** | +| RF-USR-09 | Desbloquear conta (`unlock`) | **feito** | +| RF-USR-10 | SMTP/push reais em produção | **stub** | + +--- + +## 4. Política de senha (congelada) + +```text +minLength 8 · max 128 +1 maiúscula · 1 minúscula · 1 dígito +não contém o e-mail +maxFailedAttempts 5 · lockMinutes 15 +resetTokenTtlMinutes 60 +``` + +Código: `apps/api/src/auth/password-policy.ts`. + +--- + +## 5. Fluxos + +1. Gestor cria usuário em `/admin/equipe` → hash bcrypt → opcional convite. +2. Login `/api/auth/login` incrementa falhas; 5× bloqueia 15 min. +3. Esqueci senha → `AuthToken.password_reset` → e-mail com link (resposta genérica, não vaza existência). +4. Reset consome token, valida política, limpa lockout. + +--- + +## 6. Fora desta SPEC + +- SSO / SAML +- 2FA TOTP (fase futura) +- Multi-loja no mesmo login + +--- + +*Satélite. Produto pai: SPEC.md v0.3.5.* diff --git a/Pizza-Digital/docs/SPEC.md b/Pizza-Digital/docs/SPEC.md new file mode 100644 index 0000000..8a7ac92 --- /dev/null +++ b/Pizza-Digital/docs/SPEC.md @@ -0,0 +1,724 @@ +# SPEC — Pizza Digital + +| Campo | Valor | +|-------|--------| +| **Produto** | Pizza Digital | +| **Versão do SPEC** | **0.3.2** | +| **Status** | Fase 1 cozinha no ar · **Prioridade seguinte: entrada por canais digitais** | +| **Data** | 2026-07-23 · rev. matriz acesso 2026-10-19 | +| **Owner** | Roger / iTecnologys | +| **Host Dev** | Proxmox BIG · VMID **125** · `10.10.10.125` · user `mini` | +| **URL pública** | `https://pizza.itecnologys.com` (legado SPEC: pizza.myvexx.com) | +| **Detalhe Fase 1** | [`docs/SPEC-FASE1.md`](docs/SPEC-FASE1.md) | +| **Canais digitais** | [`docs/SPEC-CANAIS-DIGITAIS.md`](docs/SPEC-CANAIS-DIGITAIS.md) · §5.5 | +| **Cardápio** | [`docs/SPEC-CARDAPIO.md`](docs/SPEC-CARDAPIO.md) · RF-MEN · Joinville | +| **Estoque** | [`docs/SPEC-ESTOQUE.md`](docs/SPEC-ESTOQUE.md) · RF-EST · BOM ↔ menu | +| **Matriz de acesso** | [`docs/SPEC-MATRIZ-ACESSO.md`](docs/SPEC-MATRIZ-ACESSO.md) · `/acessos` · RF-ACS | +| **Sequência oficial** | §15 — S0…S9 · Track C · Track M/E · Matriz ACS | +| **Referência principal** | CityFoods — Cozinha Digital + Cardápio Digital (`@cityfoods_app`) | +| **KDS / estações** | kitchenOS + restaurant-pos + este SPEC | +| **Rotas / dispatch** | **rusterando** = referência oficial (ORS, driver tour) — AGPL: modelo operacional, não fork do core | +| **Repos open-source** | kitchenOS, rusterando, URY Mosaic, MaSoVa, Tasty-Station | + +--- + +## 1. Visão + +**Pizza Digital** é uma plataforma de operação de delivery para pizzarias, restaurantes e outras cozinhas, com foco em **Cozinha Digital (KDS)**: eliminar comanda de papel, organizar produção por estações, reduzir erros e acelerar despacho. + +Não é um marketplace. É o **sistema operacional da cozinha + delivery próprio**, no mesmo espírito da CityFoods: + +> Cozinha Digital não é sobre robôs — é inteligência e fluxo à prova de falhas, do pedido à entrega. + +### 1.1 Problema + +Operações de delivery perdem tempo e clientes por: + +- Comandas de papel (perda, rasura, leitura ruim) +- Pedidos de vários canais em telas diferentes +- Falta de priorização (pedido novo não sai junto de outros prontos) +- Erros de observação / montagem +- Motoboys mal roteados (ida e volta na mesma região) +- Zero métrica de tempo por estação + +### 1.2 Promessa + +Uma plataforma única que: + +1. Recebe pedidos (PDV, cardápio digital, marketplaces) +2. Distribui por **estações de cozinha** (KDS) +3. Identifica pedidos por **etiquetas** +4. Prioriza e agrupa saída +5. Despacha com **gestão de rotas** +6. Mede **tempo e produtividade** em tempo real + +### 1.3 Posicionamento + +| | CityFoods (referência) | Pizza Digital (nossa) | +|--|------------------------|------------------------| +| Foco | Delivery multi-tipo | **Pizzaria primeiro**, depois outras cozinhas | +| Diferencial citado | “Primeira Cozinha Digital” | Cozinha Digital + templates de estação por tipo de negócio | +| Mercado | SC / BR SaaS | Começar local / controlado, evoluir para SaaS multi-tenant | + +--- + +## 2. Objetivos e métricas de sucesso + +### 2.1 Objetivos de produto (MVP) + +- Substituir comanda de papel por KDS touch +- Operar **1 pizzaria piloto** de ponta a ponta (pedido → pronto → despacho) +- Reduzir erros de montagem / observação +- Medir tempo médio por estação e por pedido + +### 2.2 KPIs (piloto) + +| KPI | Meta inicial (90 dias) | +|-----|------------------------| +| Erros de pedido (reclamação / refazer) | −50% vs baseline | +| Tempo médio pedido → pronto | −20% | +| Pedidos sem papel na cozinha | 100% no piloto | +| Atraso de despacho (> SLA) | −30% | +| Adoção diária do KDS | ≥ 95% dos pedidos passam pelo KDS | + +### 2.3 Não-objetivos (MVP) + +- App consumidor nativo (iOS/Android) completo +- Marketplace próprio +- Contabilidade / ERP completo +- Multi-país / multi-moeda +- IA generativa na cozinha + +--- + +## 3. Personas e papéis + +| Papel | Quem | Precisa | +|-------|------|---------| +| **Operador KDS** | Cozinheiro / pizzaiolo | Tela grande, bump rápido, ver observações | +| **Expedição** | Embalador / conferente | Pedido completo, etiqueta, agrupar saída | +| **Caixa / PDV** | Atendente | Lançar pedido balcão / telefone | +| **Despachante** | Quem libera motoboy | Agrupar por região, priorizar | +| **Entregador** | Motoboy | Lista de entregas, status, navegação básica | +| **Gestor** | Dono / gerente | Métricas, cardápio, usuários, relatórios | +| **Admin SaaS** (fase 2) | iTecnologys | Multi-loja, billing, suporte | + +--- + +## 4. Escopo por fases + +### Fase 0 — Fundação (esta SPEC + bootstrap) + +- Repositório, monorepo, CI mínimo +- Modelo de domínio e eventos +- Ambiente local (Docker) + +### Fase 1 — MVP Cozinha Digital (core) ✅ em andamento + +> Detalhe completo: [`docs/SPEC-FASE1.md`](docs/SPEC-FASE1.md) · Host: VM **125** `10.10.10.125` + +- Pedidos manuais (PDV web) +- KDS multi-estação (pizzaria) +- Status: `novo → em_preparo → pronto → despachado → entregue` +- Priorização / reordenação de fila +- Etiquetas (impressão ESC/POS ou PDF fallback) +- Painel gestor básico (tempos) +- Stack: NestJS + React + Postgres + Redis + Socket.io (Docker na VM 125) + +### Fase 2 — Delivery operacional (após KDS estável) + +> Prioridade de construção: **KDS multi-estação primeiro**; delivery/rotas só depois do piloto de cozinha (S5). +> Referência oficial de dispatch: **[rusterando](https://github.com/holg/rusterando)** (kitchen → driver tour → ORS). Detalhe: §15.1 e §15.2. + +- Gate `order.ready` → fila de despacho +- Agrupar por bairro (manual + sugestão) + atribuir motoboy + reordenar saída (“2 cliques” CityFoods) +- OpenRouteService (padrão rusterando) para ordem da rota +- Mapa no despacho · PWA entregador · tracking +- Pagamento/comissão por entrega · fechamento de caixa básico + +### Fase 3 — Canais digitais e SaaS (PRIORIDADE pós-S5) + +> Track **C** (canais) começa em paralelo assim que o piloto de cozinha (S5) estiver estável — **não esperar o fim de S6–S8**. +> Ver §5.5 e [`docs/SPEC-CANAIS-DIGITAIS.md`](docs/SPEC-CANAIS-DIGITAIS.md). + +- **C1** Cardápio digital público (PWA) + checkout (Pix / dinheiro na entrega) +- **C1** Pedidos digitais no quadro `/pedidos` com badge de origem +- **C2** Integração iFood (webhook → pedido normalizado) +- Multi-tenant / multi-loja +- Relatórios operacionais avançados +- Templates de cozinha: pizzaria, hamburgueria, sushi, dark kitchen + +--- + +## 5. Conceito operacional — Cozinha Digital + +### 5.1 Estações (template Pizzaria) + +| Estação | Responsabilidade | Exemplos de itens | +|---------|------------------|-------------------| +| **Massa** | Abrir / preparar base | Massa, borda | +| **Montagem** | Molho, queijo, toppings | Sabores, adicionais | +| **Forno** | Assar | Pizza em forno | +| **Expedição** | Embalar, conferir, etiquetar | Pedido completo | +| **Bebidas** (opcional) | Separar frios | Refrigerante, água | + +**Princípio KDS (CityFoods — claim exclusivo):** + +1. A **fila chega para todas** as estações. +2. Uma estação **escolhe e puxa (PEGAR)** um ou mais pedidos para si. +3. Com o claim, **as outras estações ficam bloqueadas** de pegar aquele pedido. +4. Só a estação dona dá **BUMP** (pedido pronto). + +Regras de mapeamento (histórico / tickets): + +- Cada item do cardápio mapeia para **1+ estações** (referência de produção) +- Claim é ao nível do **pedido completo**, não por item + +### 5.2 Template futuro — outras cozinhas + +| Tipo | Estações padrão | +|------|-----------------| +| Hamburgueria | Grelha · Fritura · Montagem · Expedição | +| Sushi | Corte · Montagem · Expedição | +| Dark kitchen genérica | Prep · Quente · Frios · Expedição | + +### 5.3 Etiquetas + +Cada pedido (ou item, configurável) gera etiqueta com: + +- Código curto do pedido (ex: `#A42`) +- Canal (PDV / iFood / Cardápio) +- Cliente (nome parcial) +- Itens resumidos + observações em destaque +- Horário / timer SLA +- QR opcional para conferência + +Objetivo: eliminar grampear comanda ilegível na embalagem. + +### 5.4 Priorização (inspirado CityFoods “2 cliques”) + +O despachante/gestor pode: + +- Reordenar fila de saída +- Agrupar pedidos da mesma região para sair juntos +- Marcar pedido como **urgente** (sobresai visualmente no KDS) + +### 5.5 Entrada por canais digitais (PRIORIDADE) + +> Detalhe de produto: [`docs/SPEC-CANAIS-DIGITAIS.md`](docs/SPEC-CANAIS-DIGITAIS.md) · RFs: RF-CAN, RF-PED-08/09, RF-MEN-04/06/07, RF-INT-01/03/05 + +**Problema:** pedidos chegam por telefone, WhatsApp e iFood em telas diferentes — a loja digita de novo no PDV e erra. + +**Promessa:** o cliente pede no canal digital; a loja vê **o mesmo Order** no quadro `/pedidos` e a cozinha trata igual a pedido de balcão. + +#### Dimensões (substituem `channel` misturado) + +| Dimensão | Valores | Uso na UI | +|----------|---------|-----------| +| `origin` | `pdv` · `cardapio` · `ifood` · `whatsapp` · `telefone` | Badge no card (CARDÁPIO / IFOOD / …) | +| `fulfillment` | `entrega` · `retirada` · `mesa` | Faixa ENTREGA / RETIRADA (CityFoods) | + +#### Canais de entrada → um quadro + +```text +Cardápio PWA /c/:slug ─┐ +WhatsApp (deep-link) ─┼─► Order Ingest (schema único) +iFood webhook ─┤ │ +PDV / telefone (staff) ─┘ ▼ + /pedidos (badge origem + fulfillment) + │ + ▼ + KDS · Etiqueta · Despacho +``` + +#### Track C (após piloto S5; C1 pode prototipar em paralelo) + +| Track | Entrega | +|-------|---------| +| **C1** | Cardápio público + checkout (Pix / pagar na entrega) → `/pedidos` ≤ 500 ms | +| **C2** | Webhook iFood → normalizador + idempotência | + +**Regra de ouro:** nenhum canal tem UI de cozinha própria — tudo passa pelo Order bus e pelo `/pedidos`. + +--- + +## 6. Fluxos principais + +### 6.1 Fluxo feliz — delivery pizzaria (canal digital ou PDV) + +```text +[Cliente] Cardápio /c/:slug | WhatsApp | iFood +[Staff] PDV /pedidos (+ Novo) + → Order Ingest normaliza (origin + fulfillment + itens) + → Aparece em /pedidos (badge origem · faixa ENTREGA/RETIRADA) + → Roteia itens → estações KDS + → Operadores dão claim/bump por estação + → Expedição confere + imprime etiqueta (origem + fulfillment) + → Pedido = PRONTO + → Despacho atribui motoboy / agrupa rota + → Entregador: saiu → entregue + → Métricas gravadas (tempos por etapa) +``` + +### 6.2 Estados do pedido + +```text +rascunho → confirmado → em_producao → pronto → em_rota → entregue + ↓ + cancelado +``` + +Estados de **item na estação**: + +```text +na_fila → em_preparo → concluido | cancelado +``` + +### 6.3 Diagrama lógico + +```text +Canais Core Operação +───── ──── ──────── +PDV ──────┐ +Cardápio ─┼─► Order Ingest ─► Router ─► KDS Stations +iFood ────┘ │ │ │ + │ │ ▼ + │ └──► Labels / Print + ▼ + Order Store ◄── WebSocket fan-out + │ + ▼ + Dispatch / Routes ─► Driver PWA + │ + ▼ + Analytics +``` + +--- + +## 7. Requisitos funcionais + +### 7.1 Pedidos (RF-PED) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-PED-01 | Criar pedido no PDV (itens, adicionais, obs, tipo: delivery/balcão/mesa) | Sim | +| RF-PED-02 | Schema único de pedido independente do canal | Sim | +| RF-PED-03 | Editar pedido até entrar em produção (política configurável) | Sim | +| RF-PED-04 | Cancelar pedido com motivo | Sim | +| RF-PED-05 | Observações em destaque no KDS | Sim | +| RF-PED-06 | Número sequencial diário + código curto legível | Sim | +| RF-PED-07 | Ingestão via webhook (iFood etc.) | Track C2 | +| RF-PED-08 | Campos `origin` + `fulfillment` (em vez de channel misturado) | Track C1 | +| RF-PED-09 | Pedido digital aparece no `/pedidos` em tempo real (WS) | Track C1 | + +### 7.2 KDS / Cozinha Digital (RF-KDS) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-KDS-01 | Tela KDS por estação (filtro de área) | Sim | +| RF-KDS-02 | Atualização em tempo real (WebSocket, sem polling) | Sim | +| RF-KDS-03 | Timer visual (verde / amarelo / vermelho por SLA) | Sim | +| RF-KDS-04 | Bump item / bump pedido na estação | Sim | +| RF-KDS-05 | Multi-estação no mesmo pedido | Sim | +| RF-KDS-06 | Som/alerta opcional de novo ticket | Sim | +| RF-KDS-07 | Modo tela cheia / tablet / TV | Sim | +| RF-KDS-08 | Offline degradado (fila local + sync) | Fase 2 | + +### 7.3 Etiquetas (RF-ETQ) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-ETQ-01 | Gerar etiqueta ao concluir expedição (ou sob demanda) | Sim | +| RF-ETQ-02 | Impressão térmica ESC/POS | Sim (com fallback PDF) | +| RF-ETQ-03 | Layout configurável por loja | Fase 2 | + +### 7.4 Despacho e rotas (RF-ROT) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-ROT-01 | Lista de pedidos prontos para despacho | Fase 1 (lista simples) | +| RF-ROT-02 | Atribuir entregador | Fase 2 | +| RF-ROT-03 | Agrupar por bairro / proximidade | Fase 2 | +| RF-ROT-04 | Reordenar saída (priorização) | Fase 1 | +| RF-ROT-05 | App/PWA entregador com status | Fase 2 | +| RF-ROT-06 | Cálculo de pagamento de entregadores | Fase 3 | + +### 7.5 Cardápio e PDV (RF-MEN) + +> Detalhe: [`docs/SPEC-CARDAPIO.md`](docs/SPEC-CARDAPIO.md) · pesquisa [`docs/CARDAPIO-JOINVILLE.md`](docs/CARDAPIO-JOINVILLE.md) +> Ponte estoque: [`docs/SPEC-ESTOQUE.md`](docs/SPEC-ESTOQUE.md) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-MEN-01 | CRUD categorias, itens, preços, adicionais | Sim | +| RF-MEN-02 | Mapear item → estações KDS | Sim | +| RF-MEN-03 | Tempo SLA por categoria/item | Sim | +| RF-MEN-04 | Cardápio digital público (PWA cliente) | **Track C1** | +| RF-MEN-05 | Combos / meia-a-meia (pizzaria) | Fase 1 (meia-a-meia) | +| RF-MEN-06 | Horário de funcionamento / loja aberta-fechada no cardápio | Track C1 | +| RF-MEN-07 | Áreas de entrega + taxa por bairro no cardápio | Track C1 | +| RF-MEN-08 | Tamanhos com preço (`broto`/`grande`/…) | Track M1 | +| RF-MEN-09 | Pausar item (manual ou estoque baixo) | Track M1 | +| RF-MEN-10 | Ficha técnica (BOM) ligada ao item | Track M1 ↔ EST | +| RF-MEN-11 | Bebida `stockMode=sku` 1:1 | Track M1 | +| RF-MEN-12 | Vitamina/suco natural via BOM | Track M1 | + +### 7.6 Gestão e métricas (RF-GES) + +| ID | Requisito | MVP | +|----|-----------|-----| +| RF-GES-01 | Dashboard: pedidos abertos, atrasados, tempo médio | Sim | +| RF-GES-02 | Tempo médio por estação | Sim | +| RF-GES-03 | Relatório de fluxo do pedido (timestamps) | Sim | +| RF-GES-04 | Usuários, papéis, permissões | Sim | +| RF-GES-05 | Multi-loja / tenant | Fase 3 | + +### 7.7 Integrações (RF-INT) + +| ID | Requisito | Fase | +|----|-----------|------| +| RF-INT-01 | Webhook genérico de pedido | Track C1 | +| RF-INT-02 | iFood (ou agregador BR) | Track C2 | +| RF-INT-03 | Pagamento online (Pix / dinheiro na entrega) | Track C1 | +| RF-INT-04 | Impressoras de rede | 1 | +| RF-INT-05 | Deep-link WhatsApp → cardápio da loja | Track C1 | + +### 7.8 Canais digitais (RF-CAN) + +| ID | Requisito | Track | +|----|-----------|-------| +| RF-CAN-01 | PWA cardápio público por loja (`/c/:slug`) | C1 | +| RF-CAN-02 | Carrinho cliente → Order com `origin=cardapio` | C1 | +| RF-CAN-03 | Escolha fulfillment entrega/retirada no cardápio | C1 | +| RF-CAN-04 | Taxa / área de entrega no checkout | C1 | +| RF-CAN-05 | Pedido digital no `/pedidos` ≤ 500 ms (WS) | C1 | +| RF-CAN-06 | Badge de origem no card (CARDÁPIO / IFOOD / …) | C1 | +| RF-CAN-07 | Webhook iFood → normalizador | C2 | +| RF-CAN-08 | Idempotência / dedupe de pedidos externos | C2 | + +### 7.9 Estoque (RF-EST) + +> Detalhe: [`docs/SPEC-ESTOQUE.md`](docs/SPEC-ESTOQUE.md) · dispara a partir do Order · pausa o Cardápio + +| ID | Requisito | Track | +|----|-----------|-------| +| RF-EST-01 | CRUD insumos (unidade, min, custo) | E1 | +| RF-EST-02 | Ficha técnica por MenuItem | E1 | +| RF-EST-03 | Baixa automática no pedido (`em_producao`) | E1 | +| RF-EST-04 | Entrada compra / ajuste manual | E1 | +| RF-EST-05 | Alerta estoque mínimo (WS) | E1 | +| RF-EST-06 | Pausa automática do item no cardápio | E2 | +| RF-EST-07 | Estorno em cancelamento elegível | E1 | +| RF-EST-08 | CMV teórico por dia/item | E2 | +| RF-EST-13 | Ficha por pizza com qty em **gramas** (38 tipos) | E1 | +| RF-EST-14 | Catálogo `fichas-pizzas-peso.json` | E1 | + +**Regra de ouro:** Cardápio declara `stockMode` (`bom`\|`sku`\|`none`); Estoque executa a baixa. +**Pizzas:** baixa por **peso (g)** por tipo — [`docs/FICHAS-TECNICAS-PESO.md`](docs/FICHAS-TECNICAS-PESO.md). + +--- + +## 8. Requisitos não-funcionais + +| ID | Tema | Requisito | +|----|------|-----------| +| RNF-01 | Latência KDS | Novo pedido visível na tela ≤ **500 ms** (LAN saudável) | +| RNF-02 | Disponibilidade | MVP single-node OK; alvo produção 99.5% | +| RNF-03 | Concorrência | Suportar pico de **60 pedidos/hora** no piloto | +| RNF-04 | UX cozinha | Touch-first, botões grandes, alto contraste, legível a 1–2 m | +| RNF-05 | Segurança | Auth JWT/session; senhas hashed; HTTPS; RBAC | +| RNF-06 | Auditoria | Log de mudanças de status com user + timestamp | +| RNF-07 | Observabilidade | Logs estruturados + healthcheck | +| RNF-08 | Idioma | PT-BR nativo | +| RNF-09 | Deploy | Docker Compose local; caminho claro para VPS/Proxmox | +| RNF-10 | LGPD | Dados de cliente mínimos; retenção configurável | + +--- + +## 9. Arquitetura alvo + +### 9.1 Princípios + +1. **Order bus / schema único** — todo canal vira o mesmo `Order` +2. **Event-driven para cozinha** — WebSocket fan-out por loja/estação +3. **Station routing** — regras no servidor, não na UI +4. **PWA** — KDS e entregador rodam no browser/tablet +5. **Começar simples** — 1 processo API + Postgres + Redis; extrair microsserviços só se necessário + +### 9.2 Stack sugerida (do zero) + +| Camada | Tecnologia | Motivo | +|--------|------------|--------| +| Frontend | React + Vite + TypeScript (PWA) | KDS/PDV touch, ecossistema forte | +| Realtime | Socket.io ou native WS | Padrão dos KDS de referência | +| API | NestJS ou Fastify (Node/TS) | Tipagem compartilhada, rapidez | +| DB | PostgreSQL | Pedidos, auditoria, relatórios | +| Cache / pubsub | Redis | Tickets abertos + fan-out | +| Filas (fase 2+) | BullMQ | Webhooks, impressão, retries | +| Print | ESC/POS (node) + PDF fallback | Etiquetas térmicas | +| Infra local | Docker Compose | Postgres + Redis + API + Web | + +Referências de forma (não copy-paste cego): + +- **kitchenOS** → ingestão + KDS + Redis stream +- **Tasty-Station** → Socket.io POS↔KDS +- **rusterando** → **referência oficial** pizzaria + rotas / ORS / driver tour (modelo, não fork AGPL) +- **URY Mosaic** → multi-kitchen KDS + +### 9.3 Apps da plataforma + +| App | URL mental | Usuário | +|-----|------------|---------| +| `web-pdv` | `/pdv` | Caixa | +| `web-kds` | `/kds/:station` | Cozinha | +| `web-dispatch` | `/despacho` | Despachante | +| `web-admin` | `/admin` | Gestor | +| `web-driver` | `/entregador` | Motoboy (fase 2) | +| `web-menu` | `/m/:slug` | Cliente (fase 3) | + +Monorepo sugerido: + +```text +pizza-digital/ +├── SPEC.md # este documento +├── docs/ +│ ├── domain.md # glossário e regras +│ ├── api.md # contratos HTTP/WS +│ └── ui-kds.md # wireframes textuais +├── apps/ +│ ├── web/ # PDV + KDS + Admin (rotas) +│ └── api/ +├── packages/ +│ ├── domain/ # types Order, Station, events +│ └── escpos/ # impressão +└── docker-compose.yml +``` + +--- + +## 10. Modelo de domínio (MVP) + +### 10.1 Entidades + +- **Store** — loja +- **User** — usuário + role +- **Station** — estação KDS +- **Category / Product / Modifier** — cardápio ([SPEC-CARDAPIO](docs/SPEC-CARDAPIO.md)) +- **ProductStationRule** — produto → estações +- **Recipe / RecipeLine** — ficha técnica BOM ([SPEC-ESTOQUE](docs/SPEC-ESTOQUE.md)) +- **Ingredient** — insumo com saldo +- **StockMovement** — entrada/saída/ajuste +- **Order** — pedido +- **OrderItem** — item do pedido (+ modifiers, notes) +- **StationTicket** — fatia do item/pedido na estação +- **LabelPrintJob** — job de etiqueta +- **Courier** (fase 2) — entregador +- **DispatchBatch** (fase 2) — lote de rota + +### 10.2 Campos críticos de `Order` + +```text +id, store_id, code, channel, type (delivery|pickup|dine_in), +status, customer_name, customer_phone, address, +notes, priority, promised_at, +accepted_at, production_started_at, ready_at, +dispatched_at, delivered_at, canceled_at, +created_at, updated_at +``` + +### 10.3 Eventos (WebSocket) + +```text +order.created +order.updated +order.canceled +ticket.upserted +ticket.bumped +order.ready +order.dispatched +order.delivered +label.printed +``` + +Rooms: `store:{id}`, `store:{id}:station:{stationId}` + +--- + +## 11. Regras de negócio — pizzaria + +1. **Meia-a-meia**: 1 pizza = 2 sabores; preço = maior sabor (configurável); aparece no KDS como um item com 2 metades. +2. **Borda / adicionais**: modifiers ligados à montagem (ou massa, se borda recheada). +3. **Tempo SLA padrão** (editável): Massa 3 min · Montagem 5 · Forno 8 · Expedição 3 — total alvo ~20 min. +4. **Pedido delivery sem endereço** não confirma. +5. **Bump na expedição** só liberado se estações anteriores concluídas (ou override gestor). +6. **Prioridade urgente** não pula forno fisicamente — só sobe na fila visual e alerta. + +--- + +## 12. UX — KDS (requisitos de interface) + +- Cards grandes por pedido; código `#A42` enorme +- Observação do cliente em **cor de alerta** +- Timer no canto; vira amarelo/vermelho por threshold +- Botão único: **PRONTO** (item) / **TUDO PRONTO** (estação) +- Badge do canal (PDV / iFood / …) +- Sem cards decorativos desnecessários — densidade operacional +- Funciona em tablet paisagem e TV 32"+ + +--- + +## 13. Segurança e permissões (RBAC) + +| Recurso | Operador KDS | Expedição | PDV | Despacho | Gestor | +|---------|--------------|-----------|-----|----------|--------| +| Ver KDS da sua estação | ✓ | ✓ | — | — | ✓ | +| Bump tickets | ✓ | ✓ | — | — | ✓ | +| Criar pedido | — | — | ✓ | — | ✓ | +| Cancelar pedido | — | — | ✓* | — | ✓ | +| Despachar | — | ✓ | — | ✓ | ✓ | +| Cardápio / users | — | — | — | — | ✓ | +| Métricas | — | — | — | leitura | ✓ | + +\*cancelar só antes de produção, salvo permissão. + +--- + +## 14. Critérios de aceite do MVP + +O MVP está **aceito** quando, em ambiente piloto: + +1. Atendente cria pedido no PDV com 2 pizzas (1 meia-a-meia) + bebida + observação +2. Tickets aparecem nas estações corretas em < 1 s +3. Cada estação conclui seus itens; expedição só libera no fim +4. Etiqueta é gerada/impressa +5. Gestor reordena/prioritiza um pedido pronto +6. Dashboard mostra tempos por estação daquele pedido +7. Zero uso de papel na cozinha durante o teste supervisionado + +--- + +## 15. Sequência oficial de implementação (S0–S9) + +> **Congelado 2026-07-23 (SPEC 0.2.1).** +> Ordem: **KDS por estação antes de delivery/rotas.** +> Ciclo inspirado no rusterando: `cart/PDV → kitchen board → driver tour → ORS`. +> Não forkamos o binário Rust/AGPL; reutilizamos o **modelo operacional** no stack Nest/React. + +### 15.1 Mapa de sprints + +| Sprint | Bloco (rusterando) | Entrega Pizza Digital | Status | +|--------|--------------------|------------------------|--------| +| **S0** | Foundation | Repo + Docker Compose + domain types + SPEC | ✅ | +| **S1** | Auth + menu admin | Auth JWT + Store + Cardápio + mapa item→estações | ✅ (`pizza.myvexx.com`) | +| **S2** | Cart → order | **PDV** + criar pedido + state machine `confirmado→em_producao→pronto` | ✅ | +| **S3** | Kitchen board | **KDS multi-estação** realtime (bump, timers, rooms Socket.io) | ✅ | +| **S4** | Ready / expedição | Etiquetas + fila `pronto` + priorização + dashboard tempos | pendente | +| **S5** | Piloto cozinha | 1 pizzaria estável sem papel (aceite §14) | pendente | +| **S6** | Driver tour (manual) | Despacho: fila só `pronto` → agrupar bairro → motoboy → reordenar | pendente | +| **S7** | **ORS** | Gestor de Rotas: `DispatchBatch` + mapa permanente + heurística; ORS com `ORS_API_KEY` | ✅ parcial (ORS opcional) | +| **S8** | Driver + push | `/motoboy` + rota + GPS ping (+ push staff depois) | ✅ parcial (sem PIN/offline) | +| **S9 / Track C1** | Public menu / pay | **Cardápio digital** + checkout (Pix / dinheiro) → quadro `/pedidos` | **PRIORIDADE pós-S5** | +| **Track C2** | Agregadores | iFood webhook → Order normalizado | após C1 | + +**Gate entre mundos (obrigatório):** +`order.ready` → fila despacho → batch → ORS *(só a partir de S6/S7)*. + +### 15.2 Módulo Rotas (CityFoods → Pizza Digital) + +**Referência oficial de dispatch:** [rusterando](https://github.com/holg/rusterando) (AGPL-3.0). + +| Subfase | Capacidade | Sprint | +|---------|------------|--------| +| MVP rotas | Fila só pedidos `pronto` | S6 | +| MVP rotas | Agrupar por bairro (manual + sugestão) | S6 | +| MVP rotas | Atribuir motoboy | S6 | +| MVP rotas | Reordenar saída (“2 cliques” CityFoods) | S6 | +| Ampliação | OpenRouteService — ordem ótima da tour | S7 | +| Ampliação | Mapa no despacho | S7 | +| Ampliação | PWA entregador + tracking | S8 | +| Ampliação | Pagamento / comissão por entrega | S8–S9 | + +Papéis rusterando → Pizza Digital: Admin≈gestor · Kitchen≈kds/estações · Driver≈entregador · Customer≈canal cardápio (S9). + +### 15.3 Referências por domínio + +| Domínio | Fonte | +|---------|--------| +| KDS / estações / bump | Este SPEC + kitchenOS / Tasty-Station | +| Despacho / ORS / driver tour | **rusterando** (oficial) + CityFoods Gestor de Rotas | +| UX “2 cliques” priorização | CityFoods | + +### 15.4 Design UI + +Status: **ainda não congelado.** S1 tem UI funcional (login + admin cardápio). +Antes de S3: wireframes PDV → KDS (touch) → Despacho. Design system em documento separado (`docs/DESIGN.md`) quando existir. + +--- + +## 16. Riscos + +| Risco | Mitigação | +|-------|-----------| +| Cozinha rejeita tela (hábito do papel) | UX bump mínima + treinamento 1 turno + impressão híbrida opcional | +| Impressora térmica incompatível | Driver ESC/POS + fallback PDF/A4 | +| Pico de pedidos derruba WS | Redis pubsub + rooms por loja; load test 60/h | +| Escopo inchando (virar iFood) | Freeze: MVP = cozinha digital, não marketplace | +| Integração iFood cedo demais | Só após piloto estável | + +--- + +## 17. Glossário + +| Termo | Significado | +|-------|-------------| +| **KDS** | Kitchen Display System — tela digital da cozinha | +| **Bump** | Marcar item/pedido como pronto na estação | +| **Estação** | Setor de produção (massa, forno, …) | +| **Ticket** | Representação do trabalho de um item/pedido numa estação | +| **SLA** | Tempo alvo até alerta de atraso | +| **Despacho** | Liberação do pedido pronto para entregador | +| **Canal** | Origem do pedido (PDV, cardápio, iFood, …) | +| **Cozinha Digital** | Operação completa sem comanda de papel + métricas | +| **BOM / Ficha técnica** | Lista de insumos por item do cardápio | +| **stockMode** | `bom` · `sku` · `none` — como o item consome estoque | +| **CMV** | Custo da mercadoria vendida (teórico via BOM) | + +--- + +## 18. Decisões em aberto (para fechar com Roger) + +1. Stack final: **NestJS** ✅ (congelado Fase 1) +2. Piloto: qual pizzaria / volume médio de pedidos/dia? +3. Impressora térmica já definida (marca/modelo)? +4. MVP inclui **mesa/comanda salão** ou só delivery + balcão? +5. Hospedagem inicial → **Proxmox BIG VM125** (`10.10.10.125`) ✅ +6. Domínio → **`https://pizza.myvexx.com`** ✅ +7. Sequência S0–S9 + rusterando = ref. rotas → **congelado §15** ✅ +8. Design UI/UX (wireframes KDS/PDV/despacho) — em aberto + +--- + +## 19. Referências + +- CityFoods Cozinha Digital — https://abre.ai/cityfoods / https://cityfoods.com.br/ +- CityFoods Instagram — https://www.instagram.com/cityfoods_app/ +- kitchenOS — https://github.com/rajeshselvam02/kitchenOS +- rusterando (**ref. oficial dispatch/ORS**) — https://github.com/holg/rusterando +- URY / Mosaic — https://github.com/ury-erp/ury +- MaSoVa — https://github.com/SVamseekar/masova-platform +- Tasty-Station POS — https://github.com/hey-Zayn/Tasty-Station-POS + +--- + +## 20. Próximo passo imediato + +1. **S4** — Etiquetas + priorização + dashboard tempos (fechar cozinha) +2. **S5** — Piloto pizzaria (cozinha estável) +3. **Track C1 — entrada digital** — cardápio público → `/pedidos` (§5.5) ← **foco de produto** +4. **Track E1 / M1** — Estoque + ficha técnica ligados ao cardápio Joinville +5. Em paralelo ou depois: S6–S8 (rotas) · Track C2 (iFood) + +--- + +*Documento vivo. Mudanças de escopo devem atualizar versão (ex.: 0.2.0 → 0.2.1) e registrar changelog em `docs/CHANGELOG-SPEC.md`.* diff --git a/Pizza-Digital/docs/SSH-CLAUDE-CLOUD-HANDOFF.md b/Pizza-Digital/docs/SSH-CLAUDE-CLOUD-HANDOFF.md new file mode 100644 index 0000000..13e4c35 --- /dev/null +++ b/Pizza-Digital/docs/SSH-CLAUDE-CLOUD-HANDOFF.md @@ -0,0 +1,85 @@ +# Handoff — SSH Claude Cloud → VM125 + +| Campo | Valor | +|-------|--------| +| Data | 2026-10-19 | +| Status | **Parcial** — chave instalada; túnel Cloudflare **ainda não** provisionado | +| Motivo da pausa | Usuário vai desligar a máquina | + +--- + +## Objetivo + +Dar SSH à VM125 (`mini@10.10.10.125`, projeto `~/pizza-digital`) para Claude Code em contêiner cloud que: + +- não alcança `10.10.10.x` nem Tailscale `100.x` +- só sai via proxy HTTPS (:443) +- só acessa domínios liberados na política de rede do ambiente + +--- + +## Hostname proposto + +| Item | Valor | +|------|--------| +| Hostname | **`ssh-pizza.myvexx.com`** | +| Proteção | Cloudflare Access + **service token** (Client ID + Client Secret) | +| Cliente Claude | `cloudflared access ssh` como `ProxyCommand` | +| Backend do tunnel | `localhost:22` na VM125 | + +Service token: **não colar no chat** — configurar como secrets/env no ambiente Claude (`CF_ACCESS_CLIENT_ID`, `CF_ACCESS_CLIENT_SECRET`). + +--- + +## Feito nesta sessão (antes do desligamento) + +1. **Chave pública instalada** em `/home/mini/.ssh/authorized_keys`: + ``` + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIlXrLNy18IDQcCqJd+NeXIbng4+39FRqi10CVbSPk2D claude-cloud-pizza + ``` +2. `chmod 700 ~/.ssh` · `chmod 600 ~/.ssh/authorized_keys` +3. Projeto continua em `/home/mini/pizza-digital` + +## Pendente (quando religar a VM) + +1. Instalar `cloudflared` na VM125 +2. Criar Tunnel Cloudflare apontando SSH → `ssh-pizza.myvexx.com` +3. DNS CNAME no zone `myvexx.com` +4. Cloudflare Access Application (SSH) + **Service Token** +5. Liberar `ssh-pizza.myvexx.com` (e `*.cloudflareaccess.com` se necessário) na Network access do ambiente Claude +6. Testar: `ssh -o ProxyCommand='cloudflared access ssh --hostname %h' mini@ssh-pizza.myvexx.com` + +## PasswordAuthentication + +| Acesso | Política recomendada | +|--------|----------------------| +| LAN / Tailscale (atual) | Mantém `PasswordAuthentication yes` (ops local com senha `805353`) | +| Via túnel Cloudflare | Só chave (`claude-cloud-pizza`); Access + service token; **não** depender de senha no caminho externo | + +**Não** desligamos `PasswordAuthentication` globalmente nesta pausa (quebraria o hop local). Quando o túnel estiver estável, pode restringir senha só em `Match Address` / ou deixar senha só na LAN. + +## Alternativa (se Cloudflare atrasar) + +Hop público no big1 (`95.216.14.162` ou host público do Proxmox) com `sshd` em **443** + ProxyJump até `10.10.10.125`. Preferência atual do usuário: **Cloudflare Tunnel**. + +--- + +## Credenciais locais (referência ops — não usar no Claude Cloud) + +| Campo | Valor | +|-------|--------| +| Host LAN | `10.10.10.125` | +| User | `mini` | +| Password LAN | `805353` | +| Hop Tailscale | `big1` → `100.116.138.100` | +| Projeto | `~/pizza-digital` | + +--- + +## Checklist pós-boot + +- [ ] VM125 no ar · Docker pizza-digital up +- [ ] `cloudflared` tunnel `ssh-pizza.myvexx.com` healthy +- [ ] Access service token criado e só em env do Claude +- [ ] Domínio liberado na Network access +- [ ] Claude conecta com a chave `claude-cloud-pizza` sem senha diff --git a/Pizza-Digital/docs/SYNC-PIZZA-DIGITAL.md b/Pizza-Digital/docs/SYNC-PIZZA-DIGITAL.md new file mode 100644 index 0000000..cb561d4 --- /dev/null +++ b/Pizza-Digital/docs/SYNC-PIZZA-DIGITAL.md @@ -0,0 +1,40 @@ +# Sync — Pizza Digital ↔ Obsidian + +**Última sync:** 2026-11-09T19:44:42Z +**Direção padrão:** VM125 → Vault (pull) · Vault → VM125 (push) + +## Mapa + +| Origem (VM125) | Destino (CT130 vault) | +|----------------|------------------------| +| `/home/mini/pizza-digital/SPEC.md` | `Pizza-Digital/SPEC.md` | +| `/home/mini/pizza-digital/README.md` | `Pizza-Digital/README.md` | +| `/home/mini/pizza-digital/docs/**` | `Pizza-Digital/docs/**` | + +## Comandos + +No **CT130** (hub): + +```bash +# VM → Vault +/opt/ligbox-spec-hub/scripts/sync-pizza-digital.sh pull + +# Vault → VM +/opt/ligbox-spec-hub/scripts/sync-pizza-digital.sh push +``` + +Na **VM125**: + +```bash +# Vault → VM (atualiza docs locais a partir do Obsidian) +~/pizza-digital/scripts/sync_obsidian.sh pull + +# VM → Vault +~/pizza-digital/scripts/sync_obsidian.sh push +``` + +## Notas + +- SSH: CT130 `root` → `mini@10.10.10.125` (chave `id_ed25519` spec-hub) +- Não sincroniza código (`apps/`, `packages/`, `.env`) — só documentação +- Após pull no vault, fazer commit no repo `obsidian-vault` se quiser versionar diff --git a/Pizza-Digital/docs/data/fichas-pizzas-peso.json b/Pizza-Digital/docs/data/fichas-pizzas-peso.json new file mode 100644 index 0000000..e23d72d --- /dev/null +++ b/Pizza-Digital/docs/data/fichas-pizzas-peso.json @@ -0,0 +1,595 @@ +{ + "version": "0.1.0", + "region": "Joinville/SC", + "unit": "g", + "notes": "Quantização de estoque por peso. Baixa = qty_g × OrderItem.quantity. Meia-a-meia = 0.5 × BOM de cada sabor.", + "bases": { + "grande": { + "sizeCode": "grande", + "diameterCm": 40, + "slices": 12, + "massa_g": 480, + "molho_g": 90, + "mussarela_base_g": 200, + "scaleVsGrande": 1 + }, + "broto": { + "sizeCode": "broto", + "diameterCm": 25, + "slices": 4, + "massa_g": 200, + "molho_g": 40, + "mussarela_base_g": 85, + "scaleVsGrande": 0.42 + } + }, + "ingredientsCatalog": [ + { "sku": "massa-bola", "name": "Massa (bola/disco)", "unit": "g", "minQty": 5000 }, + { "sku": "molho-tomate", "name": "Molho de tomate", "unit": "g", "minQty": 2000 }, + { "sku": "mussarela", "name": "Mussarela", "unit": "g", "minQty": 5000 }, + { "sku": "calabresa", "name": "Calabresa fatiada", "unit": "g", "minQty": 2000 }, + { "sku": "calabresa-moida", "name": "Calabresa moída", "unit": "g", "minQty": 1500 }, + { "sku": "cebola", "name": "Cebola", "unit": "g", "minQty": 1000 }, + { "sku": "frango", "name": "Frango desfiado", "unit": "g", "minQty": 2000 }, + { "sku": "catupiry", "name": "Catupiry / requeijão", "unit": "g", "minQty": 2000 }, + { "sku": "presunto", "name": "Presunto", "unit": "g", "minQty": 2000 }, + { "sku": "ovo", "name": "Ovo", "unit": "un", "minQty": 30 }, + { "sku": "azeitona", "name": "Azeitona", "unit": "g", "minQty": 800 }, + { "sku": "provolone", "name": "Provolone", "unit": "g", "minQty": 1000 }, + { "sku": "gorgonzola", "name": "Gorgonzola", "unit": "g", "minQty": 800 }, + { "sku": "parmesao", "name": "Parmesão", "unit": "g", "minQty": 800 }, + { "sku": "tomate", "name": "Tomate", "unit": "g", "minQty": 1500 }, + { "sku": "manjericao", "name": "Manjericão", "unit": "g", "minQty": 200 }, + { "sku": "bacon", "name": "Bacon", "unit": "g", "minQty": 1500 }, + { "sku": "pepperoni", "name": "Pepperoni", "unit": "g", "minQty": 1500 }, + { "sku": "lombo", "name": "Lombo defumado", "unit": "g", "minQty": 1500 }, + { "sku": "pimenta", "name": "Molho de pimenta", "unit": "g", "minQty": 300 }, + { "sku": "pimentao", "name": "Pimentão", "unit": "g", "minQty": 800 }, + { "sku": "palmito", "name": "Palmito", "unit": "g", "minQty": 1200 }, + { "sku": "brocolis", "name": "Brócolis", "unit": "g", "minQty": 1000 }, + { "sku": "milho", "name": "Milho", "unit": "g", "minQty": 1000 }, + { "sku": "ervilha", "name": "Ervilha", "unit": "g", "minQty": 800 }, + { "sku": "atum", "name": "Atum", "unit": "g", "minQty": 1200 }, + { "sku": "batata-palha", "name": "Batata palha", "unit": "g", "minQty": 800 }, + { "sku": "estrogonofe-frango", "name": "Estrogonofe de frango", "unit": "g", "minQty": 1500 }, + { "sku": "alho", "name": "Alho", "unit": "g", "minQty": 400 }, + { "sku": "linguica-blumenau", "name": "Linguiça Blumenau", "unit": "g", "minQty": 1500 }, + { "sku": "aipim", "name": "Purê de aipim", "unit": "g", "minQty": 2000 }, + { "sku": "coracao-frango", "name": "Coração de frango", "unit": "g", "minQty": 1200 }, + { "sku": "chocolate-preto", "name": "Chocolate preto", "unit": "g", "minQty": 1500 }, + { "sku": "chocolate-branco", "name": "Chocolate branco", "unit": "g", "minQty": 1500 }, + { "sku": "banana", "name": "Banana", "unit": "g", "minQty": 1500 }, + { "sku": "canela", "name": "Canela", "unit": "g", "minQty": 200 }, + { "sku": "leite-condensado", "name": "Leite condensado", "unit": "g", "minQty": 1000 }, + { "sku": "coco", "name": "Coco ralado", "unit": "g", "minQty": 800 }, + { "sku": "goiabada", "name": "Goiabada", "unit": "g", "minQty": 1000 }, + { "sku": "morango", "name": "Morango", "unit": "g", "minQty": 1000 }, + { "sku": "oregano", "name": "Orégano", "unit": "g", "minQty": 300 } + ], + "recipes": [ + { + "menuItemId": "seed-item-calabresa", + "name": "Calabresa G", + "sizeCode": "grande", + "weightTotalApprox_g": 930, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "calabresa", "qty": 120, "unit": "g" }, + { "sku": "cebola", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-mussarela", + "name": "Mussarela G", + "sizeCode": "grande", + "weightTotalApprox_g": 772, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-frango-catupiry", + "name": "Frango Catupiry G", + "sizeCode": "grande", + "weightTotalApprox_g": 992, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "frango", "qty": 150, "unit": "g" }, + { "sku": "catupiry", "qty": 90, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-portuguesa", + "name": "Portuguesa G", + "sizeCode": "grande", + "weightTotalApprox_g": 1012, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "presunto", "qty": 100, "unit": "g" }, + { "sku": "ovo", "qty": 2, "unit": "un" }, + { "sku": "cebola", "qty": 40, "unit": "g" }, + { "sku": "azeitona", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-quatro", + "name": "4 Queijos G", + "sizeCode": "grande", + "weightTotalApprox_g": 912, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 70, "unit": "g" }, + { "sku": "mussarela", "qty": 120, "unit": "g" }, + { "sku": "provolone", "qty": 80, "unit": "g" }, + { "sku": "gorgonzola", "qty": 60, "unit": "g" }, + { "sku": "catupiry", "qty": 100, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-margherita", + "name": "Marguerita G", + "sizeCode": "grande", + "weightTotalApprox_g": 852, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "tomate", "qty": 80, "unit": "g" }, + { "sku": "parmesao", "qty": 20, "unit": "g" }, + { "sku": "manjericao", "qty": 8, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-bacon", + "name": "Bacon G", + "sizeCode": "grande", + "weightTotalApprox_g": 892, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "bacon", "qty": 120, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-pepperoni", + "name": "Pepperoni G", + "sizeCode": "grande", + "weightTotalApprox_g": 922, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "pepperoni", "qty": 120, "unit": "g" }, + { "sku": "pimentao", "qty": 30, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-lombo-catupiry", + "name": "Lombo Catupiry G", + "sizeCode": "grande", + "weightTotalApprox_g": 962, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "lombo", "qty": 120, "unit": "g" }, + { "sku": "catupiry", "qty": 90, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-baiana", + "name": "Baiana G", + "sizeCode": "grande", + "weightTotalApprox_g": 902, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "calabresa-moida", "qty": 130, "unit": "g" }, + { "sku": "pimenta", "qty": 20, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-napolitana", + "name": "Napolitana G", + "sizeCode": "grande", + "weightTotalApprox_g": 842, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "tomate", "qty": 70, "unit": "g" }, + { "sku": "parmesao", "qty": 20, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-presunto", + "name": "Presunto G", + "sizeCode": "grande", + "weightTotalApprox_g": 872, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "presunto", "qty": 100, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-palmito", + "name": "Palmito G", + "sizeCode": "grande", + "weightTotalApprox_g": 892, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "palmito", "qty": 120, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-brocolis", + "name": "Brócolis G", + "sizeCode": "grande", + "weightTotalApprox_g": 942, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "brocolis", "qty": 120, "unit": "g" }, + { "sku": "bacon", "qty": 70, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-toscana", + "name": "Toscana G", + "sizeCode": "grande", + "weightTotalApprox_g": 922, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "calabresa-moida", "qty": 130, "unit": "g" }, + { "sku": "cebola", "qty": 40, "unit": "g" }, + { "sku": "pimenta", "qty": 15, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-moda-casa", + "name": "Moda da Casa G", + "sizeCode": "grande", + "weightTotalApprox_g": 1002, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "presunto", "qty": 80, "unit": "g" }, + { "sku": "tomate", "qty": 50, "unit": "g" }, + { "sku": "cebola", "qty": 30, "unit": "g" }, + { "sku": "milho", "qty": 50, "unit": "g" }, + { "sku": "ervilha", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-atum", + "name": "Atum G", + "sizeCode": "grande", + "weightTotalApprox_g": 932, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "atum", "qty": 140, "unit": "g" }, + { "sku": "cebola", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-crocante", + "name": "Crocante G", + "sizeCode": "grande", + "weightTotalApprox_g": 922, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "bacon", "qty": 100, "unit": "g" }, + { "sku": "batata-palha", "qty": 50, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-estrogonofe", + "name": "Estrogonofe Frango G", + "sizeCode": "grande", + "weightTotalApprox_g": 1002, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 60, "unit": "g" }, + { "sku": "mussarela", "qty": 160, "unit": "g" }, + { "sku": "estrogonofe-frango", "qty": 250, "unit": "g" }, + { "sku": "batata-palha", "qty": 50, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-item-vegetariana", + "name": "Vegetariana G", + "sizeCode": "grande", + "weightTotalApprox_g": 972, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "brocolis", "qty": 70, "unit": "g" }, + { "sku": "palmito", "qty": 60, "unit": "g" }, + { "sku": "milho", "qty": 50, "unit": "g" }, + { "sku": "ervilha", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-broto-calabresa", + "name": "Calabresa Broto", + "sizeCode": "broto", + "weightTotalApprox_g": 392, + "lines": [ + { "sku": "massa-bola", "qty": 200, "unit": "g" }, + { "sku": "molho-tomate", "qty": 40, "unit": "g" }, + { "sku": "mussarela", "qty": 85, "unit": "g" }, + { "sku": "calabresa", "qty": 50, "unit": "g" }, + { "sku": "cebola", "qty": 15, "unit": "g" }, + { "sku": "oregano", "qty": 1, "unit": "g" } + ] + }, + { + "menuItemId": "seed-broto-mussarela", + "name": "Mussarela Broto", + "sizeCode": "broto", + "weightTotalApprox_g": 326, + "lines": [ + { "sku": "massa-bola", "qty": 200, "unit": "g" }, + { "sku": "molho-tomate", "qty": 40, "unit": "g" }, + { "sku": "mussarela", "qty": 85, "unit": "g" }, + { "sku": "oregano", "qty": 1, "unit": "g" } + ] + }, + { + "menuItemId": "seed-broto-frango", + "name": "Frango Catupiry Broto", + "sizeCode": "broto", + "weightTotalApprox_g": 416, + "lines": [ + { "sku": "massa-bola", "qty": 200, "unit": "g" }, + { "sku": "molho-tomate", "qty": 40, "unit": "g" }, + { "sku": "mussarela", "qty": 75, "unit": "g" }, + { "sku": "frango", "qty": 60, "unit": "g" }, + { "sku": "catupiry", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 1, "unit": "g" } + ] + }, + { + "menuItemId": "seed-broto-portuguesa", + "name": "Portuguesa Broto", + "sizeCode": "broto", + "weightTotalApprox_g": 426, + "lines": [ + { "sku": "massa-bola", "qty": 200, "unit": "g" }, + { "sku": "molho-tomate", "qty": 40, "unit": "g" }, + { "sku": "mussarela", "qty": 75, "unit": "g" }, + { "sku": "presunto", "qty": 45, "unit": "g" }, + { "sku": "ovo", "qty": 1, "unit": "un" }, + { "sku": "cebola", "qty": 15, "unit": "g" }, + { "sku": "azeitona", "qty": 15, "unit": "g" }, + { "sku": "oregano", "qty": 1, "unit": "g" } + ] + }, + { + "menuItemId": "seed-broto-4queijos", + "name": "4 Queijos Broto", + "sizeCode": "broto", + "weightTotalApprox_g": 386, + "lines": [ + { "sku": "massa-bola", "qty": 200, "unit": "g" }, + { "sku": "molho-tomate", "qty": 30, "unit": "g" }, + { "sku": "mussarela", "qty": 50, "unit": "g" }, + { "sku": "provolone", "qty": 35, "unit": "g" }, + { "sku": "gorgonzola", "qty": 25, "unit": "g" }, + { "sku": "catupiry", "qty": 45, "unit": "g" }, + { "sku": "oregano", "qty": 1, "unit": "g" } + ] + }, + { + "menuItemId": "seed-esp-joinville", + "name": "Joinville G", + "sizeCode": "grande", + "weightTotalApprox_g": 852, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 200, "unit": "g" }, + { "sku": "alho", "qty": 20, "unit": "g" }, + { "sku": "parmesao", "qty": 30, "unit": "g" }, + { "sku": "pimentao", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-esp-blumenau", + "name": "Linguiça Blumenau G", + "sizeCode": "grande", + "weightTotalApprox_g": 982, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "linguica-blumenau", "qty": 140, "unit": "g" }, + { "sku": "catupiry", "qty": 90, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-esp-aipim-bacon", + "name": "Aipim com Bacon G", + "sizeCode": "grande", + "weightTotalApprox_g": 1012, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 60, "unit": "g" }, + { "sku": "mussarela", "qty": 150, "unit": "g" }, + { "sku": "aipim", "qty": 200, "unit": "g" }, + { "sku": "bacon", "qty": 120, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-esp-catarina", + "name": "Catarina G", + "sizeCode": "grande", + "weightTotalApprox_g": 942, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "calabresa-moida", "qty": 120, "unit": "g" }, + { "sku": "ovo", "qty": 2, "unit": "un" }, + { "sku": "cebola", "qty": 40, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-esp-aipim-blumenau", + "name": "Aipim Blumenau G", + "sizeCode": "grande", + "weightTotalApprox_g": 1052, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 60, "unit": "g" }, + { "sku": "mussarela", "qty": 150, "unit": "g" }, + { "sku": "aipim", "qty": 200, "unit": "g" }, + { "sku": "linguica-blumenau", "qty": 140, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-esp-coracao", + "name": "Coração G", + "sizeCode": "grande", + "weightTotalApprox_g": 972, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "molho-tomate", "qty": 90, "unit": "g" }, + { "sku": "mussarela", "qty": 180, "unit": "g" }, + { "sku": "coracao-frango", "qty": 140, "unit": "g" }, + { "sku": "catupiry", "qty": 80, "unit": "g" }, + { "sku": "oregano", "qty": 2, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-choc-preto", + "name": "Chocolate Preto G", + "sizeCode": "grande", + "weightTotalApprox_g": 780, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "mussarela", "qty": 100, "unit": "g" }, + { "sku": "chocolate-preto", "qty": 200, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-choc-branco", + "name": "Chocolate Branco G", + "sizeCode": "grande", + "weightTotalApprox_g": 780, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "mussarela", "qty": 100, "unit": "g" }, + { "sku": "chocolate-branco", "qty": 200, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-banana", + "name": "Banana Canela G", + "sizeCode": "grande", + "weightTotalApprox_g": 860, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "mussarela", "qty": 80, "unit": "g" }, + { "sku": "banana", "qty": 200, "unit": "g" }, + { "sku": "canela", "qty": 5, "unit": "g" }, + { "sku": "leite-condensado", "qty": 95, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-prestigio", + "name": "Prestígio G", + "sizeCode": "grande", + "weightTotalApprox_g": 840, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "mussarela", "qty": 80, "unit": "g" }, + { "sku": "chocolate-preto", "qty": 180, "unit": "g" }, + { "sku": "coco", "qty": 100, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-romeu", + "name": "Romeu e Julieta G", + "sizeCode": "grande", + "weightTotalApprox_g": 810, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "mussarela", "qty": 120, "unit": "g" }, + { "sku": "goiabada", "qty": 150, "unit": "g" }, + { "sku": "provolone", "qty": 60, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-morango", + "name": "Morango Chocolate G", + "sizeCode": "grande", + "weightTotalApprox_g": 860, + "lines": [ + { "sku": "massa-bola", "qty": 480, "unit": "g" }, + { "sku": "mussarela", "qty": 80, "unit": "g" }, + { "sku": "morango", "qty": 150, "unit": "g" }, + { "sku": "chocolate-preto", "qty": 150, "unit": "g" } + ] + }, + { + "menuItemId": "seed-doce-broto-choc", + "name": "Chocolate Broto", + "sizeCode": "broto", + "weightTotalApprox_g": 325, + "lines": [ + { "sku": "massa-bola", "qty": 200, "unit": "g" }, + { "sku": "mussarela", "qty": 40, "unit": "g" }, + { "sku": "chocolate-preto", "qty": 85, "unit": "g" } + ] + } + ] +} diff --git a/Pizza-Digital/scripts/sync_obsidian.sh b/Pizza-Digital/scripts/sync_obsidian.sh new file mode 100755 index 0000000..e19b9de --- /dev/null +++ b/Pizza-Digital/scripts/sync_obsidian.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Pizza Digital — sync docs com Obsidian (CT130) +# Requer: chave SSH de mini → root@10.10.10.130 OU execute no CT130: +# /opt/ligbox-spec-hub/scripts/sync-pizza-digital.sh {pull|push} +set -euo pipefail +MODE="${1:-}" +CT130="root@10.10.10.130" +REMOTE_SCRIPT="/opt/ligbox-spec-hub/scripts/sync-pizza-digital.sh" +SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new" + +if [[ -z "$MODE" ]]; then + echo "Usage: $0 {pull|push}" + echo " pull = Vault → VM (atualiza docs locais)" + echo " push = VM → Vault" + exit 1 +fi + +# Map VM local names to CT130 script directions +case "$MODE" in + pull) REMOTE_MODE=push ;; # vault→vm is CT130 push + push) REMOTE_MODE=pull ;; # vm→vault is CT130 pull + *) echo "Usage: $0 {pull|push}"; exit 1 ;; +esac + +if ssh $SSH_OPTS "$CT130" "test -x $REMOTE_SCRIPT" 2>/dev/null; then + ssh $SSH_OPTS "$CT130" "$REMOTE_SCRIPT $REMOTE_MODE" +else + echo "Sem SSH mini→CT130. Rode no CT130:" + echo " $REMOTE_SCRIPT $REMOTE_MODE" + exit 2 +fi diff --git a/_sidebar.md b/_sidebar.md index 6e72b80..572bcfd 100644 --- a/_sidebar.md +++ b/_sidebar.md @@ -12,6 +12,7 @@ - [VM500 PROJETOS](ligbox-ops-platform/docs/vms/VM500.md) - [SPEC VM500 Rent Charge](Infra-Network/SPEC-VM500-RENTCHARGE.md) - [Rentcharge Cloudflare Tunnel](Infra-Network/RENTCHARGE_CLOUDFLARE_TUNNEL.md) +- [Rently — Guias rápidos (PDF)](Infra-Network/guides/README.md) - [Cursor Tapo Tuya MCP](Infra-Network/CURSOR_TAPO_TUYA_MCP.md) --- ## Casa Dublin (energia) @@ -19,6 +20,18 @@ - [Boiler PROJECT](Boiler-Tuya-Energy/PROJECT.md) - [R630 Tapo dashboard](https://r630.myvexx.com/) --- +## Pizza Digital (VM125) +- [Hub Pizza](Pizza-Digital/Pizza-Digital.md) +- [SPEC mãe](Pizza-Digital/SPEC.md) +- [SPEC Fase 1](Pizza-Digital/docs/SPEC-FASE1.md) +- [Canais digitais](Pizza-Digital/docs/SPEC-CANAIS-DIGITAIS.md) +- [Cardápio](Pizza-Digital/docs/SPEC-CARDAPIO.md) +- [Estoque](Pizza-Digital/docs/SPEC-ESTOQUE.md) +- [Matriz acesso](Pizza-Digital/docs/SPEC-MATRIZ-ACESSO.md) +- [KDS Claim](Pizza-Digital/docs/SPEC-KDS-CLAIM.md) +- [Despacho CityFoods](Pizza-Digital/docs/SPEC-DESPACHO-CITYFOODS-PARITY.md) +- [Sync Pizza](Pizza-Digital/SYNC-PIZZA-DIGITAL.md) +--- ## MyVexx / Invest - [Invest hub](Invest-MyVexx/Invest-MyVexx.md) - [SPEC-050 AI Agents](Invest-MyVexx/SPEC-050-INVEST-AI-AGENTS-PAPER.md)