From 511bfa3ec53deeac42dcd72f0b5ec628e091d3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Mar 2026 22:36:05 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[leave]=20=ED=9C=B4=EA=B0=80=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EC=8B=9C=20=EA=B2=B0=EC=9E=AC=EC=84=A0=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 휴가 신청 모달에 결재선 드롭다운 + 미리보기 UI 추가 - 선택된 결재선으로 결재 생성 (미선택 시 기본결재선 fallback) - 휴가 목록에 결재진행 컬럼 추가 (원형 아이콘: ✓승인/✗반려/숫자대기/파랑현재) - approval.steps.approver eager load 추가 --- .../Api/Admin/HR/LeaveController.php | 1 + app/Http/Controllers/HR/LeaveController.php | 8 +++ app/Services/HR/LeaveService.php | 29 ++++++---- resources/views/hr/leaves/index.blade.php | 53 ++++++++++++++++++- .../views/hr/leaves/partials/table.blade.php | 36 ++++++++++++- 5 files changed, 114 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/Api/Admin/HR/LeaveController.php b/app/Http/Controllers/Api/Admin/HR/LeaveController.php index 45f09641..0b7006fb 100644 --- a/app/Http/Controllers/Api/Admin/HR/LeaveController.php +++ b/app/Http/Controllers/Api/Admin/HR/LeaveController.php @@ -52,6 +52,7 @@ public function store(Request $request): JsonResponse 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'reason' => 'nullable|string|max:1000', + 'approval_line_id' => 'nullable|integer|exists:approval_lines,id', ]); try { diff --git a/app/Http/Controllers/HR/LeaveController.php b/app/Http/Controllers/HR/LeaveController.php index 97c2d8b6..dc3b104f 100644 --- a/app/Http/Controllers/HR/LeaveController.php +++ b/app/Http/Controllers/HR/LeaveController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\HR; use App\Http\Controllers\Controller; +use App\Models\Approvals\ApprovalLine; use App\Models\HR\Leave; use App\Services\HR\LeaveService; use Illuminate\Contracts\View\View; @@ -26,11 +27,18 @@ public function index(\Illuminate\Http\Request $request): View|Response $typeMap = Leave::TYPE_MAP; $statusMap = Leave::STATUS_MAP; + $tenantId = session('selected_tenant_id', 1); + $approvalLines = ApprovalLine::where('tenant_id', $tenantId) + ->orderByDesc('is_default') + ->orderBy('name') + ->get(['id', 'name', 'steps', 'is_default']); + return view('hr.leaves.index', [ 'employees' => $employees, 'departments' => $departments, 'typeMap' => $typeMap, 'statusMap' => $statusMap, + 'approvalLines' => $approvalLines, ]); } diff --git a/app/Services/HR/LeaveService.php b/app/Services/HR/LeaveService.php index e7b88920..fadd7195 100644 --- a/app/Services/HR/LeaveService.php +++ b/app/Services/HR/LeaveService.php @@ -33,6 +33,7 @@ public function getLeaves(array $filters = [], int $perPage = 20): LengthAwarePa 'user.tenantProfiles' => fn ($q) => $q->where('tenant_id', $tenantId), 'user.tenantProfiles.department', 'approver', + 'approval.steps.approver', ]) ->forTenant($tenantId); @@ -109,8 +110,8 @@ public function storeLeave(array $data): Leave 'updated_by' => auth()->id(), ]); - // 결재 자동 생성 + 상신 - $approval = $this->createLeaveApproval($leave, $tenantId); + // 결재 자동 생성 + 상신 (선택된 결재선 전달) + $approval = $this->createLeaveApproval($leave, $tenantId, $data['approval_line_id'] ?? null); $leave->update(['approval_id' => $approval->id]); return $leave; @@ -632,7 +633,7 @@ public function getActiveEmployees(): \Illuminate\Database\Eloquent\Collection /** * 휴가신청 결재 자동 생성 + 상신 */ - private function createLeaveApproval(Leave $leave, int $tenantId): Approval + private function createLeaveApproval(Leave $leave, int $tenantId, ?int $approvalLineId = null): Approval { $approvalService = app(ApprovalService::class); @@ -646,20 +647,26 @@ private function createLeaveApproval(Leave $leave, int $tenantId): Approval throw new \RuntimeException('휴가신청 결재 양식이 등록되지 않았습니다.'); } - // 2. 기본결재선 조회 - $defaultLine = ApprovalLine::where('tenant_id', $tenantId) - ->where('is_default', true) - ->first(); + // 2. 결재선 조회: 지정된 ID 우선, 없으면 기본결재선 + $line = null; + if ($approvalLineId) { + $line = ApprovalLine::where('tenant_id', $tenantId)->find($approvalLineId); + } + if (! $line) { + $line = ApprovalLine::where('tenant_id', $tenantId) + ->where('is_default', true) + ->first(); + } - if (! $defaultLine) { - throw new \RuntimeException('기본결재선을 먼저 설정해주세요.'); + if (! $line) { + throw new \RuntimeException('결재선을 찾을 수 없습니다. 기본결재선을 설정하거나 결재선을 선택해주세요.'); } // 3. 결재 본문 생성 $body = $this->buildLeaveApprovalBody($leave, $tenantId); // 4. steps 변환 - $steps = collect($defaultLine->steps)->map(fn ($s) => [ + $steps = collect($line->steps)->map(fn ($s) => [ 'user_id' => $s['user_id'], 'step_type' => $s['step_type'] ?? $s['type'] ?? 'approval', ])->toArray(); @@ -671,7 +678,7 @@ private function createLeaveApproval(Leave $leave, int $tenantId): Approval $approval = $approvalService->createApproval([ 'form_id' => $form->id, - 'line_id' => $defaultLine->id, + 'line_id' => $line->id, 'title' => "휴가신청 - {$userName} ({$typeName} {$period})", 'body' => $body, 'content' => [ diff --git a/resources/views/hr/leaves/index.blade.php b/resources/views/hr/leaves/index.blade.php index e4710606..9aa913d3 100644 --- a/resources/views/hr/leaves/index.blade.php +++ b/resources/views/hr/leaves/index.blade.php @@ -182,7 +182,7 @@ class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus: