feat: [leave] 휴가 신청 시 결재선 선택 기능 추가

- 휴가 신청 모달에 결재선 드롭다운 + 미리보기 UI 추가
- 선택된 결재선으로 결재 생성 (미선택 시 기본결재선 fallback)
- 휴가 목록에 결재진행 컬럼 추가 (원형 아이콘: ✓승인/✗반려/숫자대기/파랑현재)
- approval.steps.approver eager load 추가
This commit is contained in:
김보곤
2026-03-03 22:36:05 +09:00
parent 81b64f25aa
commit 511bfa3ec5
5 changed files with 114 additions and 13 deletions

View File

@@ -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 {

View File

@@ -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,
]);
}

View File

@@ -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' => [