*/ public function expiring(int $days): Collection { return Service::with(['customer:id,uuid,fullname,email', 'product:id,name']) ->where('status', 'active') ->where('cancel_at_expiry', false) ->whereHas('customer') ->whereBetween('expires_at', [now()->startOfDay(), now()->addDays($days)->endOfDay()]) ->orderBy('expires_at') ->limit(self::MAX_RESULTS) ->get(); } public function daysLeft(Service $service): int { return (int) now()->startOfDay()->diffInDays($service->expires_at->copy()->startOfDay(), false); } /** * @param array $serviceIds */ public function send(array $serviceIds, User $admin): int { abort_if(!AutoEmailGate::mailEnabled(), 422, 'Hệ thống đang tắt gửi email (Mẫu email → Cài đặt → Bật gửi email).'); try { $template = $this->templates->loadTemplate(self::SCENARIO); } catch (ModelNotFoundException) { abort(422, 'Chưa có template nhắc gia hạn (service_renewal_reminder) đang bật trong Mẫu email.'); } AutoEmailGate::markManual(); $services = Service::with(['customer', 'product', 'orderItem']) ->whereIn('id', $serviceIds) ->whereNotNull('expires_at') ->get() ->filter(fn (Service $service) => filled($service->customer?->email)); foreach ($services as $service) { $mail = new ServiceRenewalReminderMail($service->customer, $service); MailLog::create([ 'template_id' => $template->id, 'subject' => $mail->envelope()->subject, 'from_address' => $template->mailConfig()['from_email'] ?? config('mail.from.address'), 'to' => $service->customer->email, 'customer_id' => $service->customer_id, 'sent_by' => $admin->id, 'message' => $mail->render(), ]); Mail::to($service->customer->email)->queue($mail); } return $services->count(); } }