From 6d042f5bfda18c736cc52c0cf21ee36cd24d1247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 6 Mar 2026 13:33:10 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[approvals]=20=ED=92=88=EC=9D=98?= =?UTF-8?q?=EC=84=9C=205=EC=A2=85=20=EC=83=81=EC=84=B8=20=EC=8A=A4?= =?UTF-8?q?=ED=8E=99=20=EB=B0=8F=202=EB=8B=A8=EA=B3=84=20=EC=96=91?= =?UTF-8?q?=EC=8B=9D=20=EC=84=A0=ED=83=9D=20UI=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 품의서 5종 공통 Alpine.js 컴포넌트 구조 문서화 (섹션 9) - 지출/계약체결/구매/출장/비용정산 품의서 Content JSON 스펙 (섹션 10~14) - 2단계 양식 선택 UI 구조 문서화 (분류→양식 드롭다운) - 14종 양식 설명 카드 기능 문서화 - 파일 구조에 _purchase-request-form/show 추가 - ApprovalForm 카테고리 DB/UI 분류 구분 설명 - 조회 흐름에 pr_ prefix 분기 로직 추가 --- sam/docs/features/approvals/form-types.md | 442 ++++++++++++++++++++-- 1 file changed, 415 insertions(+), 27 deletions(-) diff --git a/sam/docs/features/approvals/form-types.md b/sam/docs/features/approvals/form-types.md index 5ba9cc0..3242b78 100644 --- a/sam/docs/features/approvals/form-types.md +++ b/sam/docs/features/approvals/form-types.md @@ -14,14 +14,22 @@ SAM MNG 결재관리의 **기안함 양식** 기술 명세. 각 양식의 필드 ### 1.2 양식 목록 -| 코드 | 양식명 | 카테고리 | Blade 파일 | 설명 | -|------|--------|---------|------------|------| -| `leave` | 휴가신청 | request | `_leave-form.blade.php` | 연차, 휴가, 근태 신청 | -| `expense` | 지출결의서 | expense | `_expense-form.blade.php` | 법인카드/송금/자동이체 지출 | -| `employment_cert` | 재직증명서 | certificate | `_certificate-form.blade.php` | 재직 증명 발급 | -| `career_cert` | 경력증명서 | certificate | `_career-cert-form.blade.php` | 경력 증명 발급 | -| `appointment_cert` | 위촉증명서 | certificate | `_appointment-cert-form.blade.php` | 위촉/임명 증명 발급 | -| `resignation` | 사직서 | certificate | `_resignation-form.blade.php` | 퇴직 서류 | +| 코드 | 양식명 | 분류 | Blade 파일 | 설명 | +|------|--------|------|------------|------| +| `BUSINESS_DRAFT` | 업무기안서 | 일반 | (body 편집기) | 일반 업무 보고·요청 | +| `leave` | 휴가신청 | 인사/근태 | `_leave-form.blade.php` | 연차, 휴가, 근태 신청 | +| `attendance_request` | 근태신청 | 인사/근태 | `_leave-form.blade.php` | 외근, 출장, 조퇴 등 | +| `reason_report` | 사유서 | 인사/근태 | `_leave-form.blade.php` | 지각, 결근 등 사유 소명 | +| `resignation` | 사직서 | 인사/근태 | `_resignation-form.blade.php` | 퇴직 서류 | +| `employment_cert` | 재직증명서 | 증명서 | `_certificate-form.blade.php` | 재직 증명 발급 (PDF) | +| `career_cert` | 경력증명서 | 증명서 | `_career-cert-form.blade.php` | 경력 증명 발급 (PDF) | +| `appointment_cert` | 위촉증명서 | 증명서 | `_appointment-cert-form.blade.php` | 위촉/임명 증명 발급 (PDF) | +| `pr_expense` | 지출품의서 | 품의 | `_purchase-request-form.blade.php` | 지출 전 사전 승인 | +| `pr_contract` | 계약체결품의서 | 품의 | `_purchase-request-form.blade.php` | 계약 체결 전 승인 | +| `pr_purchase` | 구매품의서 | 품의 | `_purchase-request-form.blade.php` | 물품 구매 전 승인 | +| `pr_trip` | 출장품의서 | 품의 | `_purchase-request-form.blade.php` | 출장 계획 승인 | +| `pr_settlement` | 비용정산품의서 | 품의 | `_purchase-request-form.blade.php` | 비용 정산 승인 | +| `expense` | 지출결의서 | 재무 | `_expense-form.blade.php` | 법인카드/송금/자동이체 지출 | ### 1.3 공통 구조 @@ -39,17 +47,67 @@ getFormData() → JSON content 생성 ApprovalService::createApproval() → Approval.content (JSON 컬럼) 저장 ``` -### 1.4 파일 구조 +### 1.4 양식 선택 UI (2단계 분류 + 설명 카드) + +양식 선택은 **2단계 드롭다운 + 설명 카드** 레이아웃으로 구성된다. + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 양식 * │ +│ ┌──── 30% ────────┐ ┌─────────────── 70% ───────────────────────────┐ │ +│ │ 📋 품의 ▼ │ │ ┌─────────────────────────────────────────┐ │ │ +│ │ │ │ │ 📋 지출품의서 │ │ │ +│ │ 지출품의서 ▼ │ │ │ 지출이 발생하기 전 사전 승인을 받는 │ │ │ +│ │ │ │ │ 문서입니다. 예산 범위 내에서 지출 항목과 │ │ │ +│ │ │ │ │ 금액을 기재하여 사전에 승락을 받습니다. │ │ │ +│ └──────────────────┘ │ └─────────────────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +#### 1단계: 분류 선택 (`form_category`) + +| 분류 | 아이콘 | 포함 양식 | +|------|--------|----------| +| 일반 | 📄 | 업무기안서 | +| 인사/근태 | 👤 | 휴가신청, 근태신청, 사유서, 사직서 | +| 증명서 | 📜 | 재직증명서, 경력증명서, 위촉증명서 | +| 품의 | 📋 | 지출품의서, 계약체결품의서, 구매품의서, 출장품의서, 비용정산품의서 | +| 재무 | 💰 | 지출결의서 | + +#### 2단계: 양식 선택 (`form_id`) + +- 1단계 분류 선택 시 해당 분류에 속하지 않는 양식은 `display:none` + `disabled` +- 분류 내 첫 번째 양식 자동 선택 + +#### 설명 카드 (`formDescriptions`) + +- 양식 선택 시 우측에 해당 양식의 아이콘/제목/설명 텍스트 표시 +- 14종 전체 양식에 대한 설명 정의 (create/edit 공통) +- 색상: 양식별 Tailwind 테마 색상 (`border-*-200 bg-*-50`) + +#### 핵심 JavaScript 함수 + +| 함수 | 설명 | +|------|------| +| `buildCategoryOptions()` | 사용 가능한 카테고리만 `form_category` 옵션으로 생성 | +| `filterFormsByCategory(cat)` | 선택된 분류 외 양식 옵션 숨김/비활성화 | +| `selectCategoryByFormId(formId)` | formId로 카테고리 역산하여 자동 선택 | +| `updateFormDescription(formId)` | 설명 카드 DOM 업데이트 | + +### 1.5 파일 구조 ``` resources/views/approvals/ -├── create.blade.php ← 기안 작성 (양식 선택 + 동적 폼) -├── edit.blade.php ← 기안 수정 +├── create.blade.php ← 기안 작성 (2단계 양식 선택 + 설명 카드 + 동적 폼) +├── edit.blade.php ← 기안 수정 (create와 동일한 2단계 선택 구조) ├── show.blade.php ← 상세 조회 (양식별 조회 컴포넌트) └── partials/ ├── _leave-form.blade.php ← 휴가신청 폼 ├── _expense-form.blade.php ← 지출결의서 폼 ├── _expense-show.blade.php ← 지출결의서 조회 + ├── _purchase-request-form.blade.php ← 품의서 5종 통합 폼 (Alpine.js) + ├── _purchase-request-show.blade.php ← 품의서 5종 통합 조회 ├── _certificate-form.blade.php ← 재직증명서 폼 ├── _certificate-show.blade.php ← 재직증명서 조회 ├── _career-cert-form.blade.php ← 경력증명서 폼 @@ -447,9 +505,322 @@ API: POST /api/admin/approvals/upload-file --- -## 9. 결재 도장 테이블 (_approval-stamp-table.blade.php) +## 9. 품의서 5종 공통 (_purchase-request-form/show) -### 9.1 구조 +### 9.1 통합 Alpine.js 컴포넌트 + +품의서 5종은 **단일 Blade 파일**(`_purchase-request-form.blade.php`)에서 `prType` 프로퍼티로 동적 전환된다. + +```javascript +x-data="purchaseRequestForm(initialData, authUserName, initialFiles)" +``` + +#### 타입 전환 메커니즘 + +``` +create.blade.php → switchFormMode() + ↓ +code.startsWith('pr_') 감지 + ↓ +#purchase-request-form-container display: block + ↓ +setTimeout(50ms) → _x_dataStack[0].setPrType(code) + ↓ +Alpine.js x-if 분기 → 해당 폼 렌더링 +``` + +#### prType 코드 및 라벨 + +| prType | 라벨 | 색상 | +|--------|------|------| +| `pr_expense` | 지출품의서 | `bg-orange-50 text-orange-700` | +| `pr_contract` | 계약체결품의서 | `bg-purple-50 text-purple-700` | +| `pr_purchase` | 구매품의서 | `bg-blue-50 text-blue-700` | +| `pr_trip` | 출장품의서 | `bg-green-50 text-green-700` | +| `pr_settlement` | 비용정산품의서 | `bg-teal-50 text-teal-700` | + +### 9.2 공통 필드 (모든 품의서) + +| 필드 | 라벨 | 타입 | 기본값 | +|------|------|------|--------| +| `write_date` | 작성일자 | date | 오늘 | +| `department` | 요청부서 | text | - | +| `writer_name` | 요청자 | text | 인증 사용자명 | +| `attachment_memo` | 첨부서류 메모 | textarea | - | +| `files` | 파일 업로드 | file[] | - | + +### 9.3 공통 함수 + +| 함수 | 설명 | +|------|------| +| `setPrType(type)` | 외부에서 prType 설정 (switchFormMode에서 호출) | +| `getFormData()` | prType별 다른 JSON 구조 반환 (base에 `pr_type` 포함) | +| `addItem()` | 내역 행 추가 | +| `removeItem(index)` | 내역 행 삭제 | +| `formatMoney(val)` | 숫자 → 콤마 포맷 | +| `parseMoney(str)` | 콤마 문자열 → 정수 | +| `prVendorSearch(target, fieldName)` | 범용 거래처 Autocomplete 검색 | + +### 9.4 조회 화면 분기 (show.blade.php) + +```php +// show.blade.php에서 pr_ prefix로 분기 +@if(str_starts_with($approval->form?->code ?? '', 'pr_')) + @include('approvals.partials._purchase-request-show', ['content' => $content]) +@endif +``` + +`_purchase-request-show.blade.php`에서 `$content['pr_type']`으로 5종 분기 렌더링. + +--- + +## 10. 지출품의서 (pr_expense) + +### 10.1 추가 필드 + +| 필드 | 라벨 | 타입 | 필수 | +|------|------|------|------| +| `expense_category` | 지출항목 | text | 선택 | +| `usage_date` | 사용일자 | date | 선택 | +| `purpose` | 사용목적 | textarea | 필수 | + +### 10.2 내역 테이블 + +| 컬럼 | 라벨 | 타입 | +|------|------|------| +| `description` | 항목 | text | +| `amount` | 금액 | number (콤마 포맷) | +| `remark` | 비고 | text | + +### 10.3 Content JSON + +```json +{ + "pr_type": "pr_expense", + "write_date": "2026-03-06", + "department": "개발팀", + "writer_name": "홍길동", + "expense_category": "사무용품", + "usage_date": "2026-03-05", + "purpose": "업무용 모니터 구매", + "items": [ + { "description": "27인치 모니터", "amount": 350000, "remark": "LG전자" } + ], + "total_amount": 350000, + "attachment_memo": "견적서 첨부" +} +``` + +--- + +## 11. 계약체결품의서 (pr_contract) + +### 11.1 추가 필드 + +| 필드 | 라벨 | 타입 | 필수 | +|------|------|------|------| +| `contract_party` | 계약상대방 | text + Autocomplete | 필수 | +| `contract_party_biz_no` | 사업자번호 | text (자동) | - | +| `contract_content` | 계약내용 | textarea | 필수 | +| `contract_period_start` | 계약기간 시작 | date | 선택 | +| `contract_period_end` | 계약기간 종료 | date | 선택 | +| `contract_amount` | 계약금액 | number (콤마) | 필수 | +| `contract_conditions` | 주요조건 | textarea | 선택 | + +### 11.2 Content JSON + +```json +{ + "pr_type": "pr_contract", + "write_date": "2026-03-06", + "department": "경영지원팀", + "writer_name": "홍길동", + "contract_party": "(주)에이비씨", + "contract_party_biz_no": "123-45-67890", + "contract_content": "연간 IT 유지보수 계약", + "contract_period_start": "2026-04-01", + "contract_period_end": "2027-03-31", + "contract_amount": 12000000, + "contract_conditions": "월 1회 정기점검, 장애 발생 시 4시간 내 대응", + "attachment_memo": "계약서 초안 첨부" +} +``` + +### 11.3 특수 로직 + +- **거래처 검색**: `prVendorSearch(formData, 'contract_party')` — 계약상대방 필드에 Autocomplete 적용 +- 선택 시 `contract_party_biz_no` 자동 채움 + +--- + +## 12. 구매품의서 (pr_purchase) + +### 12.1 추가 필드 + +| 필드 | 라벨 | 타입 | 필수 | +|------|------|------|------| +| `vendor` | 납품업체 | text + Autocomplete | 선택 | +| `vendor_biz_no` | 사업자번호 | text (자동) | - | +| `delivery_date` | 납품예정일 | date | 선택 | +| `delivery_location` | 납품장소 | text | 선택 | + +### 12.2 내역 테이블 + +| 컬럼 | 라벨 | 타입 | +|------|------|------| +| `name` | 품목 | text | +| `spec` | 규격 | text | +| `quantity` | 수량 | number | +| `unit_price` | 단가 | number (콤마) | +| `amount` | 금액 | number (자동: 수량×단가) | +| `remark` | 비고 | text | + +### 12.3 Content JSON + +```json +{ + "pr_type": "pr_purchase", + "write_date": "2026-03-06", + "department": "생산팀", + "writer_name": "홍길동", + "vendor": "(주)공급사", + "vendor_biz_no": "987-65-43210", + "delivery_date": "2026-03-20", + "delivery_location": "본사 1층 창고", + "items": [ + { "name": "A4용지", "spec": "80g 500매", "quantity": 10, "unit_price": 25000, "amount": 250000, "remark": "" } + ], + "total_amount": 250000, + "attachment_memo": "" +} +``` + +### 12.4 특수 로직 + +- **금액 자동 계산**: `quantity × unit_price → amount` (x-effect 반응) +- **거래처 검색**: `prVendorSearch(formData, 'vendor')` — 납품업체 필드에 Autocomplete 적용 + +--- + +## 13. 출장품의서 (pr_trip) + +### 13.1 추가 필드 + +| 필드 | 라벨 | 타입 | 필수 | +|------|------|------|------| +| `destination` | 출장지 | text | 필수 | +| `trip_period_start` | 출장기간 시작 | date | 필수 | +| `trip_period_end` | 출장기간 종료 | date | 필수 | +| `trip_purpose` | 출장목적 | textarea | 필수 | + +### 13.2 일정표 (items) + +| 컬럼 | 라벨 | 타입 | +|------|------|------| +| `date` | 일자 | date | +| `schedule` | 일정 | text | +| `remark` | 비고 | text | + +### 13.3 경비 내역 (expenses) + +| 필드 | 라벨 | 타입 | +|------|------|------| +| `transport` | 교통비 | number (콤마) | +| `accommodation` | 숙박비 | number (콤마) | +| `meals` | 식비 | number (콤마) | +| `others` | 기타 | number (콤마) | +| (자동) | 합계 | number (합산) | + +### 13.4 Content JSON + +```json +{ + "pr_type": "pr_trip", + "write_date": "2026-03-06", + "department": "영업팀", + "writer_name": "홍길동", + "destination": "부산 해운대", + "trip_period_start": "2026-03-10", + "trip_period_end": "2026-03-11", + "trip_purpose": "거래처 방문 및 현장 점검", + "items": [ + { "date": "2026-03-10", "schedule": "거래처 미팅", "remark": "오전 10시" }, + { "date": "2026-03-11", "schedule": "현장 점검 및 복귀", "remark": "" } + ], + "expenses": { + "transport": 120000, + "accommodation": 80000, + "meals": 40000, + "others": 0 + }, + "total_amount": 240000, + "attachment_memo": "" +} +``` + +### 13.5 조회 화면 특수 구조 + +- **일정표**: 테이블 형태로 일자/일정/비고 렌더링 +- **경비 카드**: 교통비/숙박비/식비/기타 4개 항목 + 합계를 카드 그리드로 표시 + +--- + +## 14. 비용정산품의서 (pr_settlement) + +### 14.1 추가 필드 + +| 필드 | 라벨 | 타입 | 필수 | +|------|------|------|------| +| `settlement_period_start` | 정산기간 시작 | date | 선택 | +| `settlement_period_end` | 정산기간 종료 | date | 선택 | +| `payment_method` | 지급방법 | radio | 필수 | + +### 14.2 지급방법 옵션 + +| 값 | 라벨 | +|----|------| +| `corporate_card` | 법인카드 사용 | +| `personal_advance` | 개인 선지출 (환급 요청) | + +### 14.3 내역 테이블 + +| 컬럼 | 라벨 | 타입 | +|------|------|------| +| `date` | 사용일자 | date | +| `description` | 항목 | text | +| `amount` | 금액 | number (콤마) | +| `remark` | 비고 | text | + +### 14.4 Content JSON + +```json +{ + "pr_type": "pr_settlement", + "write_date": "2026-03-06", + "department": "개발팀", + "writer_name": "홍길동", + "settlement_period_start": "2026-02-01", + "settlement_period_end": "2026-02-28", + "payment_method": "personal_advance", + "items": [ + { "date": "2026-02-15", "description": "택시비", "amount": 25000, "remark": "야근 귀가" }, + { "date": "2026-02-20", "description": "회의 다과", "amount": 15000, "remark": "팀 미팅" } + ], + "total_amount": 40000, + "attachment_memo": "영수증 첨부" +} +``` + +### 14.5 조회 화면 특수 구조 + +- **지급방법 표시**: `corporate_card` → "법인카드 사용", `personal_advance` → "개인 선지출 (환급 요청)" +- 해당 라벨을 뱃지 형태로 표시 + +--- + +## 15. 결재 도장 테이블 (_approval-stamp-table.blade.php) + +### 15.1 구조 전통 한글 결재 양식의 도장 테이블을 구현한다. @@ -464,7 +835,7 @@ API: POST /api/admin/approvals/upload-file └──────┴────────┴────────┴────────┘ ``` -### 9.2 상태별 표시 +### 15.2 상태별 표시 | 상태 | approval_type | 표시 | 색상 | |------|---------------|------|------| @@ -476,9 +847,9 @@ API: POST /api/admin/approvals/upload-file --- -## 10. 결재선 편집기 (_approval-line-editor.blade.php) +## 16. 결재선 편집기 (_approval-line-editor.blade.php) -### 10.1 2패널 구조 +### 16.1 2패널 구조 ``` ┌─────────────────────┬─────────────────────┐ @@ -498,7 +869,7 @@ API: POST /api/admin/approvals/upload-file └───────────────────────────────────────────┘ ``` -### 10.2 기능 +### 16.2 기능 | 기능 | 설명 | |------|------| @@ -508,7 +879,7 @@ API: POST /api/admin/approvals/upload-file | **유형 선택** | 각 단계별 approval/agreement/reference 선택 | | **템플릿 로드** | 저장된 결재선 템플릿 드롭다운 | -### 10.3 데이터 소스 +### 16.3 데이터 소스 ``` API: /api/admin/tenant-users/list @@ -525,7 +896,7 @@ API: /api/admin/tenant-users/list ] ``` -### 10.4 Hidden Inputs (form 전송) +### 16.4 Hidden Inputs (form 전송) ```html @@ -536,9 +907,9 @@ API: /api/admin/tenant-users/list --- -## 11. ApprovalForm 모델 +## 17. ApprovalForm 모델 -### 11.1 테이블 스키마 +### 17.1 테이블 스키마 | 컬럼 | 타입 | 설명 | |------|------|------| @@ -551,20 +922,34 @@ API: /api/admin/tenant-users/list | `body_template` | LONGTEXT NULL | HTML 본문 템플릿 | | `is_active` | BOOLEAN | 활성 여부 | -### 11.2 카테고리 +### 17.2 카테고리 + +#### DB 카테고리 (ApprovalForm.category) | 카테고리 | 설명 | 양식 코드 | |---------|------|----------| -| `request` | 품의서/신청서 | `leave`, `attendance_request`, `reason_report` | +| `request` | 신청서 | `leave`, `attendance_request`, `reason_report` | | `expense` | 지출결의서 | `expense` | | `certificate` | 증명서/서류 | `employment_cert`, `career_cert`, `appointment_cert`, `resignation` | -| `expense_estimate` | 지출 예상 내역서 | - | +| `expense_estimate` | 품의서 | `pr_expense`, `pr_contract`, `pr_purchase`, `pr_trip`, `pr_settlement` | + +#### UI 분류 (formCategoryMap — 2단계 선택용) + +| UI 분류 | 양식 코드 | +|---------|----------| +| 일반 | `BUSINESS_DRAFT` | +| 인사/근태 | `leave`, `attendance_request`, `reason_report`, `resignation` | +| 증명서 | `employment_cert`, `career_cert`, `appointment_cert` | +| 품의 | `pr_expense`, `pr_contract`, `pr_purchase`, `pr_trip`, `pr_settlement` | +| 재무 | `expense` | + +> **참고**: DB 카테고리와 UI 분류는 별도 매핑이다. DB는 `approval_forms.category` ENUM이고, UI 분류는 JavaScript `formCategoryMap` 객체로 정의된다. --- -## 12. 양식별 저장/조회 흐름 +## 18. 양식별 저장/조회 흐름 -### 12.1 저장 (create/update) +### 18.1 저장 (create/update) ``` 사용자 입력 @@ -579,7 +964,7 @@ ApprovalService::createApproval() Approval.content = JSON encode → DB 저장 ``` -### 12.2 조회 (show) +### 18.2 조회 (show) ``` GET /approval-mgmt/{id} @@ -591,12 +976,15 @@ Blade: show.blade.php 양식 코드별 분기: leave → (본문에 인라인 표시) expense → @include('_expense-show') + pr_* → @include('_purchase-request-show') ← str_starts_with 매칭 employment_cert → @include('_certificate-show') career_cert → @include('_career-cert-show') appointment_cert → @include('_appointment-cert-show') resignation → @include('_resignation-show') ``` +> **품의서 분기**: `str_starts_with($approval->form?->code ?? '', 'pr_')` 조건으로 5종 모두 단일 include로 처리. `_purchase-request-show.blade.php` 내부에서 `$content['pr_type']`으로 세부 분기. + --- ## 관련 문서