sidakpelem/app/Console/Commands/SyncAttendanceDailyStatus.php

127 lines
3.8 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Attendance;
use App\Models\AttendanceSetting;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
class SyncAttendanceDailyStatus extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'attendance:sync-daily-status {--date= : Target date (Y-m-d), default H-1 berdasarkan timezone setting}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sinkron status absensi: isi alpha jika tidak absen, dan beri catatan jika check-out tidak ditemukan pada hari kerja efektif.';
/**
* Execute the console command.
*/
public function handle(): int
{
$setting = AttendanceSetting::query()->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}";
}
}