- Arquitetura corrigida: templates agora pertencem à inbox (WhatsApp),
não à unidade PIX (que é uma config financeira, não de mensagens)
- Migration: troca FK captain_unit_id -> inbox_id (up/down explícito)
- Model: belongs_to :inbox; scope for_inbox
- Controller: escopo via account.inboxes.find(inbox_id)
- Rotas: move de captain/units/:id → inboxes/:id/notification_templates
- Scanner job: joins(:conversation).where(conversations: {inbox_id:})
- UI: página /captain/notifications com seletor de inbox no topo
(chips clicáveis, templates carregam por watch no selectedInboxId)
- i18n PT/EN: novas keys INBOX_LABEL, SELECT_INBOX_HINT, EMPTY
42 lines
1.3 KiB
Ruby
42 lines
1.3 KiB
Ruby
class Captain::Notifications::NotificationScannerJob < ApplicationJob
|
|
queue_as :scheduled_jobs
|
|
|
|
# Tolerance window around the target time (job runs every 5 min, so ±5 min ensures coverage)
|
|
WINDOW_MINUTES = 5
|
|
|
|
def perform
|
|
Captain::NotificationTemplate.active.find_each do |template|
|
|
eligible_reservations_for(template).find_each do |reservation|
|
|
Captain::Notifications::SendNotificationService.new(reservation, template).perform
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def eligible_reservations_for(template)
|
|
target_time = compute_target_time(template)
|
|
window_start = target_time - WINDOW_MINUTES.minutes
|
|
window_end = target_time + WINDOW_MINUTES.minutes
|
|
|
|
Captain::Reservation
|
|
.joins(:conversation)
|
|
.where(conversations: { inbox_id: template.inbox_id })
|
|
.where(status: Captain::Reservation.statuses.slice(:confirmed, :active).values)
|
|
.where(check_in_at: window_start..window_end)
|
|
.where.not(conversation_id: nil)
|
|
.where(
|
|
"NOT (captain_reservations.metadata->'notified_templates' @> ?::jsonb)",
|
|
"[#{template.id}]"
|
|
)
|
|
end
|
|
|
|
def compute_target_time(template)
|
|
if template.before?
|
|
template.timing_minutes.minutes.from_now
|
|
else
|
|
template.timing_minutes.minutes.ago
|
|
end
|
|
end
|
|
end
|