validate([ 'service_id' => 'required|exists:services,id', 'employee_id' => 'required|exists:users,id', 'date' => 'required|date', 'starttime' => 'required' ]); $clientId = auth()->id(); if (!$clientId) { return response()->json(['error' => 'Авторизация обязательна'], 401); } $service = Services::where('id', $request->service_id) ->where('isactive', true) ->first(); if (!$service) { return response()->json(['error' => 'Услуга неактивна или не найдена'], 400); } $durationMinutes = $service->durationminutes; $endtime = date('H:i:s', strtotime($request->starttime . " +{$durationMinutes} minutes")); $availability = EmployeeAvailability::where('employee_id', $request->employee_id) ->where('date', $request->date) ->where('starttime', '<=', $request->starttime) ->where('endtime', '>=', $endtime) ->where('isavailable', true) ->first(); if (!$availability) { return response()->json(['error' => 'Сотрудник недоступен в это время'], 400); } $bookingExists = Booking::where('employee_id', $request->employee_id) ->where('bookingdate', $request->date) ->where('starttime', $request->starttime) ->whereIn('status', ['confirmed', 'completed']) ->exists(); if ($bookingExists) { return response()->json(['error' => 'Слот уже забронирован'], 400); } $bookingNumber = 'CL-' . date('Y') . '-' . str_pad(Booking::count() + 1, 4, '0', STR_PAD_LEFT); $booking = Booking::create([ 'bookingnumber' => $bookingNumber, 'client_id' => $clientId, 'employee_id' => $request->employee_id, 'service_id' => $request->service_id, 'bookingdate' => $request->date, 'starttime' => $request->starttime, 'endtime' => $endtime, 'status' => 'confirmed' ]); return response()->json([ 'booking' => $booking, 'message' => 'Бронирование создано №' . $bookingNumber ], 201); } 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 ]); } public function adminCancel(Request $request, $id) { $booking = Booking::findOrFail($id); if (!auth()->user()->isEmployeeOrAdmin()) { return response()->json(['error' => 'Доступ только для админов/сотрудников'], 403); } $booking->update([ 'status' => 'cancelled', 'cancelledby' => 'admin', 'cancelreason' => $request->reason ?? null ]); return response()->json([ 'message' => 'Бронь отменена администратором', 'booking' => $booking ]); } public function clientIndex(Request $request) { $clientId = auth()->id(); $query = Booking::where('client_id', $clientId); if ($request->date) { $query->where('bookingdate', $request->date); } if ($request->status) { $query->where('status', $request->status); } $bookings = $query->with(['service', 'employee']) ->orderBy('bookingdate', 'desc') ->orderBy('starttime', 'asc') ->get(); return response()->json($bookings); } public function adminIndex(Request $request) { if (!auth()->user()->isEmployeeOrAdmin()) { return response()->json(['error' => 'Доступ запрещен'], 403); } $query = Booking::with(['service', 'client', 'employee']); if ($request->date) { $query->where('bookingdate', $request->date); } if ($request->status) { $query->where('status', $request->status); } if ($request->employee_id) { $query->where('employee_id', $request->employee_id); } $bookings = $query->orderBy('bookingdate', 'desc') ->orderBy('starttime', 'asc') ->get(); return response()->json($bookings); } }