From 73f7811da3bb575b69f9b7a27c2a30eab0f7cfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 26 Feb 2026 22:07:36 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[leave]=20=ED=9C=B4=EA=B0=80=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=AA=A8=EB=93=88=20=EA=B0=9C=EB=B0=9C=20=EA=B3=84?= =?UTF-8?q?=ED=9A=8D=EC=84=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase 1: 기본 휴가관리 (신청/승인/잔여연차/사용현황) - Phase 2: 연차 정책 및 자동 계산 (근로기준법 기반) - Phase 3: 연차 촉진, 알림, 리포트 - 기존 API 테이블 4개 활용 - 근태현황 vacation 기능 분리 전략 포함 --- INDEX.md | 2 + plans/leave-management-plan.md | 459 +++++++++++++++++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 plans/leave-management-plan.md diff --git a/INDEX.md b/INDEX.md index 7f840d4..1ef7b30 100644 --- a/INDEX.md +++ b/INDEX.md @@ -155,6 +155,8 @@ docs/ | [SAM_ERP_회계관리_Storyboard_D1.6.md](plans/SAM_ERP_회계관리_Storyboard_D1.6.md) | ERP 회계관리 스토리보드 D1.6 (65p PDF → 마크다운 변환) | | [SAM_General_Rule_Storyboard_D1.0.md](plans/SAM_General_Rule_Storyboard_D1.0.md) | General Rule 스토리보드 D1.0 (43p PDF → 마크다운 변환, UIUX 공통 규칙) | | [production-deployment-plan.md](plans/production-deployment-plan.md) | 운영 환경 배포 계획 (CI/CD, 서버 아키텍처) | +| [attendance-management-plan.md](plans/attendance-management-plan.md) | 근태현황 개발 계획 (Phase 1~2, HTMX 기반) | +| [leave-management-plan.md](plans/leave-management-plan.md) | 휴가관리 모듈 개발 계획 (연차 발생/신청/승인/정책) | ### features/ - 기능별 문서 diff --git a/plans/leave-management-plan.md b/plans/leave-management-plan.md new file mode 100644 index 0000000..62384c3 --- /dev/null +++ b/plans/leave-management-plan.md @@ -0,0 +1,459 @@ +# 휴가관리 모듈 개발 계획서 + +> **작성일**: 2026-02-26 +> **상태**: 설계 중 + +--- + +## 1. 개요 + +### 1.1 목적 + +근태현황에 포함된 휴가/연차 기능을 **독립된 휴가관리 모듈**로 분리한다. +근로기준법 기반 연차 자동 계산, 휴가 신청/승인, 잔여 연차 관리를 체계적으로 지원한다. + +### 1.2 핵심 원칙 + +- 근태현황의 `vacation` 상태는 **결과 기록**으로만 유지 (휴가 승인 완료 시 자동 기록) +- 휴가 신청/승인/잔여일수 관리는 모두 **휴가관리 모듈**에서 수행 +- 기존 API 테이블/모델(`leaves`, `leave_balances`, `leave_policies`, `leave_grants`)을 최대한 활용 +- MNG에서 관리자 인터페이스(Blade + HTMX) 구현 + +### 1.3 현재 상태 분석 + +| 항목 | API (DB/모델) | MNG (UI/서비스) | 비고 | +|------|:------------:|:--------------:|------| +| `leaves` 테이블 | ✅ 마이그레이션 완료 | ❌ 미구현 | 핵심 테이블 | +| `leave_balances` 테이블 | ✅ 마이그레이션 완료 | ⚠️ 단순 모델만 | 연차 잔액 | +| `leave_policies` 테이블 | ✅ 마이그레이션 완료 | ❌ 미구현 | 연차 정책 설정 | +| `leave_grants` 테이블 | ✅ 마이그레이션 완료 | ❌ 미구현 | 연차 부여 이력 | +| `attendance_requests` 테이블 | ✅ 마이그레이션 완료 | ✅ 신청/승인 구현 | 근태현황에 포함 | +| 연차 자동 차감 | — | ⚠️ 단순 구현 | 복원 로직 없음 | + +### 1.4 근태현황과의 역할 분리 + +| 기능 | 근태현황 (유지) | 휴가관리 (신규) | +|------|:--------------:|:--------------:| +| 출퇴근 기록 | ✅ | — | +| 출장/재택/외근 신청·승인 | ✅ | — | +| `vacation` 상태 표시 | ✅ (결과만) | — | +| 연차 부여/발생 규칙 | — | ✅ | +| 휴가 신청·승인 워크플로우 | — | ✅ | +| 잔여 연차 관리 | — | ✅ | +| 연차 촉진 알림 | — | ✅ (Phase 3) | + +--- + +## 2. 기존 DB 스키마 + +> API에 이미 마이그레이션 완료된 테이블들. MNG에서 모델만 생성하여 활용한다. + +### 2.1 `leaves` 테이블 + +``` +┌──────────────────────────────────────────────────────────┐ +│ leaves │ +├────────────────┬─────────────┬────────────────────────────┤ +│ id │ bigint PK │ Auto Increment │ +│ tenant_id │ bigint FK │ → tenants.id │ +│ user_id │ bigint FK │ → users.id │ +│ leave_type │ enum │ annual, half_am, half_pm, │ +│ │ │ sick, family, maternity, │ +│ │ │ parental │ +│ start_date │ date │ 시작일 │ +│ end_date │ date │ 종료일 │ +│ days │ decimal(3,1)│ 사용일수 (0.5 = 반차) │ +│ reason │ text │ 사유 │ +│ status │ enum │ pending, approved, rejected, │ +│ │ │ cancelled │ +│ approved_by │ bigint │ 승인자 ID │ +│ approved_at │ datetime │ 승인 일시 │ +│ reject_reason │ text │ 반려 사유 │ +│ created_by/ │ bigint │ 감사 필드 │ +│ updated_by/ │ │ │ +│ deleted_by │ │ │ +│ timestamps │ │ │ +│ soft_deletes │ │ │ +└────────────────┴─────────────┴────────────────────────────┘ +INDEX: (tenant_id, user_id), status, (start_date, end_date) +``` + +### 2.2 `leave_balances` 테이블 + +``` +┌──────────────────────────────────────────────────────────┐ +│ leave_balances │ +├────────────────┬─────────────┬────────────────────────────┤ +│ id │ bigint PK │ │ +│ tenant_id │ bigint FK │ │ +│ user_id │ bigint FK │ │ +│ year │ int │ 연도 │ +│ total_days │ decimal(4,1)│ 부여일수 (기본 15) │ +│ used_days │ decimal(4,1)│ 사용일수 │ +│ remaining_days │ decimal(4,1)│ storedAs(total - used) │ +└────────────────┴─────────────┴────────────────────────────┘ +UNIQUE: (tenant_id, user_id, year) +``` + +### 2.3 `leave_policies` 테이블 + +``` +┌──────────────────────────────────────────────────────────┐ +│ leave_policies │ +├──────────────────────┬─────────────┬─────────────────────┤ +│ tenant_id │ bigint UNIQUE│ 테넌트당 1개 │ +│ standard_type │ enum │ fiscal / hire │ +│ fiscal_start_month │ tinyint │ 회계연도 시작월 │ +│ fiscal_start_day │ tinyint │ 회계연도 시작일 │ +│ default_annual_leave │ int │ 기본 연차 (15) │ +│ additional_leave_per_year │ int │ 근속 가산 (+1) │ +│ max_annual_leave │ int │ 최대 연차 (25) │ +│ carry_over_enabled │ boolean │ 이월 허용 │ +│ carry_over_max_days │ int │ 이월 한도 │ +│ carry_over_expiry_months │ int │ 이월 소멸 개월 │ +└──────────────────────┴─────────────┴─────────────────────┘ +``` + +### 2.4 `leave_grants` 테이블 + +``` +┌──────────────────────────────────────────────────────────┐ +│ leave_grants │ +├────────────────┬─────────────┬────────────────────────────┤ +│ tenant_id │ bigint FK │ │ +│ user_id │ bigint FK │ │ +│ grant_type │ enum │ annual, monthly, reward, │ +│ │ │ condolence, other │ +│ grant_date │ date │ 부여일 │ +│ grant_days │ decimal(4,1)│ 부여일수 │ +│ reason │ text │ 부여 사유 │ +└────────────────┴─────────────┴────────────────────────────┘ +INDEX: (tenant_id, user_id), grant_date, grant_type +``` + +--- + +## 3. Phase 1: 기본 휴가관리 (핵심) + +> 🔴 **필수** — 연차 조회, 휴가 신청/승인, 잔여일수 관리 + +### 3.1 MNG 모델 생성 + +| 모델 | 파일 | 대상 테이블 | +|------|------|------------| +| `Leave` | `app/Models/HR/Leave.php` | `leaves` | +| `LeavePolicy` | `app/Models/HR/LeavePolicy.php` | `leave_policies` | +| `LeaveGrant` | `app/Models/HR/LeaveGrant.php` | `leave_grants` | +| `LeaveBalance` | (기존 수정) | `leave_balances` | + +### 3.2 LeaveService 생성 + +**파일**: `app/Services/HR/LeaveService.php` + +| 메서드 | 설명 | +|--------|------| +| `getLeaves(array $filters, int $perPage)` | 휴가 목록 조회 (필터: 사원, 유형, 상태, 기간) | +| `storeLeave(array $data)` | 휴가 신청 등록 (잔여일수 검증 포함) | +| `approve(int $id)` | 승인 처리 → `leave_balances` 차감 → `attendances` 자동 기록 | +| `reject(int $id, ?string $reason)` | 반려 처리 | +| `cancel(int $id)` | 취소 처리 → `leave_balances` 복원 → `attendances` 삭제 | +| `getBalance(int $userId, ?int $year)` | 사원별 연차 잔여일수 조회 | +| `getBalanceSummary(?int $year)` | 전체 사원 잔여일수 요약 | +| `calculateDays(string $type, string $startDate, string $endDate)` | 신청 일수 자동 계산 (주말 제외, 반차=0.5) | + +### 3.3 LeaveController (API) 생성 + +**파일**: `app/Http/Controllers/Api/Admin/HR/LeaveController.php` + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/admin/hr/leaves` | 휴가 목록 (HTMX/JSON) | +| POST | `/admin/hr/leaves` | 휴가 신청 등록 | +| POST | `/admin/hr/leaves/{id}/approve` | 승인 | +| POST | `/admin/hr/leaves/{id}/reject` | 반려 | +| POST | `/admin/hr/leaves/{id}/cancel` | 취소 | +| GET | `/admin/hr/leaves/balance` | 전체 사원 잔여일수 요약 | +| GET | `/admin/hr/leaves/balance/{userId}` | 개별 사원 잔여일수 | +| GET | `/admin/hr/leaves/export` | 엑셀(CSV) 내보내기 | + +### 3.4 MNG 뷰 컨트롤러 + +**파일**: `app/Http/Controllers/HR/LeaveController.php` + +``` +GET /hr/leaves → index 페이지 (휴가관리 메인) +``` + +### 3.5 뷰 구성 + +**파일**: `resources/views/hr/leaves/index.blade.php` + +``` +┌───────────────────────────────────────────────────────┐ +│ 휴가관리 │ +├───────────┬───────────┬─────────────┬─────────────────┤ +│ 휴가신청 │ 잔여연차 │ 사용현황 │ (Phase 2) 설정 │ +│ (탭 1) │ (탭 2) │ (탭 3) │ (탭 4) │ +└───────────┴───────────┴─────────────┴─────────────────┘ +``` + +**탭 1: 휴가신청 목록** + +``` +┌─────────────────────────────────────────────────┐ +│ [+ 휴가 신청] [엑셀 내보내기] │ +│ │ +│ 필터: [사원 ▼] [유형 ▼] [상태 ▼] [기간 ~] │ +│ │ +│ ┌──────┬──────┬──────┬──────┬─────┬──────┬─────┐ │ +│ │ 사원 │ 유형 │ 기간 │ 일수 │사유 │ 상태 │ 처리│ │ +│ ├──────┼──────┼──────┼──────┼─────┼──────┼─────┤ │ +│ │홍길동│ 연차 │2/24~ │ 1.0 │개인 │ 대기 │승인 │ │ +│ │ │ │ 2/24 │ │사유 │ │반려 │ │ +│ │김영희│ 반차 │2/25 │ 0.5 │병원 │ 승인 │취소 │ │ +│ │ │(오전)│ │ │ │ │ │ │ +│ └──────┴──────┴──────┴──────┴─────┴──────┴─────┘ │ +│ │ +│ [페이지네이션] │ +└─────────────────────────────────────────────────┘ +``` + +**탭 2: 잔여연차 현황** + +``` +┌─────────────────────────────────────────────────┐ +│ 연도: [2026 ▼] │ +│ │ +│ ┌──────┬──────┬──────┬──────┬──────┬──────────┐ │ +│ │ 사원 │ 부서 │ 입사일│ 부여 │ 사용 │ 잔여 │ │ +│ ├──────┼──────┼──────┼──────┼──────┼──────────┤ │ +│ │홍길동│ 개발 │21.03 │ 20.0 │ 5.0 │ 15.0 │ │ +│ │김영희│ 영업 │23.08 │ 15.0 │ 3.5 │ 11.5 │ │ +│ │이민수│ 총무 │25.06 │ 11.0 │ 2.0 │ 9.0 │ │ +│ └──────┴──────┴──────┴──────┴──────┴──────────┘ │ +└─────────────────────────────────────────────────┘ +``` + +**탭 3: 사용현황 통계** + +``` +┌─────────────────────────────────────────────────┐ +│ 기간: [2026 ▼] [전체/부서별 ▼] │ +│ │ +│ 유형별 집계: 연차 45건 | 반차 12건 | 병가 3건 │ +│ │ +│ 월별 사용 추이 차트 (선택적) │ +│ │ +│ ┌──────┬──────┬──────┬──────┬──────┬──────────┐ │ +│ │ 사원 │ 연차 │ 반차 │ 병가 │ 경조 │ 합계 │ │ +│ ├──────┼──────┼──────┼──────┼──────┼──────────┤ │ +│ │홍길동│ 3.0 │ 1.0 │ 1.0 │ 0.0 │ 5.0 │ │ +│ │김영희│ 2.0 │ 1.5 │ 0.0 │ 0.0 │ 3.5 │ │ +│ └──────┴──────┴──────┴──────┴──────┴──────────┘ │ +└─────────────────────────────────────────────────┘ +``` + +### 3.6 휴가 신청 모달 + +``` +┌──────────────────────────────────────────┐ +│ 휴가 신청 │ +├──────────────────────────────────────────┤ +│ 사원: [홍길동 ▼] 잔여: 15.0일 │ +│ 유형: [연차 ▼] │ +│ 기간: [2026-02-27] ~ [2026-02-28] │ +│ 일수: 2.0일 (자동 계산, 주말 제외) │ +│ 사유: [ ] │ +│ │ +│ [취소] [신청] │ +└──────────────────────────────────────────┘ +``` + +### 3.7 근태현황 연동 로직 + +``` +휴가 승인 시: +┌──────────┐ ┌──────────────┐ ┌──────────────┐ +│ Leave │──→ │ LeaveBalance │──→ │ Attendance │ +│ approved │ │ used_days +N │ │ status= │ +│ │ │ │ │ vacation │ +└──────────┘ └──────────────┘ └──────────────┘ + +휴가 취소 시: +┌──────────┐ ┌──────────────┐ ┌──────────────┐ +│ Leave │──→ │ LeaveBalance │──→ │ Attendance │ +│ cancelled │ │ used_days -N │ │ 해당 날짜 │ +│ │ │ │ │ 레코드 삭제 │ +└──────────┘ └──────────────┘ └──────────────┘ +``` + +### 3.8 근태현황 정리 작업 + +Phase 1 구현 후 근태현황에서 다음을 정리: + +- `AttendanceRequest`의 `vacation` 유형 → 휴가관리로 이관 (출장/재택/외근만 유지) +- `AttendanceService.deductLeaveBalance()` 제거 → `LeaveService`로 일원화 +- `AttendanceController.leaveBalance()` 제거 → `LeaveController`로 이관 + +--- + +## 4. Phase 2: 연차 정책 및 자동 계산 + +> 🟡 **중요** — 근로기준법 기반 연차 자동 발생, 정책 설정 + +### 4.1 연차 정책 설정 UI + +**탭 4: 휴가 설정** (Phase 2에서 활성화) + +``` +┌──────────────────────────────────────────┐ +│ 연차 기준 │ +│ ○ 입사일 기준 ● 회계연도 기준 │ +│ 회계연도 시작: [1]월 [1]일 │ +│ │ +│ 연차 일수 │ +│ 기본 연차: [15]일 │ +│ 2년 초과 시 가산: [1]일/2년 │ +│ 최대 연차: [25]일 │ +│ │ +│ 이월 설정 │ +│ □ 잔여 연차 이월 허용 │ +│ 이월 한도: [5]일 │ +│ 이월 소멸: [3]개월 후 │ +│ │ +│ [저장] │ +└──────────────────────────────────────────┘ +``` + +### 4.2 LeavePolicyService + +**파일**: `app/Services/HR/LeavePolicyService.php` + +| 메서드 | 설명 | +|--------|------| +| `getPolicy()` | 현재 테넌트 연차 정책 조회 | +| `updatePolicy(array $data)` | 정책 저장/수정 | +| `calculateAnnualLeave(int $userId)` | 사원별 연차 자동 계산 (입사일 + 근속년수 기반) | +| `generateAnnualLeaves()` | 전체 사원 연차 일괄 발생 (연초/입사일 기준) | +| `processCarryOver()` | 이월 처리 (연말) | + +### 4.3 연차 발생 규칙 (근로기준법) + +```php +// 입사일 기준 연차 계산 로직 +function calculateAnnualDays(Carbon $hireDate): float +{ + $years = $hireDate->diffInYears(now()); + + if ($years < 1) { + // 1년 미만: 매월 개근 시 1일 (최대 11일) + $months = $hireDate->diffInMonths(now()); + return min($months, 11); + } + + // 1년 이상: 15일 + (근속년수-1)/2 가산 (최대 25일) + $base = 15; + $additional = max(0, floor(($years - 1) / 2)); + return min($base + $additional, 25); +} +``` + +### 4.4 연차 부여 이력 관리 + +`leave_grants` 테이블을 활용하여 부여 이력 추적: + +| grant_type | 설명 | 예시 | +|-----------|------|------| +| `annual` | 연차 자동 발생 | 2026년 연차 15일 부여 | +| `monthly` | 1년 미만 월차 | 2026-03 월차 1일 부여 | +| `reward` | 포상 휴가 | 우수사원 포상 2일 | +| `condolence` | 경조사 | 결혼 경조 5일 | +| `other` | 기타 | 회사 지정 휴가 | + +--- + +## 5. Phase 3: 고급 기능 + +> 🟢 **권장** — 연차 촉진, 알림, 리포트 + +### 5.1 연차 촉진제도 (근로기준법 제61조) + +| 시기 | 내용 | 자동화 | +|------|------|--------| +| 만료 6개월 전 | 1차 촉진 통보 (미사용 일수 안내) | 카카오 알림톡 발송 | +| 근로자 미응답 시 | 사용 시기 지정 촉구 | 리마인더 알림 | +| 만료 2개월 전 | 2차 촉진 통보 (회사 지정) | 카카오 알림톡 + 이력 보관 | + +### 5.2 알림 기능 + +- 휴가 승인/반려 시 신청자에게 알림톡 +- 잔여 연차 N일 이하 시 사용 권고 알림 +- 연차 소멸 D-30, D-7 자동 알림 + +### 5.3 리포트 + +- 부서별/사원별 연차 사용율 대시보드 +- 연차 대장 엑셀 출력 (노무감사 대비) +- 월별 사용 추이 통계 + +--- + +## 6. 구현 순서 + +### Phase 1 (기본 — 약 1~2일) + +| Step | 작업 | 파일 | +|------|------|------| +| 1 | MNG 모델 생성 (Leave, LeavePolicy, LeaveGrant) | `app/Models/HR/` | +| 2 | LeaveService 생성 | `app/Services/HR/LeaveService.php` | +| 3 | LeaveController (API) 생성 | `app/Http/Controllers/Api/Admin/HR/` | +| 4 | API 라우트 등록 | `routes/api.php` | +| 5 | 뷰 컨트롤러 생성 | `app/Http/Controllers/HR/LeaveController.php` | +| 6 | 웹 라우트 등록 | `routes/web.php` | +| 7 | index.blade.php 작성 (3개 탭) | `resources/views/hr/leaves/` | +| 8 | 파셜 뷰 작성 (목록, 잔여, 통계) | `resources/views/hr/leaves/partials/` | +| 9 | 근태현황 vacation 연동 정리 | AttendanceService 수정 | +| 10 | 메뉴 등록 안내 | tinker 명령 제공 | + +### Phase 2 (정책 — 약 1일) + +| Step | 작업 | +|------|------| +| 1 | LeavePolicyService 생성 | +| 2 | 연차 자동 계산 로직 구현 | +| 3 | 설정 UI (탭 4) 추가 | +| 4 | 연차 일괄 발생 Artisan 명령 | +| 5 | 이월 처리 로직 | + +### Phase 3 (고급 — 추후) + +| Step | 작업 | +|------|------| +| 1 | 연차 촉진 스케줄러 | +| 2 | 카카오 알림톡 연동 | +| 3 | 연차 대장 리포트 | +| 4 | 대시보드 위젯 | + +--- + +## 7. 핵심 설계 결정 + +| 항목 | 결정 | 이유 | +|------|------|------| +| 신규 테이블 | 불필요 | API에 4개 테이블 이미 존재 | +| 휴가 신청 테이블 | `leaves` 사용 | `attendance_requests`에서 vacation 분리 | +| 반차 처리 | `leave_type`=`half_am`/`half_pm`, `days`=0.5 | 기존 enum 활용 | +| 연차 차감 시점 | 승인 시 즉시 차감 | 잔여일수 실시간 반영 | +| 근태 연동 | 승인 시 Attendance 자동 생성 | 기존 패턴 유지 | +| 취소 시 복원 | used_days 복원 + Attendance 삭제 | 데이터 일관성 | + +--- + +## 관련 문서 + +- `plans/attendance-management-plan.md` — 근태현황 개발 계획 +- `rules/attendance-api.md` — 근태 API 규칙 + +--- + +**최종 업데이트**: 2026-02-26