Melhorar a interface de invoices no Avo (parte 3 de 4)
TLDR: Corrige os badges de status (que apontavam para valores inexistentes no enum), expõe as informações de retry na index e no show, e torna as actions condicionais ao estado real da invoice.
Sequência: parte 3 de uma iniciativa de 4 specs escritas entre 27/01 e 03/02/2026 — parte 4: métricas mensais.
Contexto
Badges de status incorretos
O resource apontava para símbolos que não existiam no enum:
ruby
field :status, as: :badge, options: {
info: [:scheduled, ...], # :scheduled não existe — status 0 é :created
danger: [:canceled, ...], # único correto
success: [:paid, ...], # :paid não existe — status 2 é :done
warning: [:in_analysis, ...] # :in_analysis não existe
}
Faltavam badges para created, pending, failed, blocked e in_bank_processing.
Outras lacunas
- Index sem campos-chave — sem
created_at, sem tempo desde a criação, sem número de tentativas de retry, sem status de retry. - Show sem os campos de retry —
retry_count,last_retry_at,last_error_messagee a próxima tentativa calculada não apareciam. - Transfers sem indicação de duplicata — o campo
duplicatedexistia no schema mas não era exibido. - Actions sempre visíveis — “Re-agendar transferência” aparecia mesmo quando não fazia sentido; faltavam “Cancelar Invoice e Liberar Meetings” e “Criar Nova Invoice para Professional”.
- Sem indicadores visuais de retry — o admin não conseguia identificar rapidamente quais invoices estavam em período de retry, quantas tentativas já haviam ocorrido, quando seria a próxima e por que a última falhou.
Objetivos
- Mapear corretamente todos os status do enum nos badges.
- Expor idade da invoice e estado de retry na index.
- Expor
retry_count,last_retry_at,last_error_messagee próxima tentativa no show. - Exibir o campo
duplicatednas transfers, com histórico de auditoria. - Tornar as actions condicionais ao estado da invoice e adicionar as duas actions faltantes.
- Adicionar um card de alerta com o estado de retry no topo do show.
Fora de escopo
- Métricas mensais e comparação entre períodos — é a parte 4.
Mudanças
1. Corrigir os badges de status
ruby
field :status, as: :badge, options: {
info: [:created, :pending],
success: [:done],
warning: [:in_bank_processing],
danger: [:failed, :canceled, :blocked]
}, format_using: -> {
I18n.t("professional_payment_invoice_statuses.#{value}")
}
Azul: aguardando processamento. Verde: pago. Amarelo: processando no banco. Vermelho: erro ou bloqueado.
2. Campos na index
id, professional (com link), status (badge), total (moeda), created_at, retry_status (badge derivado de retry_count/blocked?/done?) e paid_at.
3. Campos de retry no show
Visíveis apenas quando record.failed? || record.blocked?:
retry_count— “Tentativas de Retry”last_retry_at— “Última Tentativa”last_error_message— “Último Erro” (readonly)next_retry— próxima tentativa calculada, ou “limite atingido” / “invoice bloqueada”days_until_blocked— dias restantes até o bloqueio
4. Campo duplicated nas transfers
Em app/avo/resources/professional_payment_invoice_transfer.rb, adicionar o boolean duplicated (“Duplicada?”) e o painel de audits — as transfers já usam a gem audited via ApplicationRecord, e o histórico é útil para rastrear mudanças de status. Visível apenas quando há audits registrados.
5. Novos scopes
Avo::Scopes::WithStatusBlocked, WithStatusCreated, InRetryPeriod e ExpiredRetryPeriod, todos herdando de Avo::Advanced::Scopes::BaseScope e exibindo a contagem no nome. Remover WithStatusScheduled (status inexistente) e renomear WithStatusPaid para WithStatusDone.
6. Actions condicionais
| Action | Visibilidade | Efeito |
|---|---|---|
| Cancelar Invoice e Liberar Meetings | show + developer + status em blocked/canceled/failed |
destrói as associações de meetings (liberando-as para nova invoice) e marca a invoice como canceled, em transação |
| Criar Nova Invoice | show + developer + o profissional tem meetings invoiceáveis | chama ProfessionalPaymentInvoices::Create e reporta sucesso/erro |
| Re-agendar transferência | show + developer + status em created/failed + created_at > 10.days.ago |
enfileira ProfessionalPaymentInvoiceTransferCreationJob; a mensagem alerta quando retry_count >= 5 |
7. Card de alerta no show
Avo::Cards::ProfessionalInvoices::RetryStatusCard, visível quando failed? ou blocked?, com três estados: bloqueada (requer intervenção manual), limite de tentativas atingido, e em período de retry (com a próxima tentativa e o último erro).
Arquivos
Modificados: app/avo/resources/professional_payment_invoice.rb, app/avo/resources/professional_payment_invoice_transfer.rb, app/avo/actions/professional_invoices/execute_transfer.rb.
Novos: app/avo/scopes/with_status_blocked.rb, with_status_created.rb, in_retry_period.rb, expired_retry_period.rb, app/avo/actions/professional_invoices/cancel_and_release_meetings.rb, create_for_professional.rb, app/avo/cards/professional_invoices/retry_status_card.rb.
Removidos: app/avo/scopes/with_status_scheduled.rb.
Desvio verificado em 2026-08-06: as actions
CancelAndReleaseMeetingseCreateForProfessional, oRetryStatusCarde os campos de retry foram entregues. O mapeamento de badges desta spec, porém, foi superado: o enum deProfessionalPaymentInvoicehoje épending: 0, paid: 1, canceled: 2— os statuscreated,done,in_bank_processing,failedeblockedpertencem aProfessionalPaymentInvoiceTransfer, não à invoice. Os scopesInRetryPeriod/ExpiredRetryPeriodnão existem; o resource usaWithStatusInBankProcessing,WithStatusPaid,WithStatusCanceled,PaidCurrentMonthePendingOld. Os arquivoswith_status_blocked.rbewith_status_created.rbexistem mas não são referenciados pelo resource de invoice.
Como verificar
- Abrir a index de invoices no Avo e confirmar que todo status renderiza um badge (nenhum em branco).
- Abrir o show de uma invoice com falha e confirmar que os campos de retry e o
RetryStatusCardaparecem. - Confirmar que “Cancelar Invoice e Liberar Meetings” só aparece nos estados previstos e que, após executá-la, as meetings voltam a ser invoiceáveis.
Documentação
O estado atual das telas e ações está descrito em invoice_payment_flow.