# 자금계획일정 ## 개요 자금계획일정은 입금 예정/지급 예정 일정을 등록, 관리하고 월별 캘린더 뷰로 확인하는 기능입니다. 반복 일정, 월별 복사, 상태 관리(예정/완료/취소) 기능을 지원합니다. - **라우트**: `GET /finance/fund-schedules` - **라우트 이름**: `finance.fund-schedules.index` - **UI 기술**: Blade 템플릿 ## 파일 구조 ``` mng/ ├── app/Http/Controllers/ │ ├── Finance/ │ │ └── FundScheduleController.php # 웹 컨트롤러 (5개 메서드) │ └── Api/Admin/ │ └── FundScheduleController.php # API 컨트롤러 ├── app/Services/ │ └── FundScheduleService.php # 비즈니스 로직 (13개+ 공개 메서드) ├── app/Models/Finance/ │ └── FundSchedule.php # ORM 모델 └── resources/views/finance/fund-schedules/ ├── index.blade.php # 캘린더 뷰 ├── create.blade.php # 등록 폼 ├── edit.blade.php # 수정 폼 └── show.blade.php # 상세보기 ``` ## 라우트 ### 웹 라우트 ```php // routes/web.php (finance prefix 그룹 내) Route::get('/fund-schedules', [FundScheduleController::class, 'index'])->name('fund-schedules.index'); Route::get('/fund-schedules/create', [..., 'create'])->name('fund-schedules.create'); Route::get('/fund-schedules/{id}', [..., 'show'])->name('fund-schedules.show'); Route::get('/fund-schedules/{id}/edit', [..., 'edit'])->name('fund-schedules.edit'); // 호환성 리다이렉트 Route::get('/fund-schedule', fn() => redirect()->route('finance.fund-schedules.index')); ``` ### API 라우트 ```php // routes/api.php (admin/fund-schedules prefix) GET / → index() 목록 (페이지네이션) GET /calendar → calendar() 월별 캘린더 데이터 GET /summary → summary() 요약 통계 GET /upcoming → upcoming() 예정 일정 POST /copy → copy() 월별 일정 복사 GET /{id} → show() 상세 조회 POST / → store() 생성 PUT /{id} → update() 수정 DELETE /{id} → destroy() 삭제 ``` ## 컨트롤러 ### FundScheduleController (웹) | 메서드 | 설명 | 데이터 흐름 | |--------|------|------------| | `index()` | 캘린더 뷰 | year, month → `getCalendarData()` → `getMonthlySummary()` → View | | `create()` | 등록 폼 | 계좌 목록, 타입/상태/반복 옵션 → View | | `edit(id)` | 수정 폼 | ID로 일정 조회 → View | | `show(id)` | 상세보기 | ID로 일정 조회 → View | ## 서비스 클래스 ### FundScheduleService | 메서드 | 매개변수 | 설명 | |--------|---------|------| | `getSchedules()` | filters, perPage | 필터링 목록 (페이지네이션) | | `getSchedulesForMonth()` | year, month | 월별 일정 조회 | | `getSchedulesForDate()` | date | 특정 날짜 일정 | | `getUpcomingSchedules()` | days=30 | 향후 N일 예정 일정 | | `getScheduleById()` | id, withTrashed | 단일 조회 | | `createSchedule()` | data | 생성 (tenant_id, created_by 자동) | | `updateSchedule()` | schedule, data | 수정 (updated_by 자동) | | `deleteSchedule()` | schedule | Soft Delete | | `restoreSchedule()` | schedule | 복원 | | `forceDeleteSchedule()` | schedule | 영구 삭제 | | `markAsCompleted()` | schedule, amount, date | 완료 처리 | | `markAsCancelled()` | schedule | 취소 처리 | | `copySchedulesToMonth()` | src→tgt year/month | 월별 일정 복사 (말일 처리) | | `bulkDelete()` | ids | 일괄 삭제 | | `bulkUpdateStatus()` | ids, status | 일괄 상태 변경 | | `getMonthlySummary()` | year, month | 월별 요약 | | `getCalendarData()` | year, month | 캘린더용 날짜별 그룹핑 | | `getSummary()` | - | 전체 요약 | ## 모델 ### FundSchedule **테이블**: `fund_schedules` #### 상수 정의 ```php // 일정 유형 TYPE_INCOME = 'income' // 입금 예정 TYPE_EXPENSE = 'expense' // 지급 예정 // 상태 STATUS_PENDING = 'pending' // 예정 STATUS_COMPLETED = 'completed' // 완료 STATUS_CANCELLED = 'cancelled' // 취소 // 반복 규칙 RECURRENCE_DAILY = 'daily' RECURRENCE_WEEKLY = 'weekly' RECURRENCE_MONTHLY = 'monthly' RECURRENCE_YEARLY = 'yearly' ``` #### 주요 필드 | 필드 | 타입 | 설명 | |------|------|------| | `tenant_id` | bigint | 테넌트 ID | | `title` | string | 일정 제목 | | `description` | text | 설명 | | `schedule_type` | enum | income / expense | | `scheduled_date` | date | 예정 날짜 | | `amount` | decimal(15,2) | 예정 금액 | | `currency` | string | 통화 (KRW) | | `related_bank_account_id` | bigint | 관련 계좌 ID | | `counterparty` | string | 거래처 | | `category` | string | 카테고리 | | `status` | enum | pending / completed / cancelled | | `is_recurring` | boolean | 반복 여부 | | `recurrence_rule` | string | 반복 규칙 | | `recurrence_end_date` | date | 반복 종료일 | | `completed_date` | date | 실제 완료일 | | `completed_amount` | decimal(15,2) | 실제 금액 | | `created_by` / `updated_by` / `deleted_by` | bigint | 작업자 | #### 주요 Scope ```php ->income() // TYPE_INCOME만 ->expense() // TYPE_EXPENSE만 ->pending() // STATUS_PENDING만 ->completed() // STATUS_COMPLETED만 ->forMonth(year, month) // 특정 월의 일정 ->dateBetween(s, e) // 날짜 범위 ->ordered() // scheduled_date 정렬 ``` #### 주요 Accessor ```php $schedule->formatted_amount // "100만원" 형식 $schedule->type_label // "입금 예정" $schedule->status_label // "예정" $schedule->type_color_class // Tailwind 색상 클래스 $schedule->status_color_class // Tailwind 색상 클래스 ``` ## 뷰 구성 ### index.blade.php (캘린더 뷰) ``` ┌─ 페이지 헤더 ────────────────────── │ 제목: "자금계획일정" │ 버튼: "일정 등록" │ ├─ 월별 요약 카드 (4열) ──────────── │ 입금 예정 | 지급 예정 | 순 자금 흐름 | 총 일정 │ ├─ 캘린더 섹션 ───────────────────── │ ├─ 헤더: 월 네비게이션 (< 2026년 2월 >) │ ├─ 요일 헤더 (일~토) │ └─ 날짜 그리드 (일별 일정 표시) │ ├─ 입금: 초록 배지 │ ├─ 지출: 빨강 배지 │ └─ 클릭 시 일정 상세 │ └─ 일정 등록/수정 모달 (JavaScript) ``` ## 핵심 로직 ### 월별 복사 (copySchedulesToMonth) ``` 원본 월의 모든 일정 조회 ↓ 대상 월로 복사 (말일 처리) 예) 1월 31일 → 2월 28일 (윤년이면 29일) ↓ 새 일정 생성 (상태: pending으로 초기화) ``` ### 월별 요약 (getMonthlySummary) ```php [ 'year' => 2026, 'month' => 2, 'total_count' => 15, 'income' => [ 'count' => 5, 'total' => 10000000, 'pending' => 7000000, 'completed' => 3000000 ], 'expense' => [ 'count' => 10, 'total' => 8000000, 'pending' => 5000000, 'completed' => 3000000 ], 'net' => 2000000 // 수입 - 지출 ] ``` ### 캘린더 데이터 (getCalendarData) ```php // 날짜별 그룹핑 [ '2026-02-05' => [ {일정1}, {일정2} ], '2026-02-06' => [ {일정3} ], ... ] ``` ## HTMX 호환성 - 순수 Blade 템플릿 (HX-Redirect 불필요) - JavaScript는 모달 열기/닫기 함수만 포함