first(); if (!$setting) { $this->error('Attendance setting belum tersedia. Jalankan seeder AttendanceSettingSeeder.'); return self::FAILURE; } $timezone = $setting->timezone ?: config('app.timezone', 'Asia/Jakarta'); $targetDate = $this->resolveTargetDate($timezone); if (!$targetDate) { return self::FAILURE; } $effectiveWorkdays = collect($setting->effective_workdays ?? []) ->map(fn($day) => (int) $day) ->unique() ->values(); if ($effectiveWorkdays->isNotEmpty() && !$effectiveWorkdays->contains($targetDate->isoWeekday())) { $this->info("Skip {$targetDate->toDateString()}: bukan hari kerja efektif."); return self::SUCCESS; } $users = User::query() ->where('status', 'aktif') ->select('id', 'name') ->get(); $createdAlpha = 0; $notedMissingCheckout = 0; foreach ($users as $user) { $attendance = Attendance::query() ->where('user_id', $user->id) ->whereDate('date', $targetDate->toDateString()) ->first(); if (!$attendance) { Attendance::create([ 'user_id' => $user->id, 'date' => $targetDate->toDateString(), 'status' => 'alpha', 'notes' => 'Tidak melakukan absensi pada hari kerja efektif.', ]); $createdAlpha++; continue; } if ($setting->require_checkout && $attendance->check_in && !$attendance->check_out) { $attendance->update([ 'notes' => $this->appendNote($attendance->notes, 'Absensi tidak lengkap: check-out tidak ditemukan.'), ]); $notedMissingCheckout++; } } $this->info("Sinkronisasi {$targetDate->toDateString()} selesai."); $this->line("Alpha dibuat: {$createdAlpha}"); $this->line("Catatan check-out kosong: {$notedMissingCheckout}"); return self::SUCCESS; } private function resolveTargetDate(string $timezone): ?Carbon { $dateOption = $this->option('date'); if (!$dateOption) { return Carbon::now($timezone)->subDay()->startOfDay(); } try { return Carbon::createFromFormat('Y-m-d', (string) $dateOption, $timezone)->startOfDay(); } catch (\Throwable $e) { $this->error('Format --date tidak valid. Gunakan Y-m-d, contoh: 2026-02-19'); return null; } } private function appendNote(?string $existing, string $note): string { $current = trim((string) $existing); if ($current === '') { return $note; } if (str_contains($current, $note)) { return $current; } return "{$current} | {$note}"; } }