option('days'); $dryRun = $this->option('dry-run'); $from = now(); $to = now()->addDays($days); $items = PaymentScheduleItem::query() ->with(['schedule.contract', 'payments']) ->whereHas('schedule.contract') ->whereDate('due_date', '>=', $from) ->whereDate('due_date', '<=', $to) ->whereRaw('amount > (SELECT COALESCE(SUM(amount), 0) FROM payments WHERE payments.schedule_item_id = payment_schedule_items.id)') ->orderBy('due_date') ->get(); if ($items->isEmpty()) { $this->warn('Không có đợt thanh toán nào sắp đến hạn trong ' . $days . ' ngày tới.'); return self::SUCCESS; } $this->info("Tìm thấy {$items->count()} đợt thanh toán sắp đến hạn."); $users = User::all(); if ($users->isEmpty()) { $this->warn('Không có user nào trong hệ thống để nhận thông báo.'); return self::FAILURE; } foreach ($items as $item) { $contract = $item->schedule?->contract; $remaining = (float) $item->remaining_amount; $this->line(sprintf( '- HĐ %s | Đợt %d | Ngày %s | Còn thiếu: %s VNĐ', $contract?->contract_number ?? 'N/A', $item->installment_no, $item->due_date?->format('d/m/Y'), number_format($remaining) )); if (! $dryRun) { foreach ($users as $user) { $user->notify(new PaymentDueNotification($item)); } } } if ($dryRun) { $this->info('Chế độ dry-run: Không có thông báo nào được gửi.'); } else { $this->info("Đã gửi thông báo cho {$users->count()} user(s)."); } return self::SUCCESS; } }