where('schedule_type', 'default') ->orderBy('date', 'desc') ->orderBy('start_time', 'desc') ->get(); } public function getById($id) { return Schedule::with(['client.user', 'provider.user', 'address'])->findOrFail($id); } public function create(array $data) { $data['code'] = str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT); $provider = Provider::findOrFail($data['provider_id']); $data['total_amount'] = $this->calculateAmount($provider, $data['period_type']); $this->validateProviderAvailability($data); return Schedule::create($data); } public function createMultiple(array $baseData, array $schedules) { foreach ($schedules as $schedule) { $validationData = array_merge($baseData, $schedule); try { $this->validateProviderAvailability($validationData); } catch (\Exception $e) { throw new \Exception("Prestador não disponível para a data " . Carbon::parse($schedule['date'])->format('d/m/Y') . ": " . $e->getMessage()); } } $createdSchedules = []; foreach ($schedules as $schedule) { $scheduleData = array_merge($baseData, $schedule, [ 'code' => str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT), ]); $createdSchedules[] = Schedule::create($scheduleData); } return $createdSchedules; } public function update($id, array $data) { $schedule = Schedule::findOrFail($id); if (isset($data['provider_id']) || isset($data['period_type'])) { $providerId = $data['provider_id'] ?? $schedule->provider_id; $periodType = $data['period_type'] ?? $schedule->period_type; $provider = Provider::findOrFail($providerId); $data['total_amount'] = $this->calculateAmount($provider, $periodType); } if (isset($data['date']) || isset($data['start_time']) || isset($data['provider_id'])) { $validationData = array_merge($schedule->toArray(), $data); $this->validateProviderAvailability($validationData, $id); } $schedule->update($data); return $schedule->fresh(['client.user', 'provider.user', 'address']); } public function delete($id) { $schedule = Schedule::findOrFail($id); $schedule->delete(); return $schedule; } private function calculateAmount(Provider $provider, string $periodType): float { $hourlyRates = [ '2' => $provider->daily_price_2h ?? 0, '4' => $provider->daily_price_4h ?? 0, '6' => $provider->daily_price_6h ?? 0, '8' => $provider->daily_price_8h ?? 0, ]; return $hourlyRates[$periodType] ?? 0; } private function validateProviderAvailability(array $data, $excludeScheduleId = null) { $provider = Provider::findOrFail($data['provider_id']); $date = Carbon::parse($data['date']); $startTime = $data['start_time']; $endTime = $data['end_time']; $dayOfWeek = $date->dayOfWeek; $startHour = (int) substr($startTime, 0, 2); $period = $startHour < 12 ? 'morning' : 'afternoon'; $workingDay = ProviderWorkingDay::where('provider_id', $data['provider_id']) ->where('day', $dayOfWeek) ->where('period', $period) ->first(); if (!$workingDay) { throw new \Exception("Prestador não trabalha neste dia/período."); } $blockedDay = ProviderBlockedDay::where('provider_id', $data['provider_id']) ->where('date', $date->format('Y-m-d')) ->where(function ($query) use ($startTime, $endTime) { $query->where('period', 'full') ->orWhere(function ($q) use ($startTime, $endTime) { $q->where('period', 'partial') ->where(function ($q2) use ($startTime, $endTime) { $q2->whereBetween('init_hour', [$startTime, $endTime]) ->orWhereBetween('end_hour', [$startTime, $endTime]) ->orWhere(function ($q3) use ($startTime, $endTime) { $q3->where('init_hour', '<=', $startTime) ->where('end_hour', '>=', $endTime); }); }); }); }) ->first(); if ($blockedDay) { throw new \Exception("Prestador possui bloqueio neste dia/horário."); } $conflictingSchedule = Schedule::where('provider_id', $data['provider_id']) ->where('date', $date->format('Y-m-d')) ->whereIn('status', ['pending', 'accepted', 'paid', 'started']) ->where(function ($query) use ($startTime, $endTime) { $query->whereBetween('start_time', [$startTime, $endTime]) ->orWhereBetween('end_time', [$startTime, $endTime]) ->orWhere(function ($q) use ($startTime, $endTime) { $q->where('start_time', '<=', $startTime) ->where('end_time', '>=', $endTime); }); }) ->when($excludeScheduleId, function ($query) use ($excludeScheduleId) { $query->where('id', '!=', $excludeScheduleId); }) ->first(); if ($conflictingSchedule) { throw new \Exception("Prestador já possui agendamento neste horário."); } return true; } public function getGroupedByClient() { $schedules = Schedule::with(['client.user', 'provider.user', 'address']) // ->orderBy('date', 'desc') ->orderBy('id', 'desc') ->select( 'schedules.*' ) ->get(); $grouped = $schedules->groupBy('client_id')->map(function ($clientSchedules) { $firstSchedule = $clientSchedules->first(); return [ 'client_id' => $firstSchedule->client_id, 'client_name' => $firstSchedule->client->user->name ?? 'N/A', 'schedules' => $clientSchedules->map(function ($schedule) { return [ 'id' => $schedule->id, 'date' => $schedule->date ? Carbon::parse($schedule->date)->format('d/m/Y') : null, 'start_time' => $schedule->start_time, 'end_time' => $schedule->end_time, 'period_type' => $schedule->period_type, 'status' => $schedule->status, 'total_amount' => $schedule->total_amount, 'code' => $schedule->code, 'code_verified' => $schedule->code_verified, 'provider_id' => $schedule->provider_id, 'provider_name' => $schedule->provider->user->name ?? 'N/A', 'address' => $schedule->address ? [ 'id' => $schedule->address->id, 'address' => $schedule->address->address, 'complement' => $schedule->address->complement, 'zip_code' => $schedule->address->zip_code, 'city' => $schedule->address->city->name ?? '', 'state' => $schedule->address->city->state->name ?? '', ] : null, 'client_name' => $schedule->client->user->name ?? 'N/A', ]; })->values() ]; })->sortBy('id')->values(); return $grouped; } public function updateStatus($id, string $status) { $schedule = Schedule::findOrFail($id); $allowedTransitions = [ 'pending' => ['accepted', 'rejected'], 'accepted' => ['paid', 'cancelled'], 'paid' => ['cancelled', 'started'], 'started' => ['finished'], 'rejected' => [], 'cancelled' => [], 'finished' => [], ]; $currentStatus = $schedule->status; if (!isset($allowedTransitions[$currentStatus])) { throw new \Exception("Status atual inválido."); } if (!in_array($status, $allowedTransitions[$currentStatus])) { throw new \Exception("Transição de status não permitida: {$currentStatus} → {$status}"); } $schedule->update(['status' => $status]); return $schedule->fresh(['client.user', 'provider.user', 'address']); } }