format('Y-m-d'); // 전체 직원 수 (tenant_user_profiles 기준) $totalEmployees = DB::connection('mysql') ->table('user_tenants') ->where('tenant_id', $tenantId) ->where('is_active', true) ->count(); // 근태 (attendances) $attendanceStats = DB::connection('mysql') ->table('attendances') ->where('tenant_id', $tenantId) ->where('base_date', $dateStr) ->whereNull('deleted_at') ->selectRaw(" COUNT(*) as total_count, SUM(CASE WHEN status = 'onTime' THEN 1 ELSE 0 END) as on_time_count, SUM(CASE WHEN status = 'late' THEN 1 ELSE 0 END) as late_count, SUM(CASE WHEN status = 'absent' THEN 1 ELSE 0 END) as absent_count, SUM(CASE WHEN status = 'overtime' THEN 1 ELSE 0 END) as overtime_count ") ->first(); $attendanceCount = ($attendanceStats->on_time_count ?? 0) + ($attendanceStats->late_count ?? 0) + ($attendanceStats->overtime_count ?? 0); $attendanceRate = $totalEmployees > 0 ? ($attendanceCount / $totalEmployees) * 100 : 0; // 휴가 (leaves) $leaveStats = DB::connection('mysql') ->table('leaves') ->where('tenant_id', $tenantId) ->where('start_date', '<=', $dateStr) ->where('end_date', '>=', $dateStr) ->where('status', 'approved') ->whereNull('deleted_at') ->selectRaw(" COUNT(*) as total_count, SUM(CASE WHEN leave_type = 'annual' THEN 1 ELSE 0 END) as annual_count, SUM(CASE WHEN leave_type = 'sick' THEN 1 ELSE 0 END) as sick_count, SUM(CASE WHEN leave_type NOT IN ('annual', 'sick') THEN 1 ELSE 0 END) as other_count ") ->first(); // 초과근무 (attendances status = 'overtime') $overtimeCount = $attendanceStats->overtime_count ?? 0; StatHrAttendanceDaily::updateOrCreate( ['tenant_id' => $tenantId, 'stat_date' => $dateStr], [ 'total_employees' => $totalEmployees, 'attendance_count' => $attendanceCount, 'late_count' => $attendanceStats->late_count ?? 0, 'absent_count' => $attendanceStats->absent_count ?? 0, 'attendance_rate' => $attendanceRate, 'leave_count' => $leaveStats->total_count ?? 0, 'leave_annual_count' => $leaveStats->annual_count ?? 0, 'leave_sick_count' => $leaveStats->sick_count ?? 0, 'leave_other_count' => $leaveStats->other_count ?? 0, 'overtime_hours' => 0, // attendances에 시간 정보 없음 'overtime_employee_count' => $overtimeCount, 'total_labor_cost' => 0, // 일간 인건비는 급여 정산 시 계산 ] ); return 1; } public function aggregateMonthly(int $tenantId, int $year, int $month): int { // 인사 도메인은 일간 테이블만 운영 (월간은 Phase 4에서 필요시 추가) return 0; } }