88 lines
3.5 KiB
PHP
88 lines
3.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Services\Stats;
|
||
|
|
|
||
|
|
use App\Models\Stats\Daily\StatHrAttendanceDaily;
|
||
|
|
use Carbon\Carbon;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
|
||
|
|
class HrStatService implements StatDomainServiceInterface
|
||
|
|
{
|
||
|
|
public function aggregateDaily(int $tenantId, Carbon $date): int
|
||
|
|
{
|
||
|
|
$dateStr = $date->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;
|
||
|
|
}
|
||
|
|
}
|