Integração backend da fila de negativação

TLDR: job diário popula registry_negativations com debits elegíveis; GET endpoint lista o registry; frontend conectado ao backend sem mocks.

Contexto

O frontend da fila de negativação (branch feat/negativation) está completo visualmente mas 100% mockado. No backend, RegistryNegativation foi criado mas não é populado por nenhum processo ainda.

A regra de negócio está em .project/docs/rules/collections/credit_bureau_listing.md (R-003).

Objetivos

  • Criar NegativationQueueJob (ActiveJob) que roda diariamente às 01:00 e popula registry_negativations com os debits elegíveis
  • Configurar solid_queue para scheduling via config/recurring.yml
  • Refatorar GET /api/v1/negativation/queue para ler de registry_negativations (não derivar em tempo real)
  • Criar seed de integração cobrindo os 3 estados do frontend
  • Substituir os mocks do frontend por chamadas reais ao backend

Fora de escopo

  • Chamada real à API do Asaas/Serasa — gateway_ref fica nullable
  • Endpoints de execução manual (POST) e remoção (DELETE)
  • Webhook de reconciliação pós-pagamento (RN-NEG-5)
  • Exceção do 2º reparcelamento (RN-NEG-2)

Arquitetura

``` [01:00 AM] NegativationQueueJob ↓ avalia elegibilidade (RN-NEG-1) ↓ cria/atualiza RegistryNegativation por debit elegível ↓ status: pending

GET /api/v1/negativation/queue ↓ lê registry_negativations com status pending|negative|closed ↓ serializa uma linha por product_debit → frontend ```

Regras de negócio aplicadas

Da R-003:

  • RN-NEG-1: elegível = product_debit.consumer_progress > 25 E installments overdue/defaulted >= 3 no mesmo debit

Status do RegistryNegativation:

status significado
pending elegível, ainda não executado no Asaas
negative negativado no Asaas/Serasa
closed removido do bureau (dívida paga)
cancelled processo cancelado antes da execução

Status derivado para o frontend:

registry_negativation.status campo status no payload
pending pending
negative + installments overdue negativated
negative + sem installments overdue eligible_for_removal

Agregação por cliente: installments e amount somam todas as parcelas overdue do cliente. Um cliente com 2 cursos aparece em 2 linhas com os mesmos totais.

Mudanças

modules/backend/Gemfile (modificar)

Adicionar gem "solid_queue".

modules/backend/config/recurring.yml (novo)

yaml negativation_queue_job: class: NegativationQueueJob schedule: every day at 1am

modules/backend/app/jobs/negativation_queue_job.rb (novo)

Job que avalia elegibilidade e cria/atualiza registry_negativations: - Busca debits com consumer_progress > 25 e >= 3 installments overdue/defaulted - Para cada debit elegível, faz find_or_create_by!(debit: debit) no registry com status: :pending - Debits que saíram da elegibilidade não são removidos do registry (auditoria)

modules/backend/app/controllers/api/v1/negativation_controller.rb (modificar)

Substituir Negativation::FetchQueue.call por query direta em RegistryNegativation:

ruby RegistryNegativation .where.not(status: :cancelled) .includes(debit: [:customer, :installments, :product_debits, :user])

modules/backend/app/use_cases/negativation/fetch_queue.rb (deletar)

Lógica migrada para o job. Endpoint passa a ler do registry.

modules/backend/db/seeds/negativation.rb (novo)

Seed com dados cobrindo os 3 estados — cria debits + RegistryNegativation correspondentes.

modules/frontend/src/services/negativation.ts (modificar)

Substituir mock por apiFetch('/api/v1/negativation/queue'). ✅ já feito.

modules/frontend/src/mocks/creditBlacklist.ts (deletar)

Não será mais usado.

Como verificar

  1. NegativationQueueJob.perform_now popula registry_negativations
  2. GET /api/v1/negativation/queue retorna a fila lendo do registry
  3. Cliente com consumer_progress <= 25 não aparece
  4. Cliente com < 3 installments overdue não aparece
  5. Cliente com 2 cursos aparece em 2 linhas com os mesmos totais
  6. Frontend exibe dados reais sem imports de mocks

Documentação

Nenhuma mudança necessária — R-003 já documenta as regras aplicadas.