ScheduleService.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. namespace App\Services;
  3. use App\Jobs\FinishScheduleJob;
  4. use App\Jobs\StartScheduleJob;
  5. use App\Models\Schedule;
  6. use App\Models\Provider;
  7. use App\Models\ProviderBlockedDay;
  8. use App\Models\ProviderWorkingDay;
  9. use App\Rules\ScheduleBusinessRules;
  10. use Carbon\Carbon;
  11. use Illuminate\Support\Facades\DB;
  12. use Illuminate\Support\Facades\Log;
  13. class ScheduleService
  14. {
  15. public function getAll()
  16. {
  17. return Schedule::with(['client.user', 'provider.user', 'address'])
  18. ->where('schedule_type', 'default')
  19. ->orderBy('date', 'desc')
  20. ->orderBy('start_time', 'desc')
  21. ->get();
  22. }
  23. public function getById($id)
  24. {
  25. return Schedule::with(['client.user', 'provider.user', 'address'])->findOrFail($id);
  26. }
  27. public function createSingleOrMultiple(array $baseData, array $schedules)
  28. {
  29. $createdSchedules = [];
  30. foreach ($schedules as $schedule) {
  31. try {
  32. $datasMerged = array_merge($baseData, $schedule);
  33. $this->validateProviderAvailability($datasMerged, null);
  34. $scheduleData = array_merge($datasMerged, [
  35. 'code' => str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT),
  36. ]);
  37. $createdSchedules[] = Schedule::create($scheduleData);
  38. } catch (\Exception $e) {
  39. throw new \Exception(__("validation.provider_unavailable"));
  40. }
  41. }
  42. return $createdSchedules;
  43. }
  44. public function update($id, array $data)
  45. {
  46. $schedule = Schedule::findOrFail($id);
  47. if (isset($data['provider_id']) || isset($data['period_type'])) {
  48. $providerId = $data['provider_id'] ?? $schedule->provider_id;
  49. $periodType = $data['period_type'] ?? $schedule->period_type;
  50. $provider = Provider::findOrFail($providerId);
  51. $data['total_amount'] = $this->calculateAmount($provider, $periodType);
  52. }
  53. if (isset($data['date']) || isset($data['start_time']) || isset($data['provider_id'])) {
  54. $validationData = array_merge($schedule->toArray(), $data);
  55. $this->validateProviderAvailability($validationData, $id);
  56. }
  57. $schedule->update($data);
  58. return $schedule->fresh(['client.user', 'provider.user', 'address']);
  59. }
  60. public function delete($id)
  61. {
  62. $schedule = Schedule::findOrFail($id);
  63. $schedule->delete();
  64. return $schedule;
  65. }
  66. private function calculateAmount(Provider $provider, string $periodType): float
  67. {
  68. $hourlyRates = [
  69. '2' => $provider->daily_price_2h ?? 0,
  70. '4' => $provider->daily_price_4h ?? 0,
  71. '6' => $provider->daily_price_6h ?? 0,
  72. '8' => $provider->daily_price_8h ?? 0,
  73. ];
  74. return $hourlyRates[$periodType] ?? 0;
  75. }
  76. private function validateProviderAvailability(array $data, $excludeScheduleId = null)
  77. {
  78. $provider_id = $data['provider_id'];
  79. $client_id = $data['client_id'];
  80. $date = Carbon::parse($data['date']);
  81. $dayOfWeek = $date->dayOfWeek;
  82. $startTime = $data['start_time'];
  83. $endTime = $data['end_time'];
  84. $period = $startTime < '13:00:00' ? 'morning' : 'afternoon';
  85. // bloqueio 2 schedules por semana para o mesmo client e provider
  86. ScheduleBusinessRules::validateWeeklyScheduleLimit(
  87. $client_id,
  88. $provider_id,
  89. $data['date'],
  90. $excludeScheduleId
  91. );
  92. // bloqueio provider trabalha no dia/periodo
  93. ScheduleBusinessRules::validateWorkingDay(
  94. $provider_id,
  95. $dayOfWeek,
  96. $period
  97. );
  98. // bloqueio provider tem blockedday para dia/hora
  99. ScheduleBusinessRules::validateBlockedDay(
  100. $provider_id,
  101. $date->format('Y-m-d'),
  102. $startTime,
  103. $endTime
  104. );
  105. // bloqueio provider tem outro agendamento para dia/hora
  106. ScheduleBusinessRules::validateConflictingSchedule(
  107. $provider_id,
  108. $date->format('Y-m-d'),
  109. $startTime,
  110. $endTime,
  111. $excludeScheduleId
  112. );
  113. return true;
  114. }
  115. public function getSchedulesDefaultGroupedByClient()
  116. {
  117. $schedules = Schedule::with(['client.user', 'provider.user', 'address'])
  118. ->orderBy('id', 'desc')
  119. ->where('schedule_type', 'default')
  120. ->select(
  121. 'schedules.*'
  122. )
  123. ->get();
  124. $grouped = $schedules->groupBy('client_id')->map(function ($clientSchedules) {
  125. $firstSchedule = $clientSchedules->first();
  126. return [
  127. 'client_id' => $firstSchedule->client_id,
  128. 'client_name' => $firstSchedule->client->user->name ?? 'N/A',
  129. 'schedules' => $clientSchedules->map(function ($schedule) {
  130. return [
  131. 'id' => $schedule->id,
  132. 'date' => $schedule->date ? Carbon::parse($schedule->date)->format('d/m/Y') : null,
  133. 'start_time' => $schedule->start_time,
  134. 'end_time' => $schedule->end_time,
  135. 'period_type' => $schedule->period_type,
  136. 'status' => $schedule->status,
  137. 'total_amount' => $schedule->total_amount,
  138. 'code' => $schedule->code,
  139. 'code_verified' => $schedule->code_verified,
  140. 'provider_id' => $schedule->provider_id,
  141. 'provider_name' => $schedule->provider->user->name ?? 'N/A',
  142. 'address' => $schedule->address ? [
  143. 'id' => $schedule->address->id,
  144. 'address' => $schedule->address->address,
  145. 'complement' => $schedule->address->complement,
  146. 'zip_code' => $schedule->address->zip_code,
  147. 'city' => $schedule->address->city->name ?? '',
  148. 'state' => $schedule->address->city->state->name ?? '',
  149. ] : null,
  150. 'client_name' => $schedule->client->user->name ?? 'N/A',
  151. ];
  152. })->values()
  153. ];
  154. })->sortBy('id')->values();
  155. return $grouped;
  156. }
  157. public function updateStatus($id, string $status)
  158. {
  159. try {
  160. DB::beginTransaction();
  161. $schedule = Schedule::findOrFail($id);
  162. $allowedTransitions = [
  163. 'pending' => ['accepted', 'rejected'],
  164. 'accepted' => ['paid', 'cancelled'],
  165. 'paid' => ['cancelled', 'started'],
  166. 'started' => ['finished'],
  167. 'rejected' => [],
  168. 'cancelled' => [],
  169. 'finished' => [],
  170. ];
  171. $currentStatus = $schedule->status;
  172. if (!isset($allowedTransitions[$currentStatus])) {
  173. throw new \Exception("Status atual inválido.");
  174. }
  175. if (!in_array($status, $allowedTransitions[$currentStatus])) {
  176. throw new \Exception("Transição de status não permitida: {$currentStatus} → {$status}");
  177. }
  178. $schedule->update(['status' => $status]);
  179. switch ($status) {
  180. case 'pending':
  181. break;
  182. case 'accepted':
  183. break;
  184. case 'rejected':
  185. break;
  186. case 'paid':
  187. $date_cleaned = Carbon::parse($schedule->date)->format('Y-m-d');
  188. $date_time_dispatch = Carbon::parse($date_cleaned . ' ' . $schedule->start_time);
  189. StartScheduleJob::dispatch($schedule->id)->delay($date_time_dispatch);
  190. // dispatch de teste em local
  191. // StartScheduleJob::dispatch($schedule->id)->delay(now()->addSeconds(15));
  192. break;
  193. case 'cancelled':
  194. break;
  195. case 'started':
  196. break;
  197. case 'finished':
  198. break;
  199. }
  200. DB::commit();
  201. return $schedule->fresh(['client.user', 'provider.user', 'address']);
  202. } catch (\Exception $e) {
  203. DB::rollBack();
  204. Log::error("Erro ao atualizar status do agendamento: " . $e->getMessage());
  205. throw new \Exception("Não foi possível atualizar o status do agendamento.");
  206. }
  207. }
  208. }