From d0d5a7acd956adaf695a35393eec254a96de52e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 5 Mar 2026 16:54:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[hr]=20=EA=B7=BC=ED=83=9C=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EC=98=81=EC=97=85=ED=8C=80=20=EB=B0=8F=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=20=EC=82=AC=EC=9B=90=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 근태 목록/통계/요약/초과근무에서 영업팀+제외 사원 제외 - 근태관리 부서 드롭다운에서 영업팀 제외 - 활성 사원 목록(드롭다운)에서 영업팀+제외 사원 제외 --- app/Services/HR/AttendanceService.php | 61 ++++++++++++++++++++++++--- app/Services/HR/LeaveService.php | 12 +++++- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/app/Services/HR/AttendanceService.php b/app/Services/HR/AttendanceService.php index 1aace6bf..e548a9b3 100644 --- a/app/Services/HR/AttendanceService.php +++ b/app/Services/HR/AttendanceService.php @@ -12,6 +12,24 @@ class AttendanceService { + /** + * 제외 대상 사원의 user_id 목록 (영업팀 + 강제 제외) + */ + private function getExcludedUserIds(?int $tenantId = null): array + { + $tenantId = $tenantId ?? session('selected_tenant_id'); + + return Employee::query() + ->forTenant($tenantId) + ->where(function ($q) { + $q->whereHas('department', function ($dq) { + $dq->where('name', 'like', '%영업팀%'); + })->orWhere('json_extra->is_excluded', true); + }) + ->pluck('user_id') + ->toArray(); + } + /** * 필터 적용 쿼리 생성 (목록/엑셀 공통) */ @@ -23,6 +41,12 @@ private function buildFilteredQuery(array $filters = []) ->with(['user', 'user.tenantProfiles' => fn ($q) => $q->where('tenant_id', $tenantId), 'user.tenantProfiles.department']) ->forTenant($tenantId); + // 제외 사원 필터링 (영업팀 부서 + 강제 제외) + $excludedUserIds = $this->getExcludedUserIds($tenantId); + if (! empty($excludedUserIds)) { + $query->whereNotIn('user_id', $excludedUserIds); + } + if (! empty($filters['q'])) { $search = $filters['q']; $query->whereHas('user', function ($q) use ($search) { @@ -82,9 +106,18 @@ public function getMonthlyStats(?int $year = null, ?int $month = null): array ? now()->toDateString() : Carbon::create($year, $month, 1)->endOfMonth()->toDateString(); - $counts = Attendance::query() + // 제외 사원 필터링 + $excludedUserIds = $this->getExcludedUserIds($tenantId); + + $statsQuery = Attendance::query() ->forTenant($tenantId) - ->betweenDates($startDate, $endDate) + ->betweenDates($startDate, $endDate); + + if (! empty($excludedUserIds)) { + $statsQuery->whereNotIn('user_id', $excludedUserIds); + } + + $counts = $statsQuery ->select('status', DB::raw('COUNT(*) as cnt')) ->groupBy('status') ->pluck('cnt', 'status') @@ -364,9 +397,17 @@ public function getEmployeeMonthlySummary(int $year, int $month): array $startDate = sprintf('%04d-%02d-01', $year, $month); $endDate = Carbon::create($year, $month, 1)->endOfMonth()->toDateString(); - $raw = Attendance::query() + $excludedUserIds = $this->getExcludedUserIds($tenantId); + + $summaryQuery = Attendance::query() ->forTenant($tenantId) - ->betweenDates($startDate, $endDate) + ->betweenDates($startDate, $endDate); + + if (! empty($excludedUserIds)) { + $summaryQuery->whereNotIn('user_id', $excludedUserIds); + } + + $raw = $summaryQuery ->select( 'user_id', 'status', @@ -418,9 +459,17 @@ public function getOvertimeAlerts(): array $weekStart = now()->startOfWeek(Carbon::MONDAY)->toDateString(); $weekEnd = now()->endOfWeek(Carbon::SUNDAY)->toDateString(); - $results = Attendance::query() + $excludedUserIds = $this->getExcludedUserIds($tenantId); + + $overtimeQuery = Attendance::query() ->forTenant($tenantId) - ->betweenDates($weekStart, $weekEnd) + ->betweenDates($weekStart, $weekEnd); + + if (! empty($excludedUserIds)) { + $overtimeQuery->whereNotIn('user_id', $excludedUserIds); + } + + $results = $overtimeQuery ->select( 'user_id', DB::raw("SUM(GREATEST(0, COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(json_details, '$.work_minutes')) AS SIGNED), 0))) as week_minutes") diff --git a/app/Services/HR/LeaveService.php b/app/Services/HR/LeaveService.php index c7dbaaef..f20e0563 100644 --- a/app/Services/HR/LeaveService.php +++ b/app/Services/HR/LeaveService.php @@ -764,13 +764,14 @@ public function getDepartments(): \Illuminate\Database\Eloquent\Collection return Department::query() ->where('is_active', true) ->when($tenantId, fn ($q) => $q->where('tenant_id', $tenantId)) + ->where('name', 'not like', '%영업팀%') ->orderBy('sort_order') ->orderBy('name') ->get(['id', 'name', 'code']); } /** - * 활성 사원 목록 (드롭다운용) + * 활성 사원 목록 (드롭다운용) — 영업팀 + 강제 제외 사원 제외 */ public function getActiveEmployees(): \Illuminate\Database\Eloquent\Collection { @@ -780,6 +781,15 @@ public function getActiveEmployees(): \Illuminate\Database\Eloquent\Collection ->with('user:id,name') ->forTenant($tenantId) ->activeEmployees() + ->where(function ($q) { + $q->whereDoesntHave('department', function ($dq) { + $dq->where('name', 'like', '%영업팀%'); + })->orWhereNull('department_id'); + }) + ->where(function ($q) { + $q->whereNull('json_extra->is_excluded') + ->orWhere('json_extra->is_excluded', false); + }) ->orderBy('display_name') ->get(['id', 'user_id', 'display_name', 'department_id']); }