feat: [approval] Phase 2 결재관리 고급 기능 구현

- 보류/해제: 현재 결재자가 문서를 보류하고 해제
- 전결: 이후 모든 결재를 건너뛰고 최종 승인
- 회수 강화: 회수 사유 입력, 첫 결재자 미처리 시에만 허용
- 복사 재기안: 완료/반려/회수 문서를 복사하여 새 draft 생성
- 참조 열람 추적: 미열람/열람 필터, mark-read API
- ApprovalDelegation 모델 생성 (Phase 3 위임 대결 준비)
- 뱃지 카운트에 reference_unread 추가
This commit is contained in:
김보곤
2026-02-27 23:41:49 +09:00
parent 12c9ad620a
commit 9b96a3cad1
10 changed files with 757 additions and 24 deletions

View File

@@ -65,7 +65,7 @@ public function references(Request $request): JsonResponse
{
$result = $this->service->getReferencesForMe(
auth()->id(),
$request->only(['search', 'date_from', 'date_to']),
$request->only(['search', 'date_from', 'date_to', 'is_read']),
(int) $request->get('per_page', 15)
);
@@ -234,10 +234,10 @@ public function reject(Request $request, int $id): JsonResponse
/**
* 회수
*/
public function cancel(int $id): JsonResponse
public function cancel(Request $request, int $id): JsonResponse
{
try {
$approval = $this->service->cancel($id);
$approval = $this->service->cancel($id, $request->get('recall_reason'));
return response()->json([
'success' => true,
@@ -252,6 +252,107 @@ public function cancel(int $id): JsonResponse
}
}
/**
* 보류
*/
public function hold(Request $request, int $id): JsonResponse
{
$request->validate([
'comment' => 'required|string|max:1000',
]);
try {
$approval = $this->service->hold($id, $request->get('comment'));
return response()->json([
'success' => true,
'message' => '보류되었습니다.',
'data' => $approval,
]);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
/**
* 보류 해제
*/
public function releaseHold(int $id): JsonResponse
{
try {
$approval = $this->service->releaseHold($id);
return response()->json([
'success' => true,
'message' => '보류가 해제되었습니다.',
'data' => $approval,
]);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
/**
* 전결
*/
public function preDecide(Request $request, int $id): JsonResponse
{
try {
$approval = $this->service->preDecide($id, $request->get('comment'));
return response()->json([
'success' => true,
'message' => '전결 처리되었습니다.',
'data' => $approval,
]);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
/**
* 복사 재기안
*/
public function copyForRedraft(int $id): JsonResponse
{
try {
$approval = $this->service->copyForRedraft($id);
return response()->json([
'success' => true,
'message' => '문서가 복사되었습니다.',
'data' => $approval,
]);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
/**
* 참조 열람 추적
*/
public function markAsRead(int $id): JsonResponse
{
$this->service->markAsRead($id);
return response()->json([
'success' => true,
'message' => '열람 처리되었습니다.',
]);
}
// =========================================================================
// 유틸
// =========================================================================