# Etapa 7/8 — Motor de Alertas (BullMQ) ## Contexto do projeto Continuação do **SeConectai Vendas**. Já existem: setup, auth com RLS, painel master completo, integrações Evolution API/e-mail com serviço central de envio (`AlertDispatchService`), CRUD de customers/produtos, e o módulo completo de vendas/parcelas/dashboards. Esta é a etapa que **automatiza** o que até agora só existia como endpoint manual: verificação de vencimentos e disparo de alertas, nos dois níveis do sistema (master→tenant e tenant→customer). ## Tarefas 1. **Infraestrutura de filas** - Configurar `@nestjs/bullmq` conectado ao Redis já disponível no `docker-compose`. - Criar duas filas: `alertas-mensalidade` (master→tenant) e `alertas-cobranca` (tenant→customer). - Criar um processo/worker separado do processo web principal (pode ser o mesmo container em dev, mas documentar como se separaria em produção — ex: `main.ts` vs `worker.ts`, ou um segundo serviço no `docker-compose` chamado `worker` rodando o mesmo build com um comando diferente). 2. **Módulo `alert-templates`** - CRUD de `alert_templates`: - Nível master (`MasterGuard`): templates padrão da plataforma (`tenant_id = null`), tipo `mensalidade_tenant`. - Nível tenant (`TenantGuard`, role `tenant_admin`): templates próprios, tipo `cobranca_cliente` (e outros tipos que fizerem sentido, ex: `venda_confirmada`, `lembrete_vencimento`). Se o tenant não tiver template próprio para um tipo/canal, usar o template padrão do master como fallback. - Implementar renderização de placeholders (`{{nome}}`, `{{valor}}`, `{{vencimento}}`, etc — listar todos os placeholders suportados por tipo de template em um arquivo de constantes, documentado). 3. **Job diário: `mensalidade_tenant`** - Cron (ex: todo dia às 8h, horário configurável via env) que: 1. Roda a lógica equivalente ao endpoint `POST /master/faturas/atualizar-atrasadas` (marca faturas vencidas como `atrasado`, tenant vira `inadimplente`). 2. Para toda `tenant_faturas` com vencimento em N dias (configurável, ex: 3 dias antes) ou já vencida e ainda sem alerta hoje, enfileira um job na fila `alertas-mensalidade` com os dados necessários. - Worker da fila `alertas-mensalidade`: busca template (`mensalidade_tenant`, canal `whatsapp` e/ou `email` conforme configurado), renderiza, chama `AlertDispatchService`, grava resultado em `alert_logs`, atualiza `tenant_faturas.alerta_enviado_em`. 4. **Job diário: `cobranca_cliente`** (por tenant) - Mesmo padrão do job acima, mas iterando `sale_payments` de todos os tenants ativos: marca atrasadas, enfileira alerta para parcelas próximas do vencimento ou já atrasadas (respeitando `alerta_enviado_em` para não notificar duas vezes no mesmo dia). - Usa a instância WhatsApp e config de e-mail **do tenant dono da venda**, não a do master. 5. **Idempotência e resiliência** - Garantir que um mesmo job não dispare o mesmo alerta duas vezes (checar `alerta_enviado_em` antes de enfileirar, e novamente antes de enviar, dentro do worker, para lidar com concorrência). - Configurar retry com backoff exponencial no BullMQ para falhas transitórias de envio (ex: Evolution API fora do ar), com limite de tentativas (ex: 3) e log do erro final em `alert_logs.erro`. - Endpoint de observabilidade: `GET /master/alertas/logs` e `GET /tenant/alertas/logs` para consultar `alert_logs` com filtros (canal, status, período). ## Critérios de aceite - [ ] Worker processa as duas filas de forma independente do processo web (documentado como escalar workers separadamente) - [ ] Template do tenant sobrepõe o padrão do master quando existe; usa fallback quando não existe - [ ] Placeholders são substituídos corretamente na renderização - [ ] Job diário marca faturas/parcelas atrasadas corretamente - [ ] Alerta de mensalidade chega no WhatsApp/e-mail do tenant em teste controlado (vencimento simulado para "hoje") - [ ] Alerta de cobrança chega no WhatsApp/e-mail do customer, usando a instância/config do tenant correto (testar com 2 tenants diferentes) - [ ] Mesmo alerta não é disparado duas vezes no mesmo dia (testar rodando o job duas vezes seguidas) - [ ] Falha de envio é reprocessada com retry e, após esgotar tentativas, fica registrada em `alert_logs` com erro - [ ] Endpoints de consulta de logs funcionam com os filtros esperados