Negócio

Notificacoes

Sistema de notificacoes multicanal com 36 tipos, configuracao global e preferencias individuais por usuario.

Notificacoes

O sistema de notificacoes da plataforma AgrSis envia alertas por 3 canais diferentes, com controle granular tanto a nivel global (admin) quanto individual (usuario).

Canais de Envio

CanalIdentificadorDescricao
Sistema (in-app)in_appNotificacao interna visivel no painel do usuario
EmailemailEmail enviado via SMTP (enfileirado na queue)
WhatsAppwhatsappMensagem via Z-API (enfileirada com delay anti-spam)

Tipos de Notificacao

O enum NotificationType define todos os tipos de notificacao organizados por contexto.

Notificacoes do Produtor

TipoValorTitulo
Proposta recebidaproposal_receivedNova proposta recebida
Proposta aceitaproposal_acceptedProposta aceita
Proposta rejeitadaproposal_rejectedProposta rejeitada
Proposta canceladaproposal_cancelledProposta cancelada
Prazo proximoorder_deadline_approachingPrazo proximo do vencimento
Cotacao expiradaorder_expiredCotacao expirada
Cotacao em analiseorder_in_analysisCotacao em analise
Mensagem recebidamessage_receivedNova mensagem

Notificacoes do Fornecedor

TipoValorTitulo
Nova cotacaonew_order_availableNova cotacao disponivel
Cotacao atualizadaorder_updatedCotacao atualizada
Proposta visualizadaproposal_viewedProposta visualizada
Proposta aceitayour_proposal_acceptedSua proposta foi aceita!
Confirmacao pendenteaward_pending_confirmationConfirmacao de proposta vencedora
Negociacao confirmadaaward_confirmedNegociacao confirmada
Vencedora recusadaaward_declinedProposta vencedora recusada
Confirmacao expiradaaward_confirmation_expiredConfirmacao da vencedora expirada
Prazo prorrogadoanalysis_deadline_extendedPrazo de analise prorrogado
Proposta rejeitadayour_proposal_rejectedProposta rejeitada
Proposta perdeuyour_proposal_lostOutra proposta foi aceita
Cotacao expiradaorder_deadline_expiredCotacao expirada
Resposta recebidamessage_responseNova resposta

Notificacoes de Avaliacao e Entrega

TipoValorTitulo
Avaliacao disponivelreview_negotiation_availableAvaliacao disponivel
Lembrete avaliacaoreview_negotiation_reminderLembrete de avaliacao
Entrega pendentedelivery_pendingEntrega pendente
Lembrete entregadelivery_reminderLembrete de entrega
Entrega marcadadelivery_markedEntrega realizada
Lembrete recebimentoreceipt_reminderConfirme o recebimento
Avaliar entregadelivery_review_availableAvalie a entrega
Lembrete avaliar entregadelivery_review_reminderLembrete: avalie a entrega

Notificacoes de Assinatura e Pagamento

TipoValorTitulo
Trial expirandotrial_expiringPeriodo de teste expirando
Trial expiroutrial_expiredPeriodo de teste expirou
Pagamento confirmadopayment_confirmedPagamento confirmado
Pagamento atrasadopayment_overduePagamento atrasado
Assinatura renovadasubscription_renewedAssinatura renovada
Cobranca geradasubscription_charge_createdCobranca gerada

Notificacoes do Admin

TipoValorTitulo
Novo produtoradmin_new_producer_registeredNovo produtor cadastrado
Novo fornecedoradmin_new_supplier_registeredNovo fornecedor cadastrado

Destinatario por Tipo

Alguns tipos de notificacao sao enviados para multiplos perfis. Os metodos isForProducer(), isForSupplier() e isForAdmin() no enum definem quais tipos se aplicam a cada perfil.

Notificacoes compartilhadas entre perfis:

  • award_pending_confirmation, award_confirmed, award_declined, award_confirmation_expired, analysis_deadline_extended -- enviadas tanto para produtor quanto fornecedor e admin
  • review_negotiation_available, review_negotiation_reminder, delivery_review_available, delivery_review_reminder -- enviadas para produtor e fornecedor
  • order_in_analysis -- enviada para produtor e fornecedor

Configuracao Global (Admin)

O model NotificationChannelConfig (tabela notification_channel_config) permite ao admin habilitar ou desabilitar canais por tipo de notificacao.

CampoTipoDescricao
notification_typestringTipo da notificacao (valor do enum)
in_appbooleanCanal in-app habilitado
emailbooleanCanal email habilitado
whatsappbooleanCanal WhatsApp habilitado

Se um canal esta desabilitado globalmente, ele nao sera utilizado para nenhum usuario, independente das preferencias individuais.

Preferencias Individuais do Usuario

O model NotificationSetting (tabela notification_settings) armazena as preferencias de cada usuario por tipo de notificacao.

CampoTipoDescricao
user_idFKUsuario
notification_typeenumTipo da notificacao
in_appbooleanReceber no sistema
emailbooleanReceber por email
whatsappbooleanReceber por WhatsApp

Valores padrao

Quando um usuario recebe uma notificacao de um tipo que ainda nao tem configuracao, o sistema cria automaticamente via getOrCreateDefault():

