diff --git a/app/Http/Controllers/ApprovalController.php b/app/Http/Controllers/ApprovalController.php index d2d8d01f..c9ed3a89 100644 --- a/app/Http/Controllers/ApprovalController.php +++ b/app/Http/Controllers/ApprovalController.php @@ -5,6 +5,7 @@ use App\Models\Finance\BankAccount; use App\Models\Finance\CorporateCard; use App\Services\ApprovalService; +use App\Services\HR\LeaveService; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\View\View; @@ -39,8 +40,9 @@ public function create(Request $request): View|Response $forms = $this->service->getApprovalForms(); $lines = $this->service->getApprovalLines(); [$cards, $accounts] = $this->getCardAndAccountData(); + $employees = app(LeaveService::class)->getActiveEmployees(); - return view('approvals.create', compact('forms', 'lines', 'cards', 'accounts')); + return view('approvals.create', compact('forms', 'lines', 'cards', 'accounts', 'employees')); } /** diff --git a/app/Services/ApprovalService.php b/app/Services/ApprovalService.php index 6f795135..7fe77405 100644 --- a/app/Services/ApprovalService.php +++ b/app/Services/ApprovalService.php @@ -801,6 +801,39 @@ public function markCompletedAsRead(int $userId): int // Private 헬퍼 // ========================================================================= + /** + * 기안함에서 직접 올린 결재 → Leave 레코드 자동 생성 + */ + private function createLeaveFromApproval(Approval $approval): \App\Models\HR\Leave + { + $content = $approval->content; + $leaveType = $content['leave_type']; + $leaveService = app(\App\Services\HR\LeaveService::class); + + // 사유서는 days=0, 그 외는 자동 계산 + if (in_array($leaveType, \App\Models\HR\Leave::REASON_REPORT_TYPES)) { + $days = 0; + } else { + $days = $leaveService->calculateDays( + $leaveType, $content['start_date'], $content['end_date'] + ); + } + + return \App\Models\HR\Leave::create([ + 'tenant_id' => $approval->tenant_id, + 'user_id' => $content['user_id'] ?? $approval->drafter_id, + 'leave_type' => $leaveType, + 'start_date' => $content['start_date'], + 'end_date' => $content['end_date'], + 'days' => $days, + 'reason' => $content['reason'] ?? null, + 'status' => 'pending', + 'approval_id' => $approval->id, + 'created_by' => $approval->drafter_id, + 'updated_by' => $approval->drafter_id, + ]); + } + /** * 휴가/근태신청/사유서 관련 결재 양식인지 확인 */ @@ -819,6 +852,12 @@ private function handleApprovalCompleted(Approval $approval): void } $leave = \App\Models\HR\Leave::where('approval_id', $approval->id)->first(); + + // 기안함에서 직접 올린 경우: Leave 레코드 자동 생성 + if (! $leave && ! empty($approval->content['leave_type'])) { + $leave = $this->createLeaveFromApproval($approval); + } + if ($leave && $leave->status === 'pending') { app(\App\Services\HR\LeaveService::class)->approveByApproval($leave, $approval); } diff --git a/resources/views/approvals/create.blade.php b/resources/views/approvals/create.blade.php index 55d5e0bc..7f4533da 100644 --- a/resources/views/approvals/create.blade.php +++ b/resources/views/approvals/create.blade.php @@ -90,6 +90,11 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-
+ {{-- 휴가/근태신청/사유서 전용 폼 --}} + @include('approvals.partials._leave-form', [ + 'employees' => $employees ?? collect(), + ]) + {{-- 지출결의서 전용 폼 --}} @include('approvals.partials._expense-form', [ 'initialData' => [], @@ -211,6 +216,84 @@ class="p-1 text-gray-400 hover:text-gray-600 transition"> const formCodes = @json($forms->pluck('code', 'id')); const linesData = @json($lines); let isExpenseForm = false; +let isLeaveForm = false; + +// 양식코드별 표시할 유형 목록 +const leaveTypesByFormCode = { + leave: [ + { value: 'annual', label: '연차' }, + { value: 'half_am', label: '오전반차' }, + { value: 'half_pm', label: '오후반차' }, + { value: 'sick', label: '병가' }, + { value: 'family', label: '경조사' }, + { value: 'maternity', label: '출산' }, + { value: 'parental', label: '육아' }, + ], + attendance_request: [ + { value: 'business_trip', label: '출장' }, + { value: 'remote', label: '재택근무' }, + { value: 'field_work', label: '외근' }, + { value: 'early_leave', label: '조퇴' }, + ], + reason_report: [ + { value: 'late_reason', label: '지각사유서' }, + { value: 'absent_reason', label: '결근사유서' }, + ], +}; + +function populateLeaveTypes(formCode) { + const select = document.getElementById('leave-type'); + select.innerHTML = ''; + const types = leaveTypesByFormCode[formCode] || []; + types.forEach(t => { + const opt = document.createElement('option'); + opt.value = t.value; + opt.textContent = t.label; + select.appendChild(opt); + }); +} + +function getLeaveFormData() { + return { + user_id: parseInt(document.getElementById('leave-user-id').value), + leave_type: document.getElementById('leave-type').value, + start_date: document.getElementById('leave-start-date').value, + end_date: document.getElementById('leave-end-date').value, + reason: document.getElementById('leave-reason').value.trim() || null, + }; +} + +function buildLeaveBody(data) { + const typeSelect = document.getElementById('leave-type'); + const userSelect = document.getElementById('leave-user-id'); + const typeName = typeSelect.options[typeSelect.selectedIndex]?.text || data.leave_type; + const userName = userSelect.options[userSelect.selectedIndex]?.text || ''; + + const rows = [ + ['신청자', userName], + ['유형', typeName], + ]; + + if (data.start_date === data.end_date) { + rows.push(['대상일', data.start_date]); + } else { + rows.push(['기간', data.start_date + ' ~ ' + data.end_date]); + } + + if (data.reason) { + rows.push(['사유', data.reason]); + } + + let html = '아래와 같이 신청합니다.
'; + html += '| ' + + escapeHtml(label) + ' | ' + + escapeHtml(value) + ' |
|---|