Files
sam-docs/sam/docs/features/approvals/README.md
김보곤 ec3abc1a85 docs: [approvals] 결재관리 DB 변경사항 및 API 모델 동기화 현황 문서 작성
- 2026-02-27 ~ 03-05 마이그레이션 15개 변경 타임라인 정리
- API/MNG 모델 $fillable/$casts 동기화 비교표 작성
- API 모델 미반영으로 인한 잠재적 오류 영향 분석
2026-03-09 20:34:11 +09:00

299 lines
12 KiB
Markdown

# 결재관리 시스템
> **작성일**: 2026-02-28
> **상태**: Phase 2 구현 완료
> **프로젝트**: SAM MNG (관리자 웹)
> **우선순위**: 🔴 필수
---
## 1. 개요
### 1.1 목적
SAM MNG 전자결재 시스템. 기안부터 최종 승인, 반려, 회수, 보류, 전결, 참조까지 기업 결재 프로세스를 디지털화한다.
### 1.2 문서 구조
| 문서 | 설명 |
|------|------|
| **README.md** (이 문서) | 시스템 전체 개요, 아키텍처, 상태 관리 |
| [form-types.md](form-types.md) | 양식별 필드/JSON 구조/인터랙션 기술 명세 |
| [workflows.md](workflows.md) | 상세 워크플로우 (승인/반려/회수/보류/전결/복사재기안) |
| [api-reference.md](api-reference.md) | API 엔드포인트 명세 |
| [ui-screens.md](ui-screens.md) | 화면별 UI 구성 및 동작 |
| [db-changes-and-model-sync.md](db-changes-and-model-sync.md) | DB 변경사항 및 API/MNG 모델 동기화 현황 |
### 1.3 구현 현황
| Phase | 범위 | 상태 |
|-------|------|------|
| **Phase 1** | 순차결재, 기안/상신/승인/반려/회수 | ✅ 완료 |
| **Phase 2** | 보류/해제, 전결, 참조 열람 추적, 복사 재기안 | ✅ 완료 |
| **Phase 3** | 병렬결재, 위임(대결), 알림 | 미착수 |
| **Phase 4** | ERP 연동, 결재 통계, 관리자 설정 | 미착수 |
---
## 2. 아키텍처
### 2.1 기술 스택
| 계층 | 기술 | 설명 |
|------|------|------|
| 뷰 | Blade + HTMX + Alpine.js | 동적 UI, 부분 렌더링 |
| API | Laravel Controller + Service | JSON API (내부용) |
| 모델 | Eloquent ORM | Multi-tenant 스코프 |
| DB | MySQL 8.0 | API 프로젝트에서 마이그레이션 관리 |
### 2.2 프로젝트 분리
```
API (/home/aweso/sam/api)
├── database/migrations/ ← 모든 결재 테이블 마이그레이션
MNG (/home/aweso/sam/mng)
├── app/Models/Approvals/ ← 모델 (Approval, ApprovalStep, ApprovalForm, ApprovalLine, ApprovalDelegation)
├── app/Services/ ← ApprovalService (비즈니스 로직)
├── app/Http/Controllers/ ← ApprovalController (웹), ApprovalApiController (API)
├── resources/views/approvals/ ← Blade 뷰
└── routes/ ← 웹 라우트 + API 라우트
```
### 2.3 핵심 클래스
```
ApprovalService
├── 목록 조회: getMyDrafts(), getPendingForMe(), getCompletedByMe(), getReferencesForMe()
├── CRUD: createApproval(), updateApproval(), deleteApproval(), getApproval()
├── 워크플로우: submit(), approve(), reject(), cancel(), hold(), releaseHold(), preDecide(), copyForRedraft()
├── 참조: markAsRead()
└── 유틸: getBadgeCounts(), getApprovalLines(), getApprovalForms(), saveApprovalSteps()
```
---
## 3. 데이터베이스
### 3.1 테이블 관계
```
approval_forms (결재 양식)
│ 1:N
approvals (결재 문서)
│ 1:N │ N:1 (self)
▼ ▼
approval_steps (결재 단계) approvals (parent_doc_id → 원본 문서)
approval_lines (결재선 템플릿) ← approvals.line_id 참조
approval_delegations (위임 설정) ← Phase 3 준비
```
### 3.2 approvals (결재 문서)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `tenant_id` | BIGINT | 테넌트 격리 |
| `document_number` | VARCHAR | `APR-YYMMDD-001` 형식 |
| `form_id` | BIGINT FK | 양식 |
| `line_id` | BIGINT FK NULL | 결재선 템플릿 |
| `title` | VARCHAR(200) | 제목 |
| `content` | JSON | 양식 필드 데이터 |
| `body` | TEXT NULL | 본문 |
| `status` | VARCHAR(20) | 문서 상태 (6가지) |
| `is_urgent` | BOOLEAN | 긴급 여부 |
| `drafter_id` | BIGINT FK | 기안자 |
| `department_id` | BIGINT FK NULL | 기안 부서 |
| `current_step` | INT | 현재 결재 단계 번호 |
| `drafted_at` | TIMESTAMP NULL | 상신 일시 |
| `completed_at` | TIMESTAMP NULL | 완료 일시 |
| `recall_reason` | TEXT NULL | 회수 사유 |
| `parent_doc_id` | BIGINT FK NULL | 재기안 원본 문서 |
| `attachments` | JSON NULL | 첨부파일 |
### 3.3 approval_steps (결재 단계)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `approval_id` | BIGINT FK | 결재 문서 |
| `step_order` | INT | 순서 (1, 2, 3...) |
| `step_type` | VARCHAR | `approval`, `agreement`, `reference` |
| `parallel_group` | INT NULL | 병렬 그룹 (Phase 3) |
| `approver_id` | BIGINT FK | 결재자 |
| `acted_by` | BIGINT FK NULL | 실제 처리자 (대결 시) |
| `approver_name` | VARCHAR | 결재자명 스냅샷 |
| `approver_department` | VARCHAR | 부서 스냅샷 |
| `approver_position` | VARCHAR | 직급 스냅샷 |
| `status` | VARCHAR(20) | 단계 상태 (5가지) |
| `approval_type` | VARCHAR(20) | `normal`, `pre_decided`, `delegated` |
| `comment` | TEXT NULL | 결재 의견 |
| `acted_at` | TIMESTAMP NULL | 처리 일시 |
| `is_read` | BOOLEAN | 참조 열람 여부 |
| `read_at` | TIMESTAMP NULL | 열람 일시 |
### 3.4 approval_delegations (위임 설정, Phase 3)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `tenant_id` | BIGINT FK | |
| `delegator_id` | BIGINT FK | 위임자 |
| `delegate_id` | BIGINT FK | 대리인 |
| `start_date` | DATE | 위임 시작일 |
| `end_date` | DATE | 위임 종료일 |
| `form_ids` | JSON NULL | 대상 양식 (NULL=전체) |
| `notify_delegator` | BOOLEAN | 대결 시 보고 여부 |
| `is_active` | BOOLEAN | 활성 여부 |
| `reason` | VARCHAR(200) | 위임 사유 |
---
## 4. 상태 관리
### 4.1 문서 상태 (6가지)
| 상태 | 코드 | 라벨 | 색상 | 설명 |
|------|------|------|------|------|
| 임시저장 | `draft` | 임시저장 | gray | 작성 중, 미상신 |
| 진행 | `pending` | 진행 | blue | 결재선 순환 중 |
| 완료 | `approved` | 완료 | green | 최종 승인 |
| 반려 | `rejected` | 반려 | red | 결재자가 반려 |
| 회수 | `cancelled` | 회수 | yellow | 기안자가 회수 |
| 보류 | `on_hold` | 보류 | amber | 결재자가 보류 |
### 4.2 단계 상태 (5가지)
| 상태 | 코드 | 라벨 | 아이콘 | 설명 |
|------|------|------|--------|------|
| 대기 | `pending` | 대기 | 숫자 | 차례 아직 아님 |
| 승인 | `approved` | 승인 | ✓ (녹색) | 승인 완료 |
| 반려 | `rejected` | 반려 | ✗ (적색) | 반려 |
| 건너뜀 | `skipped` | 건너뜀 | — (회색) | 전결/회수로 소멸 |
| 보류 | `on_hold` | 보류 | ⏸ (노란) | 보류 중 |
### 4.3 결재 유형 (approval_type)
| 유형 | 코드 | 아이콘 | 설명 |
|------|------|--------|------|
| 일반결재 | `normal` | ✓ | 기본 승인 |
| 전결 | `pre_decided` | ⚡ (남색) | 이후 단계 모두 건너뛰고 즉시 완료 |
| 대결 | `delegated` | — | 대리인이 처리 (Phase 3) |
### 4.4 참여자 역할 (step_type)
| 역할 | 코드 | 의사결정 | 설명 |
|------|------|---------|------|
| 결재 | `approval` | ✅ 있음 | 승인/반려/보류/전결 가능 |
| 합의 | `agreement` | ✅ 있음 | 타부서 동의 (승인/반려 가능) |
| 참조 | `reference` | ❌ 없음 | 열람만 가능, 열람 추적 |
### 4.5 상태 전이 다이어그램
```
┌─────────────────────────────┐
│ │
┌────────┐ submit() │ ┌─────────┐ │
│ draft │────────────→│ │ pending │ │
└────────┘ │ └────┬────┘ │
▲ │ │ │
│ │ ┌────┼─────────┬───────┐ │
│ (수정 후 재상신) │ │ │ │ │ │
│ │ │ approve() reject() hold()│
│ │ │ │ │ │ │
│ │ │ ▼ ▼ ▼ │
│ │ │ 다음 step rejected on_hold│
│ │ │ 또는 │ │ │
│ │ │ approved │ releaseHold()
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ └────┼────────┼───────┘ │
│ │ │ │ │
│ │ preDecide() │ │
│ │ → approved │ │
│ │ │ │ cancel() │
│ │ │ │ │ │
│ │ ▼ │ ▼ │
│ │ ┌─────────┐ │ ┌──────────┐
│ │ │approved │ │ │cancelled │
│ │ └─────────┘ │ └──────────┘
│ │ │ │ │
│ │ │ │ │
│ │ copyForRedraft() │
│ │ │ │ │
└───────────────────┼───────┴────────┘ │
(새 draft 생성) │ │
│ copyForRedraft() │
│◀──────────────────────┘
└─────────────────────────────┘
```
---
## 5. 권한 매트릭스
### 5.1 누가 무엇을 할 수 있는가
| 액션 | 대상자 | 조건 |
|------|--------|------|
| **기안 작성** | 모든 사용자 | — |
| **수정** | 기안자 | `draft` 또는 `rejected` |
| **삭제** | 기안자 | `draft`만 |
| **상신** | 기안자 | `draft` 또는 `rejected`, 결재선 1명 이상 |
| **승인** | 현재 결재자 | `pending`, 자신이 현재 차례 |
| **반려** | 현재 결재자 | `pending`, 사유 필수 |
| **보류** | 현재 결재자 | `pending`, 사유 필수 |
| **보류 해제** | 보류한 결재자 | `on_hold`, 자신이 보류한 건 |
| **전결** | 현재 결재자 | `pending`, 이후 모든 단계 건너뜀 |
| **회수** | 기안자 | `pending` 또는 `on_hold`, 첫 결재자 미처리 |
| **복사 재기안** | 기안자 | `approved`, `rejected`, `cancelled` |
| **참조 열람** | 참조자 | `reference` step 보유 |
### 5.2 회수 가능 조건 상세
```
회수(cancel) 가능 여부 판단:
1. 문서 상태가 pending 또는 on_hold인가? → 아니면 불가
2. 요청자가 기안자(drafter_id)인가? → 아니면 불가
3. 첫 번째 결재자(approval/agreement)의 상태가 pending 또는 on_hold인가?
→ 이미 approved/rejected이면 불가 (첫 결재자가 이미 처리)
```
---
## 6. 메뉴 구조
```
결재관리
├── 기안함 /approval-mgmt/drafts ← 내가 기안한 문서
├── 결재 대기함 /approval-mgmt/pending ← 내가 결재해야 할 문서
├── 처리 완료함 /approval-mgmt/completed ← 내가 결재한 문서
└── 참조함 /approval-mgmt/references ← 참조 문서 (열람 추적)
```
### 추가 페이지
| URL | 설명 |
|-----|------|
| `/approval-mgmt/create` | 기안 작성 |
| `/approval-mgmt/{id}` | 상세 조회 |
| `/approval-mgmt/{id}/edit` | 기안 수정 |
---
## 7. 관련 문서
- [결재 양식 기술 명세](form-types.md) — 양식별 필드, JSON 구조, 인터랙션
- [결재관리 워크플로우 상세](workflows.md) — 각 동작의 상세 흐름
- [API 명세](api-reference.md) — 엔드포인트 목록 및 요청/응답 예시
- [UI 화면 구성](ui-screens.md) — 화면별 UI 요소 및 동작
- [기획서 원본](../../plans/approval-management-system-plan.md) — Phase 1~4 전체 기획
---
**최종 업데이트**: 2026-03-06