CanalPadrao
in_apptrue (habilitado)
emailtrue (habilitado)
whatsappfalse (desabilitado)

Metodos disponiveis

  • shouldSendInApp() -- verifica se in-app esta habilitado
  • shouldSendEmail() -- verifica se email esta habilitado
  • shouldSendWhatsApp() -- verifica se WhatsApp esta habilitado
  • getEnabledChannels() -- retorna array com canais habilitados
  • toggleChannel(channel, enabled) -- altera um canal especifico

Fluxo de Envio

O NotificationService coordena o envio em todos os canais com a seguinte logica:

1. Verificar preferencia do usuario (NotificationSetting)
   └── Se in_app desabilitado → nao cria notificacao
2. Criar registro Notification (in-app)
3. Verificar configuracao global (NotificationChannelConfig)
4. Para cada canal habilitado globalmente:
   └── Verificar preferencia do usuario
       ├── Email habilitado? → enfileirar NotificationMail
       └── WhatsApp habilitado? → enfileirar SendWhatsAppNotificationJob

Idempotencia

Para evitar notificacoes duplicadas, o sistema verifica se ja existe uma notificacao com o mesmo type e proposal_uuid antes de criar. Essa verificacao e feita especificamente para PROPOSAL_RECEIVED.

Canal Email

O email e enviado via NotificationMail (Mailable que implementa ShouldQueue).

Estrutura do email

ComponenteDescricao
Assunto"AgrSis - {titulo}" com codigo de referencia opcional
SaudacaoNome do destinatario
MensagemTexto da notificacao
DetalhesTabela com metadados (codigo da cotacao, proposta, prazo, valor, raio)
Botao de acaoLink para a plataforma (URL absoluta)
Link de opt-outLink para /profile#notificacoes para gerenciar preferencias

Resolucao de URLs

O NotificationService resolve URLs relativas para absolutas baseado no perfil do usuario:

PerfilBase URL
Adminhttps://admin.agrsis.com
Supplierhttps://supplier.agrsis.com
Producer (padrao)https://producer.agrsis.com

Canal WhatsApp

O envio e feito via SendWhatsAppNotificationJob, um Job que implementa ShouldQueue.

Configuracao do Job

ParametroValor
Tentativas3 ($tries = 3)
Backoff10 segundos entre tentativas
ServicoWhatsAppService (integra com Z-API)

Anti-spam (Delay Incremental)

O NotificationService implementa um mecanismo anti-spam para evitar envio massivo de mensagens WhatsApp em um unico request:

private static int $whatsappDispatchCount = 0;

// Cada mensagem adiciona 3 segundos de delay
$delay = self::$whatsappDispatchCount * 3;
self::$whatsappDispatchCount++;

Exemplo pratico: ao publicar uma cotacao que notifica 10 fornecedores:

  • Fornecedor 1: enviado imediatamente (delay 0s)
  • Fornecedor 2: enviado apos 3s
  • Fornecedor 3: enviado apos 6s
  • ...
  • Fornecedor 10: enviado apos 27s

O contador e estatico e se reseta a cada request HTTP. Isso evita que a Z-API bloqueie o numero por envio excessivo.

Notificacoes In-App

Operacoes disponiveis

MetodoDescricao
markAsRead(uuid, userId)Marcar uma notificacao como lida
markAllAsRead(userId)Marcar todas como lidas
getUnreadCount(userId)Contar nao lidas

Estrutura do registro (tabela notifications)

CampoTipoDescricao
uuidUUIDIdentificador unico
user_idFKDestinatario
typestringTipo (valor do enum)
titlestringTitulo
messagestringMensagem
dataJSONMetadados (uuids, codigos, prazos)
linkstringURL relativa para acao
readbooleanLida ou nao
read_atdatetimeData da leitura

Cenarios de Notificacao

Publicacao de cotacao

Quando uma cotacao e publicada, o NotificationService::notifyOrderPublished() busca fornecedores dentro do raio de fornecimento usando calculo de distancia por coordenadas (formula de Haversine). Apenas fornecedores com:

  • Endereco ativo com latitude/longitude
  • Status ativo (supplier e user)
  • Dentro do raio definido na cotacao

Proposta aceita

O notifyProposalAccepted() notifica:

  1. O fornecedor vencedor com YOUR_PROPOSAL_ACCEPTED
  2. Todos os outros fornecedores que enviaram proposta com YOUR_PROPOSAL_LOST

Cancelamento/Revogacao de cotacao

O notifyOrderStatusChangedForSuppliers() notifica todos os fornecedores participantes, incluindo motivo e observacoes quando disponveis.

Adjudicacao (Award)

O fluxo de adjudicacao gera multiplas notificacoes:

  1. notifyAwardPendingConfirmation() -- notifica vencedor, produtor e demais participantes
  2. notifyAwardConfirmed() -- notifica produtor, vencedor e demais (encerramento)
  3. notifyAwardDeclined() -- notifica todos e reabre prazo de analise
  4. notifyAwardConfirmationExpired() -- notifica todos sobre expiracao do prazo
Copyright © 2026