tenantId(); $userId = $this->apiUserId(); $schedules = collect(); // 타입 필터에 따라 데이터 수집 if ($type === null || $type === 'order') { $schedules = $schedules->merge( $this->getWorkOrderSchedules($tenantId, $startDate, $endDate, $departmentFilter, $userId) ); } if ($type === null || $type === 'construction') { $schedules = $schedules->merge( $this->getContractSchedules($tenantId, $startDate, $endDate, $departmentFilter, $userId) ); } if ($type === null || $type === 'schedule') { $schedules = $schedules->merge( $this->getLeaveSchedules($tenantId, $startDate, $endDate, $departmentFilter, $userId) ); } // 범용 일정 (본사 공통 + 테넌트 일정): 항상 포함 또는 'other' 필터 시 if ($type === null || $type === 'other') { $schedules = $schedules->merge( $this->getGeneralSchedules($tenantId, $startDate, $endDate) ); } // 어음 만기일 if ($type === null || $type === 'bill') { $schedules = $schedules->merge( $this->getBillSchedules($tenantId, $startDate, $endDate) ); } // 매입 결제 예정일 if ($type === null || $type === 'expected_expense') { $schedules = $schedules->merge( $this->getExpectedExpenseSchedules($tenantId, $startDate, $endDate) ); } // 수주 납기일 if ($type === null || $type === 'delivery') { $schedules = $schedules->merge( $this->getDeliverySchedules($tenantId, $startDate, $endDate) ); } // 출고 예정일 if ($type === null || $type === 'shipment') { $schedules = $schedules->merge( $this->getShipmentSchedules($tenantId, $startDate, $endDate) ); } // startDate 기준 정렬 $sortedSchedules = $schedules ->sortBy('startDate') ->values() ->toArray(); return [ 'items' => $sortedSchedules, 'total_count' => count($sortedSchedules), ]; } /** * 작업지시(발주) 일정 조회 */ private function getWorkOrderSchedules( int $tenantId, string $startDate, string $endDate, string $departmentFilter, int $userId ): Collection { $query = WorkOrder::query() ->where('tenant_id', $tenantId) ->where('is_active', true) ->whereNotNull('scheduled_date') ->where('scheduled_date', '>=', $startDate) ->where('scheduled_date', '<=', $endDate) ->with(['assignee:id,name', 'assignee.tenantProfile:id,user_id,department_id', 'assignee.tenantProfile.department:id,name']); // 부서 필터 적용 if ($departmentFilter === 'personal') { $query->where('assignee_id', $userId); } // department 필터는 부서별 필터링 로직 추가 필요 (현재는 전체) $workOrders = $query->orderBy('scheduled_date')->limit(100)->get(); return $workOrders->map(function ($wo) { $assigneeName = $wo->assignee?->name; $departmentName = $wo->assignee?->tenantProfile?->department?->name; return [ 'id' => 'wo_'.$wo->id, 'title' => $wo->project_name ?? $wo->work_order_no, 'startDate' => $wo->scheduled_date?->format('Y-m-d'), 'endDate' => $wo->scheduled_date?->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'order', 'department' => $departmentName, 'personName' => $assigneeName, 'color' => null, ]; }); } /** * 계약(시공) 일정 조회 */ private function getContractSchedules( int $tenantId, string $startDate, string $endDate, string $departmentFilter, int $userId ): Collection { $query = Contract::query() ->where('tenant_id', $tenantId) ->where('is_active', true) ->whereNotNull('contract_start_date') ->where(function ($q) use ($startDate, $endDate) { // 기간이 겹치는 계약 조회 $q->where(function ($sub) use ($startDate, $endDate) { $sub->where('contract_start_date', '<=', $endDate) ->where(function ($inner) use ($startDate) { $inner->where('contract_end_date', '>=', $startDate) ->orWhereNull('contract_end_date'); }); }); }) ->with(['constructionPm:id,name', 'constructionPm.tenantProfile:id,user_id,department_id', 'constructionPm.tenantProfile.department:id,name']); // 부서 필터 적용 if ($departmentFilter === 'personal') { $query->where('construction_pm_id', $userId); } $contracts = $query->orderBy('contract_start_date')->limit(100)->get(); return $contracts->map(function ($contract) { $pmName = $contract->constructionPm?->name; $departmentName = $contract->constructionPm?->tenantProfile?->department?->name; return [ 'id' => 'contract_'.$contract->id, 'title' => $contract->project_name ?? $contract->contract_code, 'startDate' => $contract->contract_start_date?->format('Y-m-d'), 'endDate' => $contract->contract_end_date?->format('Y-m-d') ?? $contract->contract_start_date?->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'construction', 'department' => $departmentName, 'personName' => $pmName, 'color' => null, ]; }); } /** * 휴가 일정 조회 */ private function getLeaveSchedules( int $tenantId, string $startDate, string $endDate, string $departmentFilter, int $userId ): Collection { $query = Leave::query() ->where('tenant_id', $tenantId) ->where('status', 'approved') ->where(function ($q) use ($startDate, $endDate) { // 기간이 겹치는 휴가 조회 $q->where('start_date', '<=', $endDate) ->where('end_date', '>=', $startDate); }) ->with(['user:id,name', 'user.tenantProfile:id,user_id,department_id', 'user.tenantProfile.department:id,name']); // 부서 필터 적용 if ($departmentFilter === 'personal') { $query->where('user_id', $userId); } $leaves = $query->orderBy('start_date')->limit(100)->get(); return $leaves->map(function ($leave) { $userName = $leave->user?->name; $departmentName = $leave->user?->tenantProfile?->department?->name; $leaveType = $leave->type ?? 'leave'; $title = $userName ? __('message.calendar.leave_title', ['name' => $userName]) : __('message.calendar.leave_default'); return [ 'id' => 'leave_'.$leave->id, 'title' => $title, 'startDate' => $leave->start_date?->format('Y-m-d'), 'endDate' => $leave->end_date?->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'schedule', 'department' => $departmentName, 'personName' => $userName, 'color' => null, ]; }); } /** * 일정 등록 */ public function createSchedule(array $data): array { $schedule = Schedule::create([ 'tenant_id' => $this->tenantId(), 'title' => $data['title'], 'description' => $data['description'] ?? null, 'start_date' => $data['start_date'], 'end_date' => $data['end_date'], 'start_time' => $data['start_time'] ?? null, 'end_time' => $data['end_time'] ?? null, 'is_all_day' => $data['is_all_day'] ?? true, 'type' => Schedule::TYPE_EVENT, 'color' => $data['color'] ?? null, 'is_active' => true, 'created_by' => $this->apiUserId(), ]); return [ 'id' => $schedule->id, 'title' => $schedule->title, 'start_date' => $schedule->start_date?->format('Y-m-d'), 'end_date' => $schedule->end_date?->format('Y-m-d'), ]; } /** * 일정 수정 */ public function updateSchedule(int $id, array $data): array { $schedule = Schedule::where('tenant_id', $this->tenantId()) ->findOrFail($id); $schedule->update([ 'title' => $data['title'], 'description' => $data['description'] ?? null, 'start_date' => $data['start_date'], 'end_date' => $data['end_date'], 'start_time' => $data['start_time'] ?? null, 'end_time' => $data['end_time'] ?? null, 'is_all_day' => $data['is_all_day'] ?? true, 'color' => $data['color'] ?? null, 'updated_by' => $this->apiUserId(), ]); return [ 'id' => $schedule->id, 'title' => $schedule->title, 'start_date' => $schedule->start_date?->format('Y-m-d'), 'end_date' => $schedule->end_date?->format('Y-m-d'), ]; } /** * 일정 삭제 (소프트 삭제) */ public function deleteSchedule(int $id): array { $schedule = Schedule::where('tenant_id', $this->tenantId()) ->findOrFail($id); $schedule->update(['deleted_by' => $this->apiUserId()]); $schedule->delete(); return [ 'id' => $schedule->id, ]; } /** * 범용 일정 조회 (본사 공통 + 테넌트 일정) */ private function getGeneralSchedules( int $tenantId, string $startDate, string $endDate ): Collection { $schedules = Schedule::query() ->forTenant($tenantId) ->active() ->betweenDates($startDate, $endDate) ->with(['creator:id,name']) ->orderBy('start_date') ->limit(100) ->get(); return $schedules->map(function ($schedule) { return [ 'id' => 'schedule_'.$schedule->id, 'title' => $schedule->title, 'startDate' => $schedule->start_date?->format('Y-m-d'), 'endDate' => $schedule->end_date?->format('Y-m-d') ?? $schedule->start_date?->format('Y-m-d'), 'startTime' => $schedule->start_time, 'endTime' => $schedule->end_time, 'isAllDay' => $schedule->is_all_day, 'type' => 'other', 'department' => null, 'personName' => $schedule->creator?->name, 'color' => $schedule->color, ]; }); } /** * 어음 만기일 일정 조회 */ private function getBillSchedules( int $tenantId, string $startDate, string $endDate ): Collection { $excludedStatuses = [ 'paymentComplete', 'dishonored', ]; $bills = Bill::query() ->where('tenant_id', $tenantId) ->whereNotNull('maturity_date') ->where('maturity_date', '>=', $startDate) ->where('maturity_date', '<=', $endDate) ->whereNotIn('status', $excludedStatuses) ->orderBy('maturity_date') ->limit(100) ->get(); return $bills->map(function ($bill) { $clientName = $bill->display_client_name ?? $bill->client_name ?? ''; return [ 'id' => 'bill_'.$bill->id, 'title' => '[만기] '.$clientName.' '.number_format($bill->amount).'원', 'startDate' => $bill->maturity_date->format('Y-m-d'), 'endDate' => $bill->maturity_date->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'bill', 'department' => null, 'personName' => null, 'color' => null, ]; }); } /** * 매입 결제 예정일 조회 */ private function getExpectedExpenseSchedules( int $tenantId, string $startDate, string $endDate ): Collection { $expenses = ExpectedExpense::query() ->where('tenant_id', $tenantId) ->whereNotNull('expected_payment_date') ->where('expected_payment_date', '>=', $startDate) ->where('expected_payment_date', '<=', $endDate) ->where('payment_status', '!=', 'paid') ->with(['client:id,name']) ->orderBy('expected_payment_date') ->limit(100) ->get(); return $expenses->map(function ($expense) { $clientName = $expense->client?->name ?? $expense->client_name ?? ''; return [ 'id' => 'expense_'.$expense->id, 'title' => '[결제] '.$clientName.' '.number_format($expense->amount).'원', 'startDate' => $expense->expected_payment_date->format('Y-m-d'), 'endDate' => $expense->expected_payment_date->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'expected_expense', 'department' => null, 'personName' => null, 'color' => null, ]; }); } /** * 수주 납기일 조회 */ private function getDeliverySchedules( int $tenantId, string $startDate, string $endDate ): Collection { $activeStatuses = [ 'CONFIRMED', 'IN_PROGRESS', 'IN_PRODUCTION', 'PRODUCED', 'SHIPPING', ]; $orders = Order::query() ->where('tenant_id', $tenantId) ->whereNotNull('delivery_date') ->where('delivery_date', '>=', $startDate) ->where('delivery_date', '<=', $endDate) ->whereIn('status_code', $activeStatuses) ->with(['client:id,name']) ->orderBy('delivery_date') ->limit(100) ->get(); return $orders->map(function ($order) { $clientName = $order->client?->name ?? $order->client_name ?? ''; $siteName = $order->site_name ?? $order->order_no; return [ 'id' => 'delivery_'.$order->id, 'title' => '[납기] '.$clientName.' '.$siteName, 'startDate' => $order->delivery_date->format('Y-m-d'), 'endDate' => $order->delivery_date->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'delivery', 'department' => null, 'personName' => null, 'color' => null, ]; }); } /** * 출고 예정일 조회 */ private function getShipmentSchedules( int $tenantId, string $startDate, string $endDate ): Collection { $shipments = Shipment::query() ->where('tenant_id', $tenantId) ->whereNotNull('scheduled_date') ->where('scheduled_date', '>=', $startDate) ->where('scheduled_date', '<=', $endDate) ->whereIn('status', ['scheduled', 'ready']) ->with(['client:id,name', 'order:id,site_name']) ->orderBy('scheduled_date') ->limit(100) ->get(); return $shipments->map(function ($shipment) { $clientName = $shipment->client?->name ?? $shipment->customer_name ?? ''; $siteName = $shipment->site_name ?? $shipment->order?->site_name ?? $shipment->shipment_no; return [ 'id' => 'shipment_'.$shipment->id, 'title' => '[출고] '.$clientName.' '.$siteName, 'startDate' => $shipment->scheduled_date->format('Y-m-d'), 'endDate' => $shipment->scheduled_date->format('Y-m-d'), 'startTime' => null, 'endTime' => null, 'isAllDay' => true, 'type' => 'shipment', 'department' => null, 'personName' => null, 'color' => null, ]; }); } }