with(['user', 'department']) ->forTenant($tenantId); // 검색 필터 (이름, 사번, 이메일) if (! empty($filters['q'])) { $search = $filters['q']; $query->where(function ($q) use ($search) { $q->where('display_name', 'like', "%{$search}%") ->orWhereHas('user', function ($uq) use ($search) { $uq->where('name', 'like', "%{$search}%") ->orWhere('email', 'like', "%{$search}%") ->orWhere('phone', 'like', "%{$search}%"); }) ->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.employee_code')) LIKE ?", ["%{$search}%"]); }); } // 상태 필터 if (! empty($filters['status'])) { $query->where('employee_status', $filters['status']); } // 부서 필터 if (! empty($filters['department_id'])) { $query->where('department_id', $filters['department_id']); } // 정렬 $query->orderByRaw("FIELD(employee_status, 'active', 'leave', 'resigned')") ->orderBy('created_at', 'desc'); return $query->paginate($perPage); } /** * 사원 상세 조회 */ public function getEmployeeById(int $id): ?Employee { $tenantId = session('selected_tenant_id'); return Employee::query() ->with(['user', 'department', 'manager']) ->forTenant($tenantId) ->find($id); } /** * 사원 통계 */ public function getStats(): array { $tenantId = session('selected_tenant_id'); $baseQuery = Employee::query()->forTenant($tenantId); return [ 'total' => (clone $baseQuery)->count(), 'active' => (clone $baseQuery)->where('employee_status', 'active')->count(), 'leave' => (clone $baseQuery)->where('employee_status', 'leave')->count(), 'resigned' => (clone $baseQuery)->where('employee_status', 'resigned')->count(), ]; } /** * 사원 등록 (User + TenantUserProfile 동시 생성) */ public function createEmployee(array $data): Employee { $tenantId = session('selected_tenant_id'); return DB::transaction(function () use ($data, $tenantId) { // User 생성 $user = User::create([ 'name' => $data['name'], 'email' => $data['email'] ?? null, 'phone' => $data['phone'] ?? null, 'password' => bcrypt($data['password'] ?? 'sam1234!'), 'is_active' => true, ]); // json_extra 구성 $jsonExtra = []; if (! empty($data['employee_code'])) { $jsonExtra['employee_code'] = $data['employee_code']; } if (! empty($data['hire_date'])) { $jsonExtra['hire_date'] = $data['hire_date']; } if (! empty($data['address'])) { $jsonExtra['address'] = $data['address']; } if (! empty($data['emergency_contact'])) { $jsonExtra['emergency_contact'] = $data['emergency_contact']; } // Employee(TenantUserProfile) 생성 $employee = Employee::create([ 'tenant_id' => $tenantId, 'user_id' => $user->id, 'department_id' => $data['department_id'] ?? null, 'position_key' => $data['position_key'] ?? null, 'job_title_key' => $data['job_title_key'] ?? null, 'work_location_key' => $data['work_location_key'] ?? null, 'employment_type_key' => $data['employment_type_key'] ?? null, 'employee_status' => $data['employee_status'] ?? 'active', 'manager_user_id' => $data['manager_user_id'] ?? null, 'display_name' => $data['display_name'] ?? $data['name'], 'json_extra' => ! empty($jsonExtra) ? $jsonExtra : null, ]); return $employee->load(['user', 'department']); }); } /** * 사원 정보 수정 */ public function updateEmployee(int $id, array $data): ?Employee { $employee = $this->getEmployeeById($id); if (! $employee) { return null; } // 기본 필드 업데이트 $updateData = array_filter([ 'department_id' => $data['department_id'] ?? null, 'position_key' => $data['position_key'] ?? null, 'job_title_key' => $data['job_title_key'] ?? null, 'work_location_key' => $data['work_location_key'] ?? null, 'employment_type_key' => $data['employment_type_key'] ?? null, 'employee_status' => $data['employee_status'] ?? null, 'manager_user_id' => $data['manager_user_id'] ?? null, 'display_name' => $data['display_name'] ?? null, ], fn ($v) => $v !== null); // json_extra 업데이트 $jsonExtraKeys = ['employee_code', 'hire_date', 'address', 'emergency_contact', 'salary', 'bank_account']; $extra = $employee->json_extra ?? []; foreach ($jsonExtraKeys as $key) { if (array_key_exists($key, $data)) { if ($data[$key] === null || $data[$key] === '') { unset($extra[$key]); } else { $extra[$key] = $data[$key]; } } } $updateData['json_extra'] = ! empty($extra) ? $extra : null; $employee->update($updateData); // User 기본정보 동기화 if ($employee->user) { $userUpdate = []; if (! empty($data['name'])) { $userUpdate['name'] = $data['name']; } if (! empty($data['email'])) { $userUpdate['email'] = $data['email']; } if (! empty($data['phone'])) { $userUpdate['phone'] = $data['phone']; } if (! empty($userUpdate)) { $employee->user->update($userUpdate); } } return $employee->fresh(['user', 'department']); } /** * 사원 삭제 (퇴직 처리) */ public function deleteEmployee(int $id): bool { $employee = $this->getEmployeeById($id); if (! $employee) { return false; } $employee->update(['employee_status' => 'resigned']); return true; } /** * 부서 목록 (드롭다운용) */ public function getDepartments(): \Illuminate\Database\Eloquent\Collection { $tenantId = session('selected_tenant_id'); return Department::query() ->where('is_active', true) ->when($tenantId, fn ($q) => $q->where('tenant_id', $tenantId)) ->orderBy('sort_order') ->orderBy('name') ->get(['id', 'name', 'code']); } /** * 직급 목록 (드롭다운용) */ public function getPositions(string $type = 'rank'): \Illuminate\Database\Eloquent\Collection { return Position::query() ->forTenant() ->where('type', $type) ->where('is_active', true) ->ordered() ->get(['id', 'key', 'name']); } }