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 createSingleOrMultiple(array $baseData, array $schedules) { $createdSchedules = []; foreach ($schedules as $schedule) { try { $datasMerged = array_merge($baseData, $schedule); $this->validateProviderAvailability($datasMerged, null); $scheduleData = array_merge($datasMerged, [ 'code' => str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT), ]); $createdSchedules[] = Schedule::create($scheduleData); } catch (\Exception $e) { throw new \Exception(__("validation.provider_unavailable")); } } 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_id = $data['provider_id']; $client_id = $data['client_id']; $date = Carbon::parse($data['date']); $dayOfWeek = $date->dayOfWeek; $startTime = $data['start_time']; $endTime = $data['end_time']; $period = $startTime < '13:00:00' ? 'morning' : 'afternoon'; // bloqueio 2 schedules por semana para o mesmo client e provider ScheduleBusinessRules::validateWeeklyScheduleLimit( $client_id, $provider_id, $data['date'], $excludeScheduleId ); // bloqueio provider trabalha no dia/periodo ScheduleBusinessRules::validateWorkingDay( $provider_id, $dayOfWeek, $period ); // bloqueio provider tem blockedday para dia/hora ScheduleBusinessRules::validateBlockedDay( $provider_id, $date->format('Y-m-d'), $startTime, $endTime ); // bloqueio provider tem outro agendamento para dia/hora ScheduleBusinessRules::validateConflictingSchedule( $provider_id, $date->format('Y-m-d'), $startTime, $endTime, $excludeScheduleId ); return true; } public function getSchedulesDefaultGroupedByClient() { $schedules = Schedule::with(['client.user', 'provider.user', 'address']) ->orderBy('id', 'desc') ->where('schedule_type', 'default') ->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) { try { DB::beginTransaction(); $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]); switch ($status) { case 'pending': break; case 'accepted': break; case 'rejected': break; case 'paid': $date_cleaned = Carbon::parse($schedule->date)->format('Y-m-d'); $date_time_dispatch = Carbon::parse($date_cleaned . ' ' . $schedule->start_time); StartScheduleJob::dispatch($schedule->id)->delay($date_time_dispatch); // dispatch de teste em local // StartScheduleJob::dispatch($schedule->id)->delay(now()->addSeconds(15)); break; case 'cancelled': break; case 'started': break; case 'finished': break; } DB::commit(); return $schedule->fresh(['client.user', 'provider.user', 'address']); } catch (\Exception $e) { DB::rollBack(); Log::error("Erro ao atualizar status do agendamento: " . $e->getMessage()); throw new \Exception("Não foi possível atualizar o status do agendamento."); } } }