# PRD — Central Logística: Broadcast dos toggles de status (Fase 2)

> Continuação de `00-prd.md`. A Fase 1 cobriu confirmado/route_delete/reset-alterada/markers.
> Esta fase fecha o buraco dos **botões de status do card do romaneio**.

## Contexto

Após a Fase 1, o handler do frontend (`CentralSocketHandler.handleRouteUpdate`) já sabe aplicar
todos os campos de status de uma rota (`separado`, `carregado`, `faturamento`, `confirmado`,
`veiculo_id`, `descricao_romaneio`). A infraestrutura de recepção está pronta nas duas pontas.

Porém, as ações que **mudam** esses campos — os botões de liberar/cancelar separação,
carregamento, faturamento, confirmar romaneio, vincular motorista e editar descrição — **não
emitem socket nenhum**. Resultado: o operador que clica vê a mudança; qualquer outro operador
no mesmo painel continua com o estado antigo até dar F5.

Isso é incoerente com o resto do módulo: o par `desvincularMotoristaRota` já emite, mas o
`vincularMotoristaRota` não. Os fluxos de criação/remoção de rota já emitem; os toggles de
status não.

## Problema

10 ações mutam estado de romaneio sem notificar os demais clientes conectados ao canal
`galaxia_logistica_central`:

| Ação | Campo alterado | Front já sincroniza | Tipo |
|---|---|---|---|
| `liberaSeparacao` | `separado=1` | ✅ | route_update |
| `cancelaSeparacao` | `separado=0` | ✅ | route_update |
| `liberaCarregamento` | `carregado=1` | ✅ | route_update |
| `cancelaCarregamento` | `carregado=0` | ✅ | route_update |
| `liberarFaturamento` | `faturamento=1` | ✅ | route_update |
| `cancelarFaturamento` | `faturamento=0` | ✅ | route_update |
| `confirmarRomaneio` | `confirmado=1` | ✅ | route_update |
| `vincularMotoristaRota` | `veiculo_id` | ✅ | route_update |
| `atualizarDescricaoRomaneio` | `descricao_romaneio` | ✅ | route_update |
| `finalizarRomaneio` | sai do board ativo | ✅ | route_delete |

## Objetivo

Cada uma das 10 ações, ao persistir com sucesso, emite o evento correspondente para
`galaxia_logistica_central` (e Board), para que **nenhum operador precise recarregar** a
página para ver mudança de status feita por outro.

## Escopo / Não-escopo

- **Em escopo:** apenas adicionar o broadcast nas 10 ações acima. Sem código novo no frontend
  — o handler já existe.
- **Fora de escopo:** P4 markers já entregue na Fase 1; métodos auxiliares internos
  (`novoVeiculo`, `novoPedido`, `atualizarVeiculo`) que rodam dentro de fluxos que já emitem;
  qualquer leitura (não muta estado); lock otimista / resolução de conflito concorrente.

## Decisões técnicas

1. **Dois helpers privados** em `Central.php` para não repetir 10x o mesmo bloco:
   - `broadcastStatusRota($rotaId, array $campos)` → monta payload parcial e chama
     `broadcastRouteUpdates([$payload], false)` (normalizar=false: não sobrescreve outros
     campos com defaults).
   - `broadcastRotaRemovida($rotaId)` → emite `route_delete` para Board + Central
     (mesmo formato de `excluirRotaVazia`).

2. **Payload parcial keyed por `rota_id`.** O handler do front casa por `rota_id` e só aplica
   os campos presentes (`!== undefined`). Por isso cada toggle envia só `{rota_id, <campo>,
   alterada:true}` — o `alterada` é metadado para o Board (o handler central o ignora).

3. **`rota_id` no `columns_required`.** As 8 ações de status leem o romaneio por `romaneio_id`
   com colunas enxutas que **não** trazem `rota_id`. Cada read ganha `rota_id` nas colunas para
   poder emitir keyed corretamente. `vincularMotoristaRota` já tem `rota_id` no input.
   `atualizarDescricaoRomaneio`: se `rota_id` não veio no input, busca no romaneio.

4. **Broadcast após o guard de persistência.** O emit fica logo após o `update` ter sucesso —
   antes da criação de missão (que pode falhar com warning mas não desfaz o status). Assim o
   caminho de warning também propaga o status já persistido.

## Acceptance Criteria

- [ ] AC1: liberar/cancelar separação reflete em outro painel sem F5 (`separado` 0↔1)
- [ ] AC2: liberar/cancelar carregamento idem (`carregado` 0↔1)
- [ ] AC3: liberar/cancelar faturamento idem (`faturamento` 0↔1)
- [ ] AC4: confirmar romaneio reflete `confirmado=1` em outro painel sem F5
- [ ] AC5: vincular motorista reflete `veiculo_id` em outro painel (par com desvincular)
- [ ] AC6: editar descrição reflete em outro painel sem F5
- [ ] AC7: finalizar romaneio remove a rota do painel dos demais (route_delete)
- [ ] AC8: nenhuma mudança no frontend é necessária (apenas backend)
- [ ] AC9: clientes que não têm a rota no `rotasGeradas` (filtro/dia diferente) ignoram o
        evento sem erro
