employee_id) { $query->where('employee_id', $request->employee_id); } if ($request->date) { $query->where('date', $request->date); } $availabilities = $query->get(); return response()->json($availabilities); } // POST api/admin/availabilities - создать один слот public function store(Request $request) { $request->validate([ 'employee_id' => 'required|exists:users,id', 'date' => 'required|date', 'starttime' => 'required', 'endtime' => 'required|after:starttime', 'isavailable' => 'boolean' ]); $availability = EmployeeAvailability::create($request->all()); return response()->json($availability, 201); } // POST api/admin/availabilities/bulk - создать несколько слотов public function bulkStore(Request $request) { $request->validate([ 'employee_id' => 'required|exists:users,id', 'date' => 'required|date', 'intervals' => 'required|array|min:1' ]); $availabilities = []; foreach ($request->intervals as $interval) { $availability = EmployeeAvailability::create([ 'employee_id' => $request->employee_id, 'date' => $request->date, 'starttime' => $interval['start'], 'endtime' => $interval['end'], 'isavailable' => true ]); $availabilities[] = $availability; } return response()->json($availabilities, 201); } // DELETE api/admin/availabilities/{id} - удалить слот (брони остаются!) public function destroy($id) { $availability = EmployeeAvailability::findOrFail($id); $availability->delete(); return response()->json(['message' => 'Слот удален из расписания (брони сохранены)']); } public function publicAvailability(Request $request) { $serviceId = $request->query('service_id'); $date = $request->query('date'); if (!$serviceId || !$date) { return response()->json(['error' => 'service_id и date обязательны'], 400); } // Найти услугу и получить длительность $service = \App\Models\Services::find($serviceId); if (!$service) { return response()->json(['error' => 'Услуга не найдена'], 404); } $durationMinutes = $service->durationminutes; // Найти сотрудников с расписанием на эту дату $availabilities = \App\Models\EmployeeAvailability::where('date', $date) ->where('isavailable', true) ->with('employee') // связь с User ->get(); $freeSlots = []; foreach ($availabilities as $availability) { $employeeId = $availability->employee_id; // Найти занятые слоты этого сотрудника $bookings = \App\Models\Booking::where('employee_id', $employeeId) ->where('bookingdate', $date) ->where('status', '!=', 'cancelled') ->pluck('starttime', 'endtime'); // Генерировать возможные слоты с учетом duration $start = new \DateTime($availability->starttime); $end = new \DateTime($availability->endtime); $current = clone $start; while ($current < $end) { $slotEnd = clone $current; $slotEnd->modify("+{$durationMinutes} minutes"); // Проверить пересечение с бронями $isFree = true; foreach ($bookings as $bookingStart => $bookingEnd) { $bookingStartTime = new \DateTime($bookingStart); $bookingEndTime = new \DateTime($bookingEnd); if ($current < $bookingEndTime && $slotEnd > $bookingStartTime) { $isFree = false; break; } } if ($isFree && $slotEnd <= $end) { $freeSlots[] = [ 'employee_id' => $employeeId, 'start' => $current->format('H:i'), 'end' => $slotEnd->format('H:i') ]; } $current->modify('+30 minutes'); // шаг 30 мин } } return response()->json($freeSlots); } public function cancel(Request $request, $id) { $booking = Booking::findOrFail($id); // Проверка: только автор брони может отменить if ($booking->client_id != auth()->id()) { return response()->json(['error' => 'Можете отменить только свою бронь'], 403); } // Проверка: нельзя отменить уже отмененную/выполненную if ($booking->status != 'confirmed') { return response()->json(['error' => 'Можно отменить только подтвержденные брони'], 400); } // Обновить статус $booking->update([ 'status' => 'cancelled', 'cancelledby' => 'client', 'cancelreason' => $request->reason ?? null ]); return response()->json([ 'message' => 'Бронь отменена', 'booking' => $booking ]); } }