<?php
 namespace App\Console\Commands; use App\Jobs\SendEmailJob; use App\Models\ClientDomain; use App\Models\DomainRenewalAlert; use App\Models\Setting; use Illuminate\Console\Command; class SendDomainRenewalAlertsCommand extends Command { protected $signature = 'domain:renewal-alerts
                            {--dry-run : Show what would be sent without actually sending}'; protected $description = 'Send renewal reminder emails for expiring domains'; private const REMINDER_TIERS = [ 'first' => ['setting' => 'domain_reminder_first_days', 'default' => 30, 'template' => 'Domain Renewal First Reminder', 'number' => 1], 'second' => ['setting' => 'domain_reminder_second_days', 'default' => 14, 'template' => 'Domain Renewal Second Reminder', 'number' => 2], 'third' => ['setting' => 'domain_reminder_third_days', 'default' => 7, 'template' => 'Domain Renewal Third Reminder', 'number' => 3], 'final' => ['setting' => 'domain_reminder_final_days', 'default' => 1, 'template' => 'Domain Renewal Final Reminder', 'number' => 4], ]; public function handle(): int { if (! \App\Services\Domain\DomainPolicyGuard::renewalEnabled()) { $this->info('Domain renewals master gate is off (enable_renewal_orders=false); skipping alerts.'); return Command::SUCCESS; } $dryRun = $this->option('dry-run'); $totalReminders = count(self::REMINDER_TIERS); $this->info('Starting domain renewal alerts process...'); if ($dryRun) { $this->info('DRY RUN MODE - No emails will be sent'); } $sentCount = 0; $skippedCount = 0; $alertedDomainIdsThisRun = []; $enabledTierDays = []; foreach (self::REMINDER_TIERS as $tn => $t) { $d = (int) Setting::getValue($t['setting'], $t['default']); if ($d > 0) { $enabledTierDays[$tn] = $d; } } foreach (self::REMINDER_TIERS as $tierName => $tier) { $days = (int) Setting::getValue($tier['setting'], $tier['default']); if ($days <= 0) { $this->line("Skipping {$tierName} reminder (disabled, days = 0)"); continue; } $lowerDays = 0; foreach ($enabledTierDays as $d) { if ($d < $days && $d > $lowerDays) { $lowerDays = $d; } } $upperDate = now()->addDays($days)->toDateString(); $query = ClientDomain::where('status', 'active') ->whereDate('expire_date', '<=', $upperDate) ->where(function ($q) { $q->where('disable_reminders', false) ->orWhereNull('disable_reminders'); }) ->whereDoesntHave('renewalAlerts', function ($q) use ($tierName) { $q->where('reminder_type', $tierName) ->whereColumn('for_expire_date', 'client_domains.expire_date'); }) ->with('client'); if ($lowerDays > 0) { $query->whereDate('expire_date', '>', now()->addDays($lowerDays)->toDateString()); } else { $query->whereDate('expire_date', '>=', now()->toDateString()); } $domains = $query->get(); if ($domains->isEmpty()) { $this->line("No domains in {$tierName} band ((+{$lowerDays}, +{$days}] days)."); continue; } $this->info("Found {$domains->count()} domains for {$tierName} reminder band ((+{$lowerDays}, +{$days}] days)."); foreach ($domains as $domain) { if (!$domain->client) { $this->warn("Skipped: {$domain->domain} - no associated client"); $skippedCount++; continue; } if (isset($alertedDomainIdsThisRun[$domain->id])) { continue; } $daysRemaining = max(0, now()->startOfDay()->diffInDays( \Carbon\Carbon::parse($domain->expire_date)->startOfDay(), false )); if ($dryRun) { $this->line("Would send {$tierName} reminder for {$domain->domain} to {$domain->client->email} ({$daysRemaining} days remaining)"); $alertedDomainIdsThisRun[$domain->id] = true; $sentCount++; continue; } try { SendEmailJob::dispatch( $tier['template'], 'client_domain', $domain->id, [ 'DOMAIN' => $domain->domain, 'EXPIRY_DATE' => $domain->expire_date->format('d/m/Y'), 'DAYS_REMAINING' => $daysRemaining, 'CLIENT_NAME' => $domain->client->first_name . ' ' . $domain->client->last_name, 'RENEW_URL' => route('store.auth.domains.renew', $domain->id), 'REMINDER_NUMBER' => $tier['number'], 'TOTAL_REMINDERS' => $totalReminders, 'REMINDER_LABEL' => $tierName, 'IS_FINAL' => $tierName === 'final' ? 'yes' : 'no', ] ); DomainRenewalAlert::create([ 'client_domain_id' => $domain->id, 'days_before_expiry' => $daysRemaining, 'reminder_type' => $tierName, 'for_expire_date' => $domain->expire_date->toDateString(), 'sent_at' => now(), ]); app(\App\Core\Hook\HookManager::class)->action('DomainRenewalAlertSent', [ 'domain' => $domain, 'domain_id' => $domain->id, 'client_id' => $domain->client_id, 'tier' => $tierName ?? null, 'days_remaining' => $daysRemaining, ]); $alertedDomainIdsThisRun[$domain->id] = true; $this->info("Sent {$tierName} reminder: {$domain->domain} ({$daysRemaining} days before expiry)"); $sentCount++; } catch (\Exception $e) { $this->error("Failed: {$domain->domain} - {$e->getMessage()}"); $skippedCount++; } } } $this->newLine(); $this->info('Domain renewal alerts completed:'); $this->info("- Alerts sent/queued: {$sentCount}"); $this->info("- Skipped/failed: {$skippedCount}"); return Command::SUCCESS; } } 