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:
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 ORSroute_polyline— geometria da rotabase_fare,price_per_km,min_km— snapshot da config vigentedriver_earning— calculado pela fórmula de pricingclient_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
/assignganha.
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: