## v31.4 (2026-07-11)
### Correções
- **export-csv**: corrigido AttributeError `call_group` → `callgroup` (model column sem underscore)
- **import-csv**: mesmo erro corrigido nas linhas `call_group=` e `pickup_group=`
- **Engine**: IP público hardcoded `144.31.32.19` removido de 4 locais nos transports UDP/TCP — atualizado para `144.31.32.50`
- **QR Code** (main.py + notification.py): IP atualizado nos dados do QR (`144.31.32.50`)
- **WireGuard** (wg-juliano.conf): endpoint atualizado
- **WhatsApp SIP** (build_whatsapp_pjsip): `external_signaling_address` e `external_media_address` agora inseridos na seção de TRANSPORT (não no endpoint) — string replace na saída final após `local_net=10.1.23.0/24`
- **Startup hook**: `main.py` agora regenera configs do Asterisk na inicialização (`@app.on_event("startup")`)
- **pyclean**: função adicionada no `/root/.bashrc` do servidor

## v31.3.1 (2026-07-11) — WhatsApp SIP external addresses
- **Engine** (`build_whatsapp_pjsip`): auto-detecção de IP público via DNS (`socket.gethostbyname`), insere `external_signaling_address` e `external_media_address` no transport `[whatsapp-tls]`
- Startup hook valida na produção

## v31 (2026-06-30)
- CDR: coluna Uniq.ID adicionada (tabela + CSV export)
- Gravações: coluna Uniq.ID adicionada
- Monitor: latência (RTT) para ramais online
- Monitor: "Queda: X min atrás" para ramais offline (via AstDB)
- Monitor: countdown auto-refresh corrigido (race condition)
- Monitor: status normalization corrigida ('Online' incluso)
- Monitor: RTT "nan" → "---"
- ChanSpy: dialplan corrigido (DB_GET/PUT/DELETE, Gosub, GotoIf)
- ChanSpy: beep + auto-conexão por spy rules
- qualify_frequency=60 adicionado ao AOR no pjsip.conf (default para ramais)
- Engine: `is False` em vez de `not` para campos enabled
- Timestamp ISO 8601 no AstDB (sem espaços)


## v31.1 (2026-07-02 19:25)
### Painel de Monitoramento — novos status
- **Backend (pbx_router.py)**: `/monitor/data` agora retorna `dnd` e `busy` por ramal
  - DND consultado via `database show DEVICE/DND` (ativado com *75/*76/*74)
  - Ocupado detectado via `core show channels`
- **Frontend (monitor.html)**: cards mostram ícones e cores:
  - 🔇 Laranja + legenda "Não perturbe" quando DND ativo
  - 📞 Azul + legenda "Em chamada" quando ramal ocupado
  - Latência fica oculta se ramal estiver em DND ou ocupado
- **Instalador**: Extraído da produção em 19:25, api/ e tarball sincronizados

## v31.2 (2026-07-02 19:45)
### Sistema de Update Remoto
- **Novo endpoint**: `POST /app/system/update` — trigger remoto via token de segurança
  - Auth via header `X-Update-Token`
  - Aceita `{"version": "latest"}` ou `{"version": "v31.2"}`
  - Retorna progresso e status do update
- **Novo endpoint**: `GET /app/system/version` — consulta versão atual e status dos serviços
- **Novo script**: `tools/update.sh` — mecanismo completo de update
  - Backup automático do `api/`, `.env` e token
  - Preserva configurações do cliente (`.env` e `.update_token`)
  - Checksum SHA-256 do pacote
  - Rollback via backups mantidos (últimos 5)
  - Hooks pré/pós-update customizáveis em `custom/`
  - Retorna JSON para integração com API
- **VERSION**: arquivo de version tracking em `/opt/sisnetcall/api/VERSION`
- **Integração no instalador**: `tools/` e `VERSION` copiados durante instalação

## v31.3 (2026-07-08)
- WhatsApp Calling: inbound route via web interface → `from-whatsapp` redireciona para `from-trunk` (DID matching)
- WhatsApp Calling: engine gera contexto `[from-whatsapp]` automaticamente com `Goto(from-trunk,${EXTEN:1},1)`
- WhatsApp Calling: `build_whatsapp_pjsip()` corrigido — `identify_by=auth_username,ip`, 12 ranges CIDR Meta/AS32934, `local_net` adicionados
- Corrigido `_do_write()` em `pbx_router.py`: agora passa `time_groups`, `time_conditions` e `announcements` para `write_configs()`
- Corrigido áudio acelerado (Minnie Mouse): arquivos `.sln16` com sample rate errado (8kHz como 16kHz) removidos do sounds/
- IAX2: `build_trunk_dial_contexts()` agora aceita `iax_trunks` e gera `Dial(IAX2/...)` corretamente
- IAX2: `build_outbound_routes_context()` detecta tipo do tronco e gera `PJSIP/` ou `IAX2/` conforme necessário
- IAX2: formulários de rota de saída (novo/editar) agora incluem troncos IAX2 na seleção
- IAX2: formato Dial corrigido para IAX2/nome/extensao no trunk-call context
