obsidian-vault/Pizza-Digital/docs/SPEC-DESPACHO-CITYFOODS-PARITY.md
Roger 62a20a2573 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.
2026-11-09 19:45:11 +00:00

397 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.*