2026-03-11 16:46:54 +09:00
|
|
|
# 결재관리 시스템 통합 계획서 — MNG 로직을 API로 통합
|
|
|
|
|
|
|
|
|
|
> **작성일**: 2026-03-11
|
2026-03-11 19:40:29 +09:00
|
|
|
> **상태**: P1~P4 완료 / P5~P6 미착수
|
2026-03-11 16:46:54 +09:00
|
|
|
> **담당**: R&D실
|
|
|
|
|
> **관련 문서**: [`phase4-approval-integration-plan.md`](./phase4-approval-integration-plan.md) (Document↔Approval 브릿지, 완료)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1. 개요
|
|
|
|
|
|
|
|
|
|
### 1.1 배경
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
MNG와 API에 이중 구현되어 있던 결재관리 시스템을 API로 통합하는 작업이다. **P1~P4는 완료되어 API가 MNG와 동등한 기능을 제공**한다.
|
|
|
|
|
|
|
|
|
|
| 항목 | MNG (관리자 패널) | API (REST API) | 비고 |
|
|
|
|
|
|------|------------------|----------------|------|
|
|
|
|
|
| 결재 흐름 | ✅ 상신/승인/반려/회수/보류/전결/복사재기안 | ✅ 동일 | P2에서 완료 |
|
|
|
|
|
| 위임 시스템 | ✅ 완전 구현 | ✅ CRUD 구현 | P4에서 완료 |
|
|
|
|
|
| 병렬 결재 | ✅ `parallel_group` | ⚠️ 컬럼 존재, 로직 미확인 | 검증 필요 |
|
|
|
|
|
| 결재자 스냅샷 | ✅ 이름/부서/직급 | ✅ 구현 완료 | P2에서 완료 |
|
|
|
|
|
| 대결 처리 | ✅ `acted_by` | ⚠️ 컬럼 존재, 위임 inbox 통합 미확인 | 검증 필요 |
|
|
|
|
|
| 반려 이력 | ✅ `rejection_history` | ✅ 구현 완료 | P2에서 완료 |
|
|
|
|
|
| 뱃지 카운트 | ✅ 4종 뱃지 | ✅ 구현 완료 | P2에서 완료 |
|
|
|
|
|
| 양식별 뷰 | ✅ 27개 Blade 파일 | ❌ (React 측 담당) | 범위 외 |
|
|
|
|
|
| Leave 연동 | ✅ 결재 완료→휴가 자동 생성 | ❌ 미구현 | **P5 미착수** |
|
|
|
|
|
| Document 동기화 | ❌ | ✅ `syncToLinkedDocument()` | API 고유 |
|
|
|
|
|
| 현황 요약 API | ❌ | ✅ `draftsSummary()`, `inboxSummary()` | API 고유 |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
MNG에서 코드브릿지엑스(tenant_id=1)만 사용하던 결재 시스템을 **모든 테넌트가 API를 통해 동일하게 사용**할 수 있도록 통합한다.
|
|
|
|
|
|
|
|
|
|
### 1.2 목표
|
|
|
|
|
|
|
|
|
|
1. **API에 MNG의 고급 결재 기능을 모두 이식** — 보류/전결/위임/병렬결재/스냅샷/뱃지
|
|
|
|
|
2. **MNG는 자체 서비스 유지** — MNG는 HTMX 기반 관리자 패널로, API 호출이 아닌 자체 서비스를 사용 (기존 동작 유지)
|
|
|
|
|
3. **React(사용자 앱)에서 API를 통해 전체 결재 기능 사용 가능**
|
|
|
|
|
4. **기존 데이터/동작에 영향 없음** — 하위 호환성 100% 유지
|
|
|
|
|
|
|
|
|
|
### 1.3 핵심 원칙
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
✅ API 모델은 MNG 모델의 상위 호환이 되어야 한다
|
|
|
|
|
✅ DB 마이그레이션은 API 프로젝트에서만 관리
|
|
|
|
|
✅ MNG의 검증된 비즈니스 로직을 API에 이식
|
|
|
|
|
✅ 기존 API 엔드포인트의 요청/응답 호환성 유지
|
|
|
|
|
❌ MNG의 Blade 뷰 시스템은 이식 대상 아님 (React에서 별도 구현)
|
|
|
|
|
❌ e-Sign(전자서명)은 이번 범위 제외 (별도 프로젝트)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 2. 현재 상태 (2026-03-11 기준)
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
### 2.1 구현 현황 요약
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
| 항목 | 수량 | 상태 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| API 라우트 | 45개 | ✅ 전체 등록 완료 |
|
|
|
|
|
| ApprovalController 메서드 | 24개 | ✅ 전체 구현 |
|
|
|
|
|
| ApprovalService public 메서드 | 48개 | ✅ 전체 구현 |
|
|
|
|
|
| FormRequest 클래스 | 6개 (Approval) + 2개 (Delegation) | ✅ 생성 완료 |
|
|
|
|
|
| TODO/FIXME 잔존 | 0건 | ✅ |
|
|
|
|
|
|
|
|
|
|
### 2.2 DB 테이블 현황
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
모든 테이블은 API 마이그레이션으로 생성 완료. API 모델에서도 활용한다.
|
|
|
|
|
|
|
|
|
|
| 테이블 | 핵심 컬럼 | DB 존재 | API 활용 | MNG 활용 |
|
|
|
|
|
|--------|----------|:-------:|:-------:|:-------:|
|
|
|
|
|
| `approvals` | `line_id`, `body`, `is_urgent`, `department_id` | ✅ | ✅ | ✅ |
|
|
|
|
|
| | `drafter_read_at`, `resubmit_count`, `rejection_history` | ✅ | ✅ | ✅ |
|
|
|
|
|
| | `recall_reason`, `parent_doc_id` | ✅ | ✅ | ✅ |
|
2026-03-11 16:46:54 +09:00
|
|
|
| | `linkable_type/id` | ✅ | ✅ | ❌ |
|
2026-03-11 19:40:29 +09:00
|
|
|
| `approval_steps` | `tenant_id`, `deleted_at` | ✅ | ✅ | ✅ |
|
|
|
|
|
| | `parallel_group`, `acted_by` | ✅ | ⚠️ | ✅ |
|
|
|
|
|
| | `approver_name/department/position` | ✅ | ✅ | ✅ |
|
|
|
|
|
| | `approval_type` | ✅ | ✅ | ✅ |
|
|
|
|
|
| `approval_forms` | `body_template` | ✅ | ✅ | ✅ |
|
|
|
|
|
| `approval_delegations` | 전체 | ✅ | ✅ | ✅ |
|
|
|
|
|
|
|
|
|
|
> **2026-03-11 추가 마이그레이션**: `approval_steps` 테이블에 `tenant_id` + `deleted_at` 컬럼 추가 (`BelongsToTenant`, `SoftDeletes` 적용)
|
|
|
|
|
|
|
|
|
|
### 2.3 API 서비스 기능 현황
|
|
|
|
|
|
|
|
|
|
| 기능 | API | MNG | 상태 |
|
|
|
|
|
|------|:---:|:---:|:----:|
|
|
|
|
|
| 기안함/결재함/참조함/완료함 조회 | ✅ | ✅ | 완료 |
|
|
|
|
|
| 문서 CRUD | ✅ | ✅ | 완료 |
|
|
|
|
|
| 상신/승인/반려/회수 | ✅ | ✅ | 완료 |
|
|
|
|
|
| 보류 / 보류 해제 | ✅ | ✅ | 완료 |
|
|
|
|
|
| 전결 (preDecide) | ✅ | ✅ | 완료 |
|
|
|
|
|
| 복사 재기안 (copyForRedraft) | ✅ | ✅ | 완료 |
|
|
|
|
|
| 뱃지 카운트 (badgeCounts) | ✅ | ✅ | 완료 |
|
|
|
|
|
| 완료함 일괄 읽음 | ✅ | ✅ | 완료 |
|
|
|
|
|
| 결재자 스냅샷 저장 | ✅ | ✅ | 완료 |
|
|
|
|
|
| 반려 후 재상신 이력 | ✅ | ✅ | 완료 |
|
|
|
|
|
| 위임 CRUD | ✅ | ✅ | 완료 |
|
|
|
|
|
| 현황 요약 (summary) | ✅ | ❌ | API 고유 |
|
|
|
|
|
| 참조 미열람 (markUnread) | ✅ | ❌ | API 고유 |
|
|
|
|
|
| Document 동기화 | ✅ | ❌ | API 고유 |
|
|
|
|
|
| **병렬 결재 (parallel_group)** | ⚠️ | ✅ | **검증 필요** |
|
|
|
|
|
| **위임 inbox 통합 (대결 처리)** | ⚠️ | ✅ | **검증 필요** |
|
|
|
|
|
| **Leave 연동** | ❌ | ✅ | **P5 미착수** |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3. 작업 범위 및 단계
|
|
|
|
|
|
|
|
|
|
### 3.1 전체 단계 요약
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
| Phase | 작업 | 상태 | 비고 |
|
|
|
|
|
|:-----:|------|:----:|------|
|
|
|
|
|
| **P1** | API 모델 확장 | ✅ 완료 | 모델 4개 수정/생성, ApprovalStep에 tenant_id+SoftDeletes 추가 |
|
|
|
|
|
| **P2** | API 서비스 — 핵심 워크플로우 이식 | ✅ 완료 | 48개 public 메서드 구현 |
|
|
|
|
|
| **P3** | API 엔드포인트 추가 | ✅ 완료 | 45개 라우트, 24개 컨트롤러 메서드, FormRequest 6개 |
|
|
|
|
|
| **P4** | 위임(Delegation) 시스템 이식 | ✅ 완료 | CRUD 구현, FormRequest 2개 |
|
|
|
|
|
| **P5** | Leave 연동 이식 | ❌ 미착수 | 결재 완료→휴가 자동 생성 |
|
|
|
|
|
| **P6** | 테스트 및 검증 | ❌ 미착수 | 병렬 결재, 위임 inbox 통합 검증 포함 |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 4. Phase 1 — API 모델 확장 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 4.1 Approval 모델 (`api/app/Models/Tenants/Approval.php`)
|
|
|
|
|
|
|
|
|
|
#### 4.1.1 상태 상수 추가
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
// 기존
|
|
|
|
|
const STATUS_DRAFT = 'draft';
|
|
|
|
|
const STATUS_PENDING = 'pending';
|
|
|
|
|
const STATUS_APPROVED = 'approved';
|
|
|
|
|
const STATUS_REJECTED = 'rejected';
|
|
|
|
|
const STATUS_CANCELLED = 'cancelled';
|
|
|
|
|
|
|
|
|
|
// 추가
|
|
|
|
|
const STATUS_ON_HOLD = 'on_hold';
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.1.2 fillable 필드 확인
|
|
|
|
|
|
|
|
|
|
현재 API 모델의 `$fillable`에 누락된 필드 추가:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
// 추가 대상 (DB에 이미 존재하는 컬럼)
|
|
|
|
|
'line_id', 'body', 'is_urgent', 'department_id',
|
|
|
|
|
'drafter_read_at', 'resubmit_count', 'rejection_history',
|
|
|
|
|
'recall_reason', 'parent_doc_id'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.1.3 casts 확장
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
// 추가
|
|
|
|
|
'rejection_history' => 'array',
|
|
|
|
|
'is_urgent' => 'boolean',
|
|
|
|
|
'drafter_read_at' => 'datetime',
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.1.4 관계 추가
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function line(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(ApprovalLine::class, 'line_id');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function parentDocument(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(Approval::class, 'parent_doc_id');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function childDocuments(): HasMany
|
|
|
|
|
{
|
|
|
|
|
return $this->hasMany(Approval::class, 'parent_doc_id');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function department(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(Department::class, 'department_id');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.1.5 헬퍼 메서드 추가 (MNG에서 이식)
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function isHoldable(): bool // pending 상태에서 보류 가능
|
|
|
|
|
public function isHoldReleasable(): bool // on_hold 상태에서 해제 가능
|
|
|
|
|
public function isCopyable(): bool // 완료/반려/회수 상태에서 복사 가능
|
|
|
|
|
public function isDeletableBy(?User $user): bool // 특정 사용자가 삭제 가능한지
|
|
|
|
|
public function getStatusColorAttribute(): string // 상태별 UI 색상
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.1.6 스코프 추가
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function scopeOnHold($query) // on_hold 상태 필터
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 4.2 ApprovalStep 모델 (`api/app/Models/Tenants/ApprovalStep.php`)
|
|
|
|
|
|
|
|
|
|
#### 4.2.1 상태 상수 추가
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
const STATUS_ON_HOLD = 'on_hold'; // 추가
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.2.2 fillable 필드 확인
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
// 추가 대상
|
|
|
|
|
'parallel_group', 'acted_by', 'approver_name',
|
|
|
|
|
'approver_department', 'approver_position', 'approval_type'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 4.2.3 관계 추가
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function actedBy(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(User::class, 'acted_by');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 4.3 ApprovalForm 모델 (`api/app/Models/Tenants/ApprovalForm.php`)
|
|
|
|
|
|
|
|
|
|
#### 4.3.1 fillable 필드 확인
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
// 추가 대상
|
|
|
|
|
'body_template'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 4.4 ApprovalDelegation 모델 신규 생성
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
파일: api/app/Models/Tenants/ApprovalDelegation.php
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
MNG의 `ApprovalDelegation` 모델을 API에 생성한다.
|
|
|
|
|
|
|
|
|
|
| 항목 | 내용 |
|
|
|
|
|
|------|------|
|
|
|
|
|
| Trait | `BelongsToTenant`, `SoftDeletes`, `Auditable` |
|
|
|
|
|
| 관계 | `delegator()`, `delegate()` (BelongsTo User) |
|
|
|
|
|
| 스코프 | `active()`, `currentlyActive()`, `forDelegator(userId)` |
|
|
|
|
|
| 메서드 | `isEffective()` — 현재 유효한 위임인지 확인 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 4.5 수정 파일 목록 (Phase 1)
|
|
|
|
|
|
|
|
|
|
| 파일 | 작업 | 유형 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| `api/app/Models/Tenants/Approval.php` | 상수/fillable/cast/관계/메서드 추가 | 수정 |
|
|
|
|
|
| `api/app/Models/Tenants/ApprovalStep.php` | 상수/fillable/관계 추가 | 수정 |
|
|
|
|
|
| `api/app/Models/Tenants/ApprovalForm.php` | fillable 추가 | 수정 |
|
|
|
|
|
| `api/app/Models/Tenants/ApprovalDelegation.php` | 신규 생성 | 신규 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 5. Phase 2 — API 서비스 핵심 워크플로우 이식 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 5.1 보류 / 보류 해제
|
|
|
|
|
|
|
|
|
|
**MNG 로직 참조**: `ApprovalService::hold()`, `releaseHold()`
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
보류 흐름:
|
|
|
|
|
1. 현재 결재자만 보류 가능 (pending 상태)
|
|
|
|
|
2. 해당 ApprovalStep.status → on_hold
|
|
|
|
|
3. Approval.status → on_hold
|
|
|
|
|
4. 보류 사유(comment) 기록
|
|
|
|
|
|
|
|
|
|
보류 해제 흐름:
|
|
|
|
|
1. 보류한 결재자만 해제 가능
|
|
|
|
|
2. ApprovalStep.status → pending
|
|
|
|
|
3. Approval.status → pending
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 구현 위치: `api/app/Services/ApprovalService.php`
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function hold(int $id, string $comment): Approval
|
|
|
|
|
public function releaseHold(int $id): Approval
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.2 전결 (Pre-Decide)
|
|
|
|
|
|
|
|
|
|
**MNG 로직 참조**: `ApprovalService::preDecide()`
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
전결 흐름:
|
|
|
|
|
1. 현재 결재자가 전결 처리
|
|
|
|
|
2. 현재 ApprovalStep → approved (approval_type = 'pre_decided')
|
|
|
|
|
3. 이후 모든 pending 단계 → skipped
|
|
|
|
|
4. Approval.status → approved
|
|
|
|
|
5. completed_at 설정
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function preDecide(int $id, ?string $comment = null): Approval
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.3 복사 재기안 (Copy for Redraft)
|
|
|
|
|
|
|
|
|
|
**MNG 로직 참조**: `ApprovalService::copyForRedraft()`
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
복사 재기안 흐름:
|
|
|
|
|
1. 완료/반려/회수된 문서만 대상
|
|
|
|
|
2. 원본 문서의 content, form_id, title 등 복사
|
|
|
|
|
3. 새 Approval 생성 (status = draft)
|
|
|
|
|
4. parent_doc_id = 원본 문서 ID
|
|
|
|
|
5. 결재선(steps)은 복사하지 않음 (새로 설정)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function copyForRedraft(int $id): Approval
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.4 결재자 스냅샷 저장
|
|
|
|
|
|
|
|
|
|
**MNG 로직 참조**: `ApprovalService::saveApprovalSteps()`
|
|
|
|
|
|
|
|
|
|
결재 단계 생성 시 결재자의 **현재 시점** 이름/부서/직급을 스냅샷으로 저장한다.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
private function createApprovalSteps(Approval $approval, array $steps): void
|
|
|
|
|
{
|
|
|
|
|
foreach ($steps as $index => $step) {
|
|
|
|
|
$user = User::find($step['approver_id']);
|
|
|
|
|
ApprovalStep::create([
|
|
|
|
|
// ... 기존 필드 ...
|
|
|
|
|
'approver_name' => $user?->name,
|
|
|
|
|
'approver_department' => $user?->department?->name,
|
|
|
|
|
'approver_position' => $user?->position_name,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.5 반려 후 재상신 이력 관리
|
|
|
|
|
|
|
|
|
|
**MNG 로직 참조**: `ApprovalService::submit()` (재상신 분기)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
재상신 흐름 (rejected → pending):
|
|
|
|
|
1. 모든 ApprovalStep.status → pending으로 초기화
|
|
|
|
|
2. rejection_history JSON에 반려 이력 추가:
|
|
|
|
|
{
|
|
|
|
|
"round": N,
|
|
|
|
|
"approver_name": "홍길동",
|
|
|
|
|
"approver_position": "팀장",
|
|
|
|
|
"comment": "반려 사유",
|
|
|
|
|
"rejected_at": "2026-03-11 14:30:00"
|
|
|
|
|
}
|
|
|
|
|
3. resubmit_count += 1
|
|
|
|
|
4. Approval.status → pending
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
기존 `submit()` 메서드에 재상신 분기를 추가한다.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.6 뱃지 카운트 API
|
|
|
|
|
|
|
|
|
|
**MNG 로직 참조**: `ApprovalService::getBadgeCounts()`
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function badgeCounts(int $userId): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'pending' => /* 현재 내 차례인 결재 대기 건수 */,
|
|
|
|
|
'draft' => /* 내 임시저장 건수 */,
|
|
|
|
|
'reference_unread' => /* 참조 미열람 건수 */,
|
|
|
|
|
'completed_unread' => /* 완료 미확인 건수 (drafter_read_at IS NULL) */,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.7 완료함 일괄 읽음
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function markCompletedAsRead(int $userId): int
|
|
|
|
|
{
|
|
|
|
|
return Approval::where('drafter_id', $userId)
|
|
|
|
|
->whereIn('status', [self::STATUS_APPROVED, self::STATUS_REJECTED, self::STATUS_CANCELLED])
|
|
|
|
|
->whereNull('drafter_read_at')
|
|
|
|
|
->update(['drafter_read_at' => now()]);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.8 회수 시 recall_reason 저장
|
|
|
|
|
|
|
|
|
|
기존 `cancel()` 메서드에 `recall_reason` 파라미터를 추가한다.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function cancel(int $id, ?string $reason = null): Approval
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5.9 수정 파일 목록 (Phase 2)
|
|
|
|
|
|
|
|
|
|
| 파일 | 작업 |
|
|
|
|
|
|------|------|
|
|
|
|
|
| `api/app/Services/ApprovalService.php` | `hold()`, `releaseHold()`, `preDecide()`, `copyForRedraft()`, `badgeCounts()`, `markCompletedAsRead()` 추가, `submit()` 재상신 분기 추가, `cancel()` reason 파라미터 추가, `createApprovalSteps()` 스냅샷 로직 추가 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 6. Phase 3 — API 엔드포인트 추가 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 6.1 신규 엔드포인트 목록
|
|
|
|
|
|
|
|
|
|
| Method | URL | 서비스 메서드 | 설명 |
|
|
|
|
|
|--------|-----|-------------|------|
|
|
|
|
|
| `POST` | `/v1/approvals/{id}/hold` | `hold()` | 보류 |
|
|
|
|
|
| `POST` | `/v1/approvals/{id}/release-hold` | `releaseHold()` | 보류 해제 |
|
|
|
|
|
| `POST` | `/v1/approvals/{id}/pre-decide` | `preDecide()` | 전결 |
|
|
|
|
|
| `POST` | `/v1/approvals/{id}/copy-for-redraft` | `copyForRedraft()` | 복사 재기안 |
|
|
|
|
|
| `GET` | `/v1/approvals/badge-counts` | `badgeCounts()` | 뱃지 카운트 |
|
|
|
|
|
| `POST` | `/v1/approvals/mark-completed-read` | `markCompletedAsRead()` | 완료 일괄 읽음 |
|
|
|
|
|
| `GET` | `/v1/approvals/completed` | `completed()` | 완료함 목록 |
|
|
|
|
|
| `GET` | `/v1/approvals/completed/summary` | `completedSummary()` | 완료함 현황 |
|
|
|
|
|
|
|
|
|
|
### 6.2 기존 엔드포인트 수정
|
|
|
|
|
|
|
|
|
|
| Method | URL | 변경 내용 |
|
|
|
|
|
|--------|-----|----------|
|
|
|
|
|
| `POST` | `/v1/approvals/{id}/cancel` | `reason` 파라미터 추가 |
|
|
|
|
|
| `POST` | `/v1/approvals/{id}/submit` | 재상신(rejected→pending) 분기 처리 |
|
|
|
|
|
| `GET` | `/v1/approvals/{id}` | 응답에 `line`, `department`, `parentDocument` 포함 |
|
|
|
|
|
|
|
|
|
|
### 6.3 FormRequest 추가
|
|
|
|
|
|
|
|
|
|
| 파일 | 용도 |
|
|
|
|
|
|------|------|
|
|
|
|
|
| `HoldRequest.php` | `comment` 필수 검증 |
|
|
|
|
|
| `PreDecideRequest.php` | `comment` 선택 검증 |
|
|
|
|
|
| `CancelRequest.php` 수정 | `reason` 선택 검증 추가 |
|
|
|
|
|
|
|
|
|
|
### 6.4 수정 파일 목록 (Phase 3)
|
|
|
|
|
|
|
|
|
|
| 파일 | 작업 | 유형 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| `api/app/Http/Controllers/Api/V1/ApprovalController.php` | 6개 메서드 추가 | 수정 |
|
|
|
|
|
| `api/routes/api/v1/hr.php` | 8개 라우트 추가 | 수정 |
|
|
|
|
|
| `api/app/Http/Requests/V1/Approval/HoldRequest.php` | 신규 | 신규 |
|
|
|
|
|
| `api/app/Http/Requests/V1/Approval/PreDecideRequest.php` | 신규 | 신규 |
|
|
|
|
|
| `api/app/Http/Requests/V1/Approval/CancelRequest.php` | reason 추가 | 수정 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 7. Phase 4 — 위임(Delegation) 시스템 이식 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 7.1 개요
|
|
|
|
|
|
|
|
|
|
결재자가 부재 시(출장, 휴가) 대리인에게 결재 권한을 위임하는 시스템이다.
|
|
|
|
|
|
|
|
|
|
### 7.2 위임 CRUD API
|
|
|
|
|
|
|
|
|
|
| Method | URL | 설명 |
|
|
|
|
|
|--------|-----|------|
|
|
|
|
|
| `GET` | `/v1/approval-delegations` | 위임 목록 |
|
|
|
|
|
| `POST` | `/v1/approval-delegations` | 위임 생성 |
|
|
|
|
|
| `GET` | `/v1/approval-delegations/{id}` | 위임 상세 |
|
|
|
|
|
| `PATCH` | `/v1/approval-delegations/{id}` | 위임 수정 |
|
|
|
|
|
| `DELETE` | `/v1/approval-delegations/{id}` | 위임 삭제 |
|
|
|
|
|
|
|
|
|
|
### 7.3 위임 적용 로직
|
|
|
|
|
|
|
|
|
|
결재함(inbox) 조회 시 위임 대상도 함께 조회한다:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
inbox 조회 흐름:
|
|
|
|
|
1. 내가 직접 결재자인 문서 조회 (기존)
|
|
|
|
|
2. + 내가 현재 유효한 위임의 대리인인 경우, 위임자의 결재 대기 문서도 조회
|
|
|
|
|
3. 대리 결재 시 acted_by = 대리인 ID, approval_type = 'delegated'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 7.4 수정 파일 목록 (Phase 4)
|
|
|
|
|
|
|
|
|
|
| 파일 | 작업 | 유형 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| `api/app/Http/Controllers/Api/V1/ApprovalDelegationController.php` | 신규 | 신규 |
|
|
|
|
|
| `api/app/Services/ApprovalDelegationService.php` | 신규 | 신규 |
|
|
|
|
|
| `api/app/Http/Requests/V1/ApprovalDelegation/*.php` | FormRequest 3개 | 신규 |
|
|
|
|
|
| `api/routes/api/v1/hr.php` | 위임 라우트 추가 | 수정 |
|
|
|
|
|
| `api/app/Services/ApprovalService.php` | `inbox()` 위임 조회 통합 | 수정 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 8. Phase 5 — Leave 연동 이식 ❌ 미착수
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 8.1 개요
|
|
|
|
|
|
|
|
|
|
MNG에서는 휴가 신청 결재가 완료되면 자동으로 Leave 레코드를 생성/승인한다.
|
|
|
|
|
|
|
|
|
|
### 8.2 연동 흐름
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
결재 승인 완료 (approval_forms.code = 'leave')
|
|
|
|
|
└─ handleApprovalCompleted()
|
|
|
|
|
├─ approval.content에서 leave_type, start_date, end_date 추출
|
|
|
|
|
├─ Leave 레코드 생성 (status = approved)
|
|
|
|
|
└─ approval_id로 연결
|
|
|
|
|
|
|
|
|
|
결재 반려/회수
|
|
|
|
|
└─ handleApprovalRejected/Cancelled()
|
|
|
|
|
└─ 연결된 Leave 있으면 상태 동기화 (cancelled)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 8.3 수정 파일 목록 (Phase 5)
|
|
|
|
|
|
|
|
|
|
| 파일 | 작업 |
|
|
|
|
|
|------|------|
|
|
|
|
|
| `api/app/Services/ApprovalService.php` | `approve()`, `reject()`, `cancel()` 후크에 Leave 동기화 추가 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 9. Phase 6 — 테스트 및 검증 ❌ 미착수
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 9.1 테스트 시나리오
|
|
|
|
|
|
|
|
|
|
| # | 시나리오 | 검증 항목 |
|
|
|
|
|
|---|---------|----------|
|
|
|
|
|
| T1 | 기본 결재 흐름 | 상신→승인→완료, 스냅샷 저장 확인 |
|
|
|
|
|
| T2 | 반려 후 재상신 | rejection_history 저장, resubmit_count 증가, 단계 초기화 |
|
|
|
|
|
| T3 | 보류/보류 해제 | on_hold 상태 전환, 보류 사유 기록 |
|
|
|
|
|
| T4 | 전결 | 이후 단계 skipped, 즉시 완료 |
|
|
|
|
|
| T5 | 회수 | recall_reason 저장, 미처리 단계 skipped |
|
|
|
|
|
| T6 | 복사 재기안 | 새 draft 생성, parent_doc_id 연결 |
|
|
|
|
|
| T7 | 위임 — 생성/조회 | CRUD 정상 동작, 기간 검증 |
|
|
|
|
|
| T8 | 위임 — 대결 처리 | inbox에 위임 문서 표시, acted_by 기록 |
|
|
|
|
|
| T9 | 뱃지 카운트 | 4종 카운트 정확성 |
|
|
|
|
|
| T10 | Leave 연동 | 휴가 결재 완료→Leave 자동 생성 |
|
|
|
|
|
| T11 | Document 동기화 | 기존 syncToLinkedDocument 정상 동작 유지 |
|
|
|
|
|
| T12 | 하위 호환성 | 기존 API 요청/응답 형식 변경 없음 확인 |
|
|
|
|
|
|
|
|
|
|
### 9.2 데이터 마이그레이션
|
|
|
|
|
|
|
|
|
|
> **주의**: DB 스키마 변경이 없으므로 데이터 마이그레이션은 불필요하다.
|
|
|
|
|
> 기존 데이터는 새 필드가 null인 상태로 정상 동작한다.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
## 10. 완료된 작업 요약
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
### 10.1 API 프로젝트 (`/home/aweso/sam/api`) — P1~P4 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
| 파일 | Phase | 상태 | 작업 내용 |
|
|
|
|
|
|------|:-----:|:----:|----------|
|
|
|
|
|
| `app/Models/Tenants/Approval.php` | P1 | ✅ | 상수/fillable/cast/관계/메서드 추가 |
|
|
|
|
|
| `app/Models/Tenants/ApprovalStep.php` | P1 | ✅ | BelongsToTenant, SoftDeletes, tenant_id 추가 |
|
|
|
|
|
| `app/Models/Tenants/ApprovalForm.php` | P1 | ✅ | ModelTrait 추가, fillable 확장 |
|
|
|
|
|
| `app/Models/Tenants/ApprovalDelegation.php` | P1 | ✅ | Auditable, ModelTrait 추가 |
|
|
|
|
|
| `app/Services/ApprovalService.php` | P2 | ✅ | 48개 public 메서드 (tenant_id 스냅샷 포함) |
|
|
|
|
|
| `app/Http/Controllers/Api/V1/ApprovalController.php` | P3 | ✅ | 24개 메서드, FormRequest 적용 |
|
|
|
|
|
| `routes/api/v1/hr.php` | P3,P4 | ✅ | 45개 라우트 등록 |
|
|
|
|
|
| `app/Http/Requests/Approval/*.php` | P3 | ✅ | 6개 (Approve, Cancel, Hold, PreDecide, DelegationStore, DelegationUpdate) |
|
|
|
|
|
| `database/migrations/2026_03_11_*` | - | ✅ | approval_steps에 tenant_id + deleted_at 추가 |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
### 10.2 MNG 프로젝트 (`/home/aweso/sam/mng`) — 최소 수정
|
2026-03-11 16:46:54 +09:00
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
| 파일 | 작업 | 상태 |
|
|
|
|
|
|------|------|:----:|
|
|
|
|
|
| `app/Models/Approvals/ApprovalStep.php` | SoftDeletes, tenant_id 추가 | ✅ |
|
|
|
|
|
| `app/Services/ApprovalService.php` | tenant_id 스냅샷 로직 추가 | ✅ |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
### 10.3 DB 마이그레이션
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
| 마이그레이션 | 대상 | 상태 |
|
|
|
|
|
|------------|------|:----:|
|
|
|
|
|
| `2026_03_11_100001_add_tenant_id_and_soft_deletes_to_approval_steps_table` | `approval_steps` | ✅ 개발/운영 배포 완료 |
|
|
|
|
|
|
|
|
|
|
### 10.4 문서
|
|
|
|
|
|
|
|
|
|
| 문서 | 경로 | 상태 |
|
|
|
|
|
|------|------|:----:|
|
|
|
|
|
| 프론트엔드 API 명세서 | `frontend/api-specs/approval-api.md` | ✅ 작성 완료 |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 11. 작업 순서 및 의존성
|
|
|
|
|
|
|
|
|
|
```
|
2026-03-11 19:40:29 +09:00
|
|
|
Phase 1: 모델 확장 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
│
|
2026-03-11 19:40:29 +09:00
|
|
|
├──→ Phase 2: 서비스 워크플로우 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
│ │
|
2026-03-11 19:40:29 +09:00
|
|
|
│ └──→ Phase 3: 엔드포인트 추가 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
│
|
2026-03-11 19:40:29 +09:00
|
|
|
├──→ Phase 4: 위임 시스템 ✅ 완료
|
2026-03-11 16:46:54 +09:00
|
|
|
│
|
2026-03-11 19:40:29 +09:00
|
|
|
└──→ Phase 5: Leave 연동 ❌ 미착수 (P2 완료 후 가능)
|
2026-03-11 16:46:54 +09:00
|
|
|
│
|
2026-03-11 19:40:29 +09:00
|
|
|
└──→ Phase 6: 테스트 ❌ 미착수 (전체 완료 후)
|
2026-03-11 16:46:54 +09:00
|
|
|
```
|
|
|
|
|
|
2026-03-11 19:40:29 +09:00
|
|
|
**남은 작업**: P5 (Leave 연동) → P6 (테스트 및 검증)
|
|
|
|
|
|
|
|
|
|
### 11.1 추가 검증 필요 항목
|
|
|
|
|
|
|
|
|
|
| 항목 | 설명 | 우선순위 |
|
|
|
|
|
|------|------|:--------:|
|
|
|
|
|
| 병렬 결재 | `parallel_group` 기반 동시 결재 로직이 API에서 동작하는지 검증 | 중간 |
|
|
|
|
|
| 위임 inbox 통합 | inbox 조회 시 위임 대상 문서가 함께 조회되는지 검증 | 중간 |
|
|
|
|
|
| 대결 처리 | 대리 결재 시 `acted_by`, `approval_type='delegated'` 기록 여부 | 중간 |
|
2026-03-11 16:46:54 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 12. 위험 요소 및 대응
|
|
|
|
|
|
|
|
|
|
| 위험 | 영향도 | 대응 |
|
|
|
|
|
|------|:------:|------|
|
|
|
|
|
| API 기존 엔드포인트 호환성 깨짐 | 높음 | 기존 필드 제거 금지, 신규 필드만 추가 (nullable) |
|
|
|
|
|
| MNG와 API 모델 divergence 심화 | 중간 | MNG 모델은 그대로 유지, API만 확장 |
|
|
|
|
|
| 위임 + 결재함 조회 성능 저하 | 중간 | 위임 조회를 LEFT JOIN으로 최적화, 인덱스 확인 |
|
|
|
|
|
| 반려 후 재상신 시 데이터 무결성 | 높음 | DB 트랜잭션으로 감싸기, rejection_history append-only |
|
|
|
|
|
| 전결 시 Leave 연동 누락 | 중간 | preDecide()에도 completion 후크 추가 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 13. 제외 범위
|
|
|
|
|
|
|
|
|
|
| 항목 | 사유 |
|
|
|
|
|
|------|------|
|
|
|
|
|
| e-Sign (전자서명) | 별도 프로젝트로 관리 (`features/esign/`) |
|
|
|
|
|
| MNG Blade 뷰 이식 | React에서 별도 구현 (프론트엔드 범위) |
|
|
|
|
|
| MNG 서비스 수정 | MNG는 독립 동작 유지 |
|
|
|
|
|
| React UI 구현 | 별도 프론트엔드 작업으로 분리 |
|
|
|
|
|
| Swagger 문서 | 엔드포인트 추가 후 별도 작업 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 관련 문서
|
|
|
|
|
|
|
|
|
|
| 문서 | 경로 | 설명 |
|
|
|
|
|
|------|------|------|
|
2026-03-11 19:40:29 +09:00
|
|
|
| 프론트엔드 API 명세서 | `frontend/api-specs/approval-api.md` | 28개 엔드포인트 전체 명세 |
|
2026-03-11 16:46:54 +09:00
|
|
|
| Document↔Approval 브릿지 | `dev/dev_plans/phase4-approval-integration-plan.md` | 완료된 Phase 4.2 작업 |
|
|
|
|
|
| DB 스키마 (HR) | `system/database/hr.md` | 인사 관련 테이블 구조 |
|
|
|
|
|
| DB 스키마 (문서) | `system/database/documents.md` | 문서/전자서명 테이블 구조 |
|
|
|
|
|
| API 개발 규칙 | `dev/standards/api-rules.md` | Service-First 패턴 |
|
|
|
|
|
| options 컬럼 정책 | `dev/standards/options-column-policy.md` | JSON 컬럼 규칙 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**최종 업데이트**: 2026-03-11
|