'schedule', 'title' => 'График монтажей', 'id' => 'schedule', ]; public function index(Request $request) { $this->data['districts'] = District::query()->get()->pluck('name', 'id'); $this->data['areas'] = Area::query()->get()->pluck('name', 'id'); $this->data['brigadiers'] = User::query()->where('role', Role::BRIGADIER)->get()->pluck('name', 'id'); $this->data['scheduleYear'] = (int)$request->get('year', date('Y')); if ($this->data['scheduleYear'] < 2000 || $this->data['scheduleYear'] > 2100) { $this->data['scheduleYear'] = (int)date('Y'); } $tab = $request->get('tab', 'week'); $this->data['activeTab'] = in_array($tab, ['week', 'month'], true) ? $tab : 'week'; $this->data['weekNumber'] = $request->get('week' ,date('W')); $weekDates = [ 'mon' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber']), 'tue' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 2), 'wed' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 3), 'thu' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 4), 'fri' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 5), 'sat' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 6), 'sun' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 7), ]; $this->data['weekDates'] = $weekDates; $schedules = []; foreach ($weekDates as $date) { $schedules[$date] = null; } $result = Schedule::query() ->whereBetween('installation_date', [$weekDates['mon'], $weekDates['sun']]) ->with(['brigadier', 'district', 'area']) ->get(); $this->data['scheduleStatusMap'] = $this->buildScheduleStatusMap($result); foreach ($result as $schedule) { $schedules[$schedule->installation_date][] = $schedule; } $this->data['schedules'] = $schedules; $monthParam = $request->get('month'); $monthNumber = (int)date('m'); if (is_string($monthParam)) { if (preg_match('/^(0[1-9]|1[0-2])$/', $monthParam)) { $monthNumber = (int)$monthParam; } elseif (preg_match('/^(\\d{4})-(0[1-9]|1[0-2])$/', $monthParam, $matches)) { $this->data['scheduleYear'] = (int)$matches[1]; $monthNumber = (int)$matches[2]; } } $monthStart = Carbon::createFromDate($this->data['scheduleYear'], $monthNumber, 1)->startOfMonth(); $monthEnd = $monthStart->copy()->endOfMonth(); $gridStart = $monthStart->copy()->startOfWeek(Carbon::MONDAY); $gridEnd = $monthEnd->copy()->endOfWeek(Carbon::SUNDAY); $cursor = $gridStart->copy(); $monthDays = []; while ($cursor->lte($gridEnd)) { $monthDays[] = [ 'date' => $cursor->toDateString(), 'day' => (int)$cursor->format('j'), 'week' => (int)$cursor->isoWeek(), 'weekYear' => (int)$cursor->isoWeekYear(), 'inMonth' => $cursor->month === $monthStart->month, 'isToday' => $cursor->isToday(), ]; $cursor->addDay(); } $monthSchedules = Schedule::query() ->whereBetween('installation_date', [$monthStart->toDateString(), $monthEnd->toDateString()]) ->with(['brigadier']) ->get(); $monthScheduleColors = []; $monthBrigadierLegend = []; foreach ($monthSchedules as $schedule) { $date = $schedule->installation_date; $brigadierId = $schedule->brigadier_id ?? 0; if (!isset($monthScheduleColors[$date])) { $monthScheduleColors[$date] = []; } if (!isset($monthScheduleColors[$date][$brigadierId])) { $monthScheduleColors[$date][$brigadierId] = [ 'color' => $schedule->brigadier?->color ?? '#cccccc', 'name' => $schedule->brigadier?->name ?? '', ]; } if (!isset($monthBrigadierLegend[$brigadierId])) { $monthBrigadierLegend[$brigadierId] = [ 'color' => $schedule->brigadier?->color ?? '#cccccc', 'name' => $schedule->brigadier?->name ?? '', ]; } } foreach ($monthScheduleColors as $date => $colors) { $monthScheduleColors[$date] = array_values($colors); } $this->data['monthGrid'] = array_chunk($monthDays, 7); $this->data['monthLabel'] = $monthStart->isoFormat('MMMM YYYY'); $this->data['monthNumber'] = $monthNumber; $this->data['monthPrev'] = $monthStart->copy()->subMonth()->format('Y-m'); $this->data['monthNext'] = $monthStart->copy()->addMonth()->format('Y-m'); $this->data['monthValue'] = $monthStart->format('Y-m'); $this->data['monthScheduleColors'] = $monthScheduleColors; $this->data['monthBrigadierLegend'] = array_values($monthBrigadierLegend); return view('schedule.index', $this->data); } public function createFromOrder(CreateScheduleFromOrderRequest $request) { $validated = $request->validated(); // delete all auto schedules for this order if(isset($validated['delete_old_records'])) { Schedule::query() ->where('order_id', $validated['order_id']) ->where('source', 'Площадки') ->where('manual', false) ->delete(); } // create all records in schedule $order = Order::query() ->where('id', $validated['order_id']) ->first(); $errors = []; if(!$order->brigadier_id) $errors[] = 'Не указан бригадир!'; if(!$order->installation_date) $errors[] = 'Не указана дата монтажа!'; if(!$order->install_days) $errors[] = 'Не указан срок монтажа!'; if($errors) { return redirect()->route('order.show', $order->id)->with(['danger' => $errors]); } if(empty($validated['skus'])) { foreach ($order->products_sku as $psku) $validated['skus'][] = $psku->id; } $mafs = []; foreach ($validated['skus'] as $skuId) { $sku = ProductSKU::query()->where('id', $skuId)->first(); if(!isset($mafs[$sku->product->article])) { $mafs[$sku->product->article] = 1; } else { $mafs[$sku->product->article] += 1; } } $mafsText = ''; $mafsCount = 0; foreach ($mafs as $article => $count) { $mafsText .= $article . ' - ' . $count . "\r\n"; $mafsCount += $count; } $first = true; for ($iDays = 1; $iDays < $order->install_days + 1; $iDays++) { $instDate = date('Y-m-d', strtotime('+' . $iDays - 1 . ' days', strtotime($order->installation_date))); $schedule = Schedule::query() ->create([ 'order_id' => $validated['order_id'], 'address_code' => $validated['order_id'], 'source' => 'Площадки', 'installation_date' => $instDate, 'manual' => false, 'district_id' => $order->district_id, 'area_id' => $order->area_id, 'object_address' => $order->object_address, 'object_type' => $order->objectType->name, 'mafs' => $mafsText, 'mafs_count' => $mafsCount, 'brigadier_id' => $order->brigadier_id, 'comment' => $validated['comment'], ]); if($first && isset($validated['send_notifications'])) { $first = false; NotifyOrderInScheduleJob::dispatch($schedule); } } return redirect()->route('schedule.index'); } public function createFromReclamation(CreateScheduleFromReclamationRequest $request) { $validated = $request->validated(); // delete all auto schedules for this order if(isset($validated['delete_old_records'])) { Schedule::query() ->where('address_code', 'РЕКЛ-' . $validated['reclamation_id']) ->where('source', 'Рекламации') ->where('manual', false) ->delete(); } $reclamation = Reclamation::query() ->where('id', $validated['reclamation_id']) ->first(); $order = $reclamation->order; $mafs = []; if(empty($validated['skus'])) { foreach ($reclamation->skus as $sku) if(!isset($mafs[$sku->product->article])) { $mafs[$sku->product->article] = 1; } else { $mafs[$sku->product->article] += 1; } } $mafsCount = 0; $mafsText = ''; foreach ($mafs as $article => $count) { $mafsCount += $count; $mafsText .= $article . ' - ' . $count . "\r\n"; } $first = true; for ($iDays = 1; $iDays < $reclamation->work_days + 1; $iDays++) { $instDate = date('Y-m-d', strtotime('+' . $iDays - 1 . ' days', strtotime($reclamation->start_work_date))); $schedule = Schedule::query() ->create([ 'order_id' => $order->id, 'address_code' => 'РЕКЛ-' . $validated['reclamation_id'], 'source' => 'Рекламации', 'installation_date' => $instDate, 'manual' => false, 'district_id' => $order->district_id, 'area_id' => $order->area_id, 'object_address' => $order->object_address, 'object_type' => $reclamation->reason, 'mafs' => $mafsText, 'mafs_count' => $mafsCount, 'brigadier_id' => $reclamation->brigadier_id, 'comment' => $reclamation->guarantee, ]); if($first && isset($validated['send_notifications'])) { $first = false; NotifyOrderInScheduleJob::dispatch($schedule); } } return redirect()->route('schedule.index'); } public function update(UpdateScheduleRequest $request) { $validated = $request->validated(); if(isset($validated['id'])) { Schedule::query() ->where('id', $validated['id']) ->update($validated); $schedule = Schedule::query()->find($validated['id']); } else { $schedule = Schedule::query() ->create([ 'address_code' => $validated['address_code'] ?? '-', 'installation_date' => $validated['installation_date'], 'manual' => true, 'district_id' => $validated['district_id'], 'area_id' => $validated['area_id'], 'object_address' => $validated['object_address'], 'object_type' => $validated['object_type'], 'mafs' => $validated['mafs'], 'mafs_count' => $validated['mafs_count'], 'brigadier_id' => $validated['brigadier_id'], 'comment' => $validated['comment'], 'transport' => $validated['transport'] ?? null, 'admin_comment' => $validated['admin_comment'] ?? null, ]); } NotifyOrderInScheduleJob::dispatch($schedule); return redirect()->back(); } public function delete(Schedule $schedule) { $schedule->delete(); return redirect()->back(); } public function export(ExportScheduleRequest $request) { $startDate = $request->validated()['start_date']; $endDate = $request->validated()['end_date']; $schedules = Schedule::query() ->where('installation_date', '>=', $startDate) ->where('installation_date', '<=', $endDate) ->orderBy('installation_date') ->orderBy('brigadier_id') ->get(); ExportScheduleJob::dispatch($schedules, $request->user()->id); return redirect()->route('schedule.index', [ 'week' => (int) $request->validated()['week'], 'year' => (int) $request->input('year', date('Y')), ]) ->with(['success' => 'Задача генерации графика создана!']); } private function buildScheduleStatusMap($schedules): array { $statusMap = []; $orderIds = []; $reclamationScheduleIds = []; foreach ($schedules as $schedule) { $statusMap[$schedule->id] = [ 'name' => '-', 'color' => 'secondary', ]; if ($schedule->source === 'Площадки' && $schedule->order_id) { $orderIds[] = (int)$schedule->order_id; continue; } if ($schedule->source === 'Рекламации') { $reclamationId = $this->extractReclamationId((string)$schedule->address_code); if ($reclamationId) { $reclamationScheduleIds[$reclamationId][] = $schedule->id; } } } if ($orderIds) { $orderStatuses = Order::query() ->withoutGlobalScopes() ->whereIn('id', array_unique($orderIds)) ->pluck('order_status_id', 'id') ->all(); foreach ($schedules as $schedule) { if ($schedule->source !== 'Площадки' || !$schedule->order_id) { continue; } $statusId = $orderStatuses[(int)$schedule->order_id] ?? null; if ($statusId) { $statusMap[$schedule->id] = [ 'name' => Order::STATUS_NAMES[$statusId] ?? '-', 'color' => Order::STATUS_COLOR[$statusId] ?: 'secondary', ]; } } } if ($reclamationScheduleIds) { $reclamationStatuses = Reclamation::query() ->withoutGlobalScopes() ->whereIn('id', array_keys($reclamationScheduleIds)) ->pluck('status_id', 'id') ->all(); foreach ($reclamationScheduleIds as $reclamationId => $scheduleIds) { $statusId = $reclamationStatuses[$reclamationId] ?? null; $statusName = $statusId ? (Reclamation::STATUS_NAMES[$statusId] ?? '-') : '-'; $statusColor = $statusId ? (ReclamationStatus::STATUS_COLOR[$statusId] ?? 'secondary') : 'secondary'; foreach ($scheduleIds as $scheduleId) { $statusMap[$scheduleId] = [ 'name' => $statusName, 'color' => $statusColor, ]; } } } return $statusMap; } private function extractReclamationId(string $addressCode): ?int { if (preg_match('/^РЕКЛ-(\d+)$/u', $addressCode, $matches)) { return (int)$matches[1]; } return null; } }