commit 12.01

This commit is contained in:
Владимир
2026-01-12 14:25:15 +00:00
parent 36084ba590
commit ae5ab2554b
26 changed files with 1116 additions and 1083 deletions

View File

@@ -2,168 +2,92 @@
namespace App\Http\Controllers;
use App\Models\EmployeeAvailability;
use App\Models\User;
use Illuminate\Http\Request;
use App\Models\EmployeeAvailability;
class AvailabilitiesController extends Controller
{
// GET api/admin/availabilities?employee_id=5&date=2025-06-15
public function index(Request $request)
{
$query = EmployeeAvailability::query();
if ($request->employee_id) {
$query->where('employee_id', $request->employee_id);
}
if ($request->date) {
$query->where('date', $request->date);
}
$availabilities = $query->get();
$availabilities = EmployeeAvailability::with('employee')
->when($request->has('employee_id'), function ($query) use ($request) {
$query->where('employee_id', $request->employee_id);
})
->when($request->has('date'), function ($query) use ($request) {
$query->where('date', $request->date);
})
->get();
return response()->json($availabilities);
}
// POST api/admin/availabilities - создать один слот
public function store(Request $request)
{
$request->validate([
$validated = $request->validate([
'employee_id' => 'required|exists:users,id',
'date' => 'required|date',
'starttime' => 'required',
'endtime' => 'required|after:starttime',
'isavailable' => 'boolean'
'start_time' => 'required|date_format:H:i:s',
'end_time' => 'required|date_format:H:i:s|after:start_time',
'is_available' => 'boolean'
]);
$availability = EmployeeAvailability::create($request->all());
$availability = EmployeeAvailability::create($validated);
return response()->json($availability, 201);
}
// POST api/admin/availabilities/bulk - создать несколько слотов
public function bulkStore(Request $request)
{
$request->validate([
$validated = $request->validate([
'employee_id' => 'required|exists:users,id',
'date' => 'required|date',
'intervals' => 'required|array|min:1'
'intervals' => 'required|array'
]);
$availabilities = [];
foreach ($request->intervals as $interval) {
$availability = EmployeeAvailability::create([
EmployeeAvailability::create([
'employee_id' => $request->employee_id,
'date' => $request->date,
'starttime' => $interval['start'],
'endtime' => $interval['end'],
'isavailable' => true
'start_time' => $interval['start'],
'end_time' => $interval['end'],
'is_available' => true
]);
$availabilities[] = $availability;
}
return response()->json($availabilities, 201);
return response()->json(['message' => 'Расписание добавлено']);
}
// DELETE api/admin/availabilities/{id} - удалить слот (брони остаются!)
public function destroy($id)
{
$availability = EmployeeAvailability::findOrFail($id);
$availability->delete();
return response()->json(['message' => 'Слот удален из расписания (брони сохранены)']);
return response()->json(['message' => 'Слот удалён']);
}
// ✅ Единственный метод publicAvailability
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 мин
{
$serviceId = $request->query('service_id');
$date = $request->query('date');
if (!$serviceId || !$date) {
return response()->json([]);
}
$availabilities = EmployeeAvailability::where('date', $date)
->where('is_available', true)
->get();
$slots = [];
foreach ($availabilities as $avail) {
$slots[] = [
'employee_id' => $avail->employee_id,
'start' => substr($avail->start_time, 0, 5),
'end' => substr($avail->end_time, 0, 5),
];
}
return response()->json($slots);
}
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
]);
}
}