diff --git a/app/Http/Controllers/Api/Admin/DocumentApiController.php b/app/Http/Controllers/Api/Admin/DocumentApiController.php index 5f61a910..e5ee7d5a 100644 --- a/app/Http/Controllers/Api/Admin/DocumentApiController.php +++ b/app/Http/Controllers/Api/Admin/DocumentApiController.php @@ -215,6 +215,151 @@ public function destroy(int $id): JsonResponse ]); } + /** + * 결재 제출 (DRAFT → PENDING) + */ + public function submit(int $id): JsonResponse + { + $tenantId = session('selected_tenant_id'); + $userId = auth()->id(); + + $document = Document::where('tenant_id', $tenantId)->findOrFail($id); + + if ($document->status !== Document::STATUS_DRAFT && $document->status !== Document::STATUS_REJECTED) { + return response()->json([ + 'success' => false, + 'message' => '작성중 또는 반려 상태의 문서만 제출할 수 있습니다.', + ], 422); + } + + $document->update([ + 'status' => Document::STATUS_PENDING, + 'submitted_at' => now(), + 'updated_by' => $userId, + ]); + + // 결재라인 상태 초기화 (반려 후 재제출 시) + $document->approvals()->update([ + 'status' => DocumentApproval::STATUS_PENDING, + 'comment' => null, + 'acted_at' => null, + ]); + + return response()->json([ + 'success' => true, + 'message' => '결재가 제출되었습니다.', + 'data' => $document->fresh(['approvals']), + ]); + } + + /** + * 결재 승인 (단계별) + */ + public function approve(Request $request, int $id): JsonResponse + { + $tenantId = session('selected_tenant_id'); + $userId = auth()->id(); + + $document = Document::where('tenant_id', $tenantId)->findOrFail($id); + + if ($document->status !== Document::STATUS_PENDING) { + return response()->json([ + 'success' => false, + 'message' => '결재중 상태의 문서만 승인할 수 있습니다.', + ], 422); + } + + // 현재 단계의 미처리 결재 찾기 + $pendingApproval = $document->approvals() + ->where('status', DocumentApproval::STATUS_PENDING) + ->orderBy('step') + ->first(); + + if (! $pendingApproval) { + return response()->json([ + 'success' => false, + 'message' => '승인 대기 중인 결재 단계가 없습니다.', + ], 422); + } + + $pendingApproval->update([ + 'user_id' => $userId, + 'status' => DocumentApproval::STATUS_APPROVED, + 'comment' => $request->input('comment'), + 'acted_at' => now(), + 'updated_by' => $userId, + ]); + + // 모든 결재가 완료되었는지 확인 + $remainingPending = $document->approvals() + ->where('status', DocumentApproval::STATUS_PENDING) + ->count(); + + if ($remainingPending === 0) { + $document->update([ + 'status' => Document::STATUS_APPROVED, + 'completed_at' => now(), + 'updated_by' => $userId, + ]); + } + + return response()->json([ + 'success' => true, + 'message' => $remainingPending === 0 ? '최종 승인되었습니다.' : '승인되었습니다. (다음 단계 대기)', + 'data' => $document->fresh(['approvals']), + ]); + } + + /** + * 결재 반려 + */ + public function reject(Request $request, int $id): JsonResponse + { + $tenantId = session('selected_tenant_id'); + $userId = auth()->id(); + + $request->validate([ + 'comment' => 'required|string|max:500', + ]); + + $document = Document::where('tenant_id', $tenantId)->findOrFail($id); + + if ($document->status !== Document::STATUS_PENDING) { + return response()->json([ + 'success' => false, + 'message' => '결재중 상태의 문서만 반려할 수 있습니다.', + ], 422); + } + + // 현재 단계 결재에 반려 기록 + $pendingApproval = $document->approvals() + ->where('status', DocumentApproval::STATUS_PENDING) + ->orderBy('step') + ->first(); + + if ($pendingApproval) { + $pendingApproval->update([ + 'user_id' => $userId, + 'status' => DocumentApproval::STATUS_REJECTED, + 'comment' => $request->input('comment'), + 'acted_at' => now(), + 'updated_by' => $userId, + ]); + } + + $document->update([ + 'status' => Document::STATUS_REJECTED, + 'completed_at' => now(), + 'updated_by' => $userId, + ]); + + return response()->json([ + 'success' => true, + 'message' => '문서가 반려되었습니다.', + 'data' => $document->fresh(['approvals']), + ]); + } + /** * 문서 데이터 저장 (기본필드 + 섹션 테이블 데이터) */ diff --git a/resources/views/documents/edit.blade.php b/resources/views/documents/edit.blade.php index 33e07518..96c9af86 100644 --- a/resources/views/documents/edit.blade.php +++ b/resources/views/documents/edit.blade.php @@ -320,8 +320,14 @@ class="px-6 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover: + @if(!$isCreate && $document && $document->canEdit()) + + @endif @endif @@ -418,5 +424,32 @@ class="px-6 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg- }); }); }); + +@if(!$isCreate && $document) +window.submitForApproval = function() { + if (!confirm('결재를 제출하시겠습니까? 제출 후에는 수정이 불가합니다.')) return; + + fetch('/api/admin/documents/{{ $document->id }}/submit', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, + } + }) + .then(response => response.json()) + .then(result => { + if (result.success) { + showToast(result.message, 'success'); + window.location.href = '/documents/{{ $document->id }}'; + } else { + showToast(result.message || '오류가 발생했습니다.', 'error'); + } + }) + .catch(error => { + console.error('Submit error:', error); + showToast('결재 제출 중 오류가 발생했습니다.', 'error'); + }); +}; +@endif @endpush \ No newline at end of file diff --git a/resources/views/documents/show.blade.php b/resources/views/documents/show.blade.php index 5291b174..f5335e13 100644 --- a/resources/views/documents/show.blade.php +++ b/resources/views/documents/show.blade.php @@ -10,7 +10,7 @@
{{ $approval->step_name }}
- @if($approval->approved_at) -{{ $approval->approved_at->format('Y-m-d H:i') }}
+{{ $approval->user->name ?? '미지정' }}
+ @if($approval->acted_at) +{{ $approval->acted_at->format('Y-m-d H:i') }}
@endif @if($approval->comment){{ $approval->comment }}
@@ -192,4 +221,103 @@ class="text-sm text-blue-600 hover:text-blue-800">