option('minutes'); $since = now()->subMinutes($minutes)->format('Y-m-d H:i:s'); // Scope to company users (same filter as the daily batch) $userIds = DB::table('users') ->whereNotNull('company_id') ->pluck('id') ->all(); if (empty($userIds)) { $this->line('No users found — nothing to poll.'); return Command::SUCCESS; } // Find linkedids present in CDR but absent from outbound_reports $unprocessed = DB::table('asteriskcdrdb.cdr') ->select('linkedid') ->whereIn('disposition', ['ANSWERED', 'BUSY', 'NO ANSWER', 'FAILED']) ->where('dcontext', 'from-internal') ->where('lastapp', 'Dial') ->whereColumn('uniqueid', 'linkedid') ->where('calldate', '>=', $since) ->whereIn('cnum', $userIds) ->whereNotIn('linkedid', function ($q) { $q->select('linkedid')->from('outbound_reports'); }) ->distinct() ->pluck('linkedid'); if ($unprocessed->isEmpty()) { $this->line('No unprocessed outbound calls found in the last ' . $minutes . ' minutes.'); return Command::SUCCESS; } $this->info('Found ' . $unprocessed->count() . ' unprocessed outbound call(s) — dispatching jobs...'); foreach ($unprocessed as $linkedid) { // Small stagger so workers aren't hammered simultaneously ProcessOutboundCallJob::dispatch($linkedid)->delay(now()->addSeconds(3)); } return Command::SUCCESS; } }