Skip to content

Fluxo de Entregas

Documentação completa dos estados de entrega, regras de transição e comportamentos de negócio.


Máquina de Estados

                    ┌─────────┐
                    │ pending │ ← Admin cria entrega
                    └────┬────┘
                         │ Driver aceita
                         │ PATCH /assign
                    ┌──────────┐
                    │ assigned │ ← Motorista indo buscar
                    └────┬─────┘
                         │ Driver coletou
                         │ PATCH /start
                   ┌─────────────┐
                   │ in_progress │ ← Motorista entregando
                   └──────┬──────┘
                          │ Driver entregou
                          │ PATCH /complete
                    ┌───────────┐
                    │ completed │ ← Ganhos creditados
                    └───────────┘

    Admin pode cancelar em qualquer estado (exceto completed/cancelled):
    pending/assigned/in_progress → cancelled

Transições Válidas

Implementadas em backend/app/schemas/delivery.py:

VALID_TRANSITIONS: dict[str, list[str]] = {
    "pending":     ["assigned", "cancelled"],
    "assigned":    ["in_progress", "cancelled"],
    "in_progress": ["completed", "cancelled"],
    "completed":   [],          # estado terminal — imutável
    "cancelled":   [],          # estado terminal — imutável
}

Qualquer tentativa de transição inválida retorna 422 Unprocessable Entity:

{
  "detail": "Transição inválida: completed → assigned"
}


Estados em Detalhe

pending — Aguardando Motorista

  • Criado por: Admin via POST /api/v1/deliveries
  • Quem pode ver: Admin (todas) + Drivers (todas as pending)
  • Campos preenchidos na criação:
  • distance_km — calculado pelo ORS
  • route_polyline — geometria da rota
  • base_fare, price_per_km, min_km — snapshot da config vigente
  • driver_earning — calculado pela fórmula de pricing
  • client_value, company_profit — opcional (margem da empresa)
  • wait_since — timestamp de criação (para medir tempo de espera)
  • Visibilidade: Todos os drivers online veem as entregas pending
  • Comportamento especial: Campo wait_since é usado para ordenar pela fila de espera

assigned — Motorista Aceito

  • Transição feita por: Driver via PATCH /assign
  • Campos atualizados:
  • status = 'assigned'
  • driver_id = <driver_uuid> (JWT sub)
  • assigned_at = now()
  • Quem pode ver: Admin + o Driver responsável
  • Regra importante: Apenas um driver por entrega. Primeiro a chamar /assign ganha.

in_progress — Em Rota de Entrega

  • Transição feita por: Driver via PATCH /start
  • Campos atualizados:
  • status = 'in_progress'
  • started_at = now()
  • Significado: Driver coletou o pacote e está a caminho do destino
  • GPS: Driver continua enviando localização via PUT /locations/me

completed — Entrega Finalizada

  • Transição feita por: Driver via PATCH /complete
  • Campos atualizados:
  • status = 'completed'
  • completed_at = now()
  • Ações automáticas disparadas:
async def _credit_driver_earning(delivery: DeliveryOut) -> None:
    # 1. Insere transação de ganho
    supabase.table("transactions").insert({
        "driver_id": delivery.driver_id,
        "delivery_id": delivery.id,
        "type": "earning",
        "direction": "credit",
        "status": "completed",
        "amount": str(delivery.driver_earning),
        "description": f"Ganho entrega #{delivery.id[:8]}",
    }).execute()

    # 2. Incrementa saldo via RPC PostgreSQL (atômico)
    supabase.rpc(
        "increment_wallet_balance",
        {"p_driver_id": delivery.driver_id, "p_amount": str(delivery.driver_earning)},
    ).execute()
  • Estado terminal: Não pode ser revertido ou cancelado

cancelled — Cancelada

  • Transição feita por: Admin via PATCH /cancel
  • Válido a partir de: pending, assigned, in_progress
  • Campos atualizados: status = 'cancelled'
  • Sem ações financeiras: Nenhum crédito ou débito é gerado
  • Estado terminal: Não pode ser revertido

Regras de Negócio

Quem pode fazer qual transição

Transição Quem Endpoint
pending → assigned Driver PATCH /assign
assigned → in_progress Driver PATCH /start
in_progress → completed Driver PATCH /complete
* → cancelled Admin PATCH /cancel

Isolamento da entrega

Quando uma entrega sai do estado pending, ela deixa de ser visível para outros drivers (RLS policy):

CREATE POLICY "driver_read_deliveries" ON public.deliveries FOR SELECT
  USING (status = 'pending' OR driver_id = auth.uid());

Snapshot de preços

O preço (base_fare, price_per_km, min_km) é capturado no momento da criação. Mudanças posteriores na pricing_config não afetam entregas existentes.

Timestamps de rastreamento

Campo Quando é preenchido
created_at Criação da entrega
wait_since Criação (igual ao created_at)
assigned_at pending → assigned
started_at assigned → in_progress
completed_at in_progress → completed

Exemplo de Ciclo Completo

# 1. Admin cria entrega
curl -X POST http://localhost:8000/api/v1/deliveries \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -d '{"title": "Teste", "origin_address": "...", ...}'
# → status: "pending", id: "abc-123"

# 2. Driver aceita
curl -X PATCH http://localhost:8000/api/v1/deliveries/abc-123/assign \
  -H "Authorization: Bearer $DRIVER_TOKEN"
# → status: "assigned", driver_id: "driver-uuid"

# 3. Driver coletou o pacote
curl -X PATCH http://localhost:8000/api/v1/deliveries/abc-123/start \
  -H "Authorization: Bearer $DRIVER_TOKEN"
# → status: "in_progress", started_at: "2026-05-18T10:05:00Z"

# 4. Driver entregou
curl -X PATCH http://localhost:8000/api/v1/deliveries/abc-123/complete \
  -H "Authorization: Bearer $DRIVER_TOKEN"
# → status: "completed", completed_at: "2026-05-18T10:35:00Z"
# → ganhos creditados automaticamente na carteira do driver

Métricas de Performance

Para calcular o tempo de cada etapa:

SELECT
  id,
  title,
  assigned_at - wait_since AS tempo_espera,
  started_at - assigned_at AS tempo_coleta,
  completed_at - started_at AS tempo_entrega,
  completed_at - wait_since AS tempo_total
FROM deliveries
WHERE status = 'completed'
ORDER BY completed_at DESC;