diff --git a/app/Http/Controllers/Api/Admin/FundScheduleController.php b/app/Http/Controllers/Api/Admin/FundScheduleController.php index a752f957..e962146f 100644 --- a/app/Http/Controllers/Api/Admin/FundScheduleController.php +++ b/app/Http/Controllers/Api/Admin/FundScheduleController.php @@ -239,6 +239,47 @@ public function updateStatus(Request $request, int $id): JsonResponse|Response ]); } + /** + * 월별 일정 복사 + */ + public function copy(Request $request): JsonResponse + { + $validated = $request->validate([ + 'source_year' => 'required|integer|min:2000|max:2100', + 'source_month' => 'required|integer|min:1|max:12', + 'target_year' => 'required|integer|min:2000|max:2100', + 'target_month' => 'required|integer|min:1|max:12', + ]); + + if ($validated['source_year'] === $validated['target_year'] + && $validated['source_month'] === $validated['target_month']) { + return response()->json([ + 'success' => false, + 'message' => '원본 월과 대상 월이 동일합니다.', + ], 422); + } + + $copiedCount = $this->fundScheduleService->copySchedulesToMonth( + $validated['source_year'], + $validated['source_month'], + $validated['target_year'], + $validated['target_month'] + ); + + if ($copiedCount === 0) { + return response()->json([ + 'success' => false, + 'message' => '복사할 일정이 없습니다.', + ], 422); + } + + return response()->json([ + 'success' => true, + 'message' => "{$copiedCount}건의 일정이 복사되었습니다.", + 'data' => ['copied_count' => $copiedCount], + ]); + } + /** * 월별 요약 통계 */ diff --git a/app/Services/FundScheduleService.php b/app/Services/FundScheduleService.php index c2da0be6..132c60f8 100644 --- a/app/Services/FundScheduleService.php +++ b/app/Services/FundScheduleService.php @@ -218,6 +218,53 @@ public function updateStatus(FundSchedule $schedule, string $status): FundSchedu return $schedule->fresh(); } + // ========================================================================= + // 월별 복사 + // ========================================================================= + + /** + * 원본 월의 일정을 대상 월로 복사 + */ + public function copySchedulesToMonth(int $sourceYear, int $sourceMonth, int $targetYear, int $targetMonth): int + { + $schedules = $this->getSchedulesForMonth($sourceYear, $sourceMonth); + + if ($schedules->isEmpty()) { + return 0; + } + + $targetLastDay = cal_days_in_month(CAL_GREGORIAN, $targetMonth, $targetYear); + $copiedCount = 0; + + foreach ($schedules as $schedule) { + $sourceDay = $schedule->scheduled_date->day; + $targetDay = min($sourceDay, $targetLastDay); + + $targetDate = sprintf('%04d-%02d-%02d', $targetYear, $targetMonth, $targetDay); + + $this->createSchedule([ + 'title' => $schedule->title, + 'description' => $schedule->description, + 'schedule_type' => $schedule->schedule_type, + 'scheduled_date' => $targetDate, + 'amount' => $schedule->amount, + 'currency' => $schedule->currency, + 'related_bank_account_id' => $schedule->related_bank_account_id, + 'counterparty' => $schedule->counterparty, + 'category' => $schedule->category, + 'status' => FundSchedule::STATUS_PENDING, + 'is_recurring' => $schedule->is_recurring, + 'recurrence_rule' => $schedule->recurrence_rule, + 'recurrence_end_date' => $schedule->recurrence_end_date, + 'memo' => $schedule->memo, + ]); + + $copiedCount++; + } + + return $copiedCount; + } + // ========================================================================= // 일괄 작업 // ========================================================================= diff --git a/resources/views/finance/fund-schedules/index.blade.php b/resources/views/finance/fund-schedules/index.blade.php index 3029f30f..9de41eb9 100644 --- a/resources/views/finance/fund-schedules/index.blade.php +++ b/resources/views/finance/fund-schedules/index.blade.php @@ -122,6 +122,15 @@ class="p-2 hover:bg-gray-100 rounded-lg transition-colors"> + + {{-- 일정 복사 버튼 --}} + @@ -144,6 +153,60 @@ class="p-2 hover:bg-gray-100 rounded-lg transition-colors"> +{{-- 일정 복사 모달 --}} + + {{-- 일정 편집/등록 모달 --}}