Browse Source

feat: :sparkles: feat(informa falta)Foi criado a migrate juntamente com a notificação e ajuste das funções

Foi criada uma migration para identificar e bloquear os dias da agenda do prestador que forem gerados como penalidade por falta, impedindo que esses bloqueios sejam desbloqueados ou alterados manualmente. Também foram ajustadas as funções responsáveis pelo gerenciamento desses bloqueios e implementada a notificação específica para casos de falta do prestador.

fase:dev | origin:escopo
kayo henrique 48 minutes ago
parent
commit
4255701184

+ 6 - 2
app/Http/Controllers/ScheduleController.php

@@ -155,11 +155,15 @@ class ScheduleController extends Controller
     public function reportProviderAbsence(int $id): JsonResponse
     public function reportProviderAbsence(int $id): JsonResponse
     {
     {
         try {
         try {
-            $schedule = $this->scheduleService->reportProviderAbsence($id);
+            $cancelText = request()->input('cancel_text');
+
+            $schedule = $this->scheduleService->reportProviderAbsence(
+                $id,
+                $cancelText
+            );
 
 
             return $this->successResponse(
             return $this->successResponse(
                 payload: new ScheduleResource($schedule),
                 payload: new ScheduleResource($schedule),
-                message: __('messages.provider_absence_reported'),
             );
             );
         } catch (\Exception $e) {
         } catch (\Exception $e) {
             return $this->errorResponse($e->getMessage(), 422);
             return $this->errorResponse($e->getMessage(), 422);

+ 1 - 0
app/Models/ProviderBlockedDay.php

@@ -47,6 +47,7 @@ class ProviderBlockedDay extends Model
         'date',
         'date',
         'period',
         'period',
         'reason',
         'reason',
+        'type',
         'init_hour',
         'init_hour',
         'end_hour',
         'end_hour',
     ];
     ];

+ 53 - 0
app/Notifications/Push/Prestador/Agendamento/PrestadorFaltouPush.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Notifications\Push\Prestador\Agendamento;
+
+use App\Enums\PushNotificationCategoryEnum;
+use App\Enums\PushNotificationTargetEnum;
+use App\Notifications\Push\BasePushNotification;
+use Illuminate\Database\Eloquent\Collection;
+
+class PrestadorFaltouPush extends BasePushNotification
+{
+    public function __construct() {}
+
+    public function label(): string
+    {
+        return 'provider_absence_schedule';
+    }
+
+    public function title(): string
+    {
+        return 'Agendamento cancelado por falta!';
+    }
+
+    public function body(): string
+    {
+        return 'Você não compareceu ao agendamento. Uma penalidade de 3 dias foi aplicada à sua agenda.';
+    }
+
+    public function target(): PushNotificationTargetEnum
+    {
+        return PushNotificationTargetEnum::PRESTADOR;
+    }
+
+    public function category(): PushNotificationCategoryEnum
+    {
+        return PushNotificationCategoryEnum::AGENDA;
+    }
+
+    public function eligibleUsers(): Collection
+    {
+        return new Collection();
+    }
+
+    public function notificationCooldownDays(): int
+    {
+        return 0;
+    }
+
+    public function categoryCooldownDays(): int
+    {
+        return 0;
+    }
+}

+ 19 - 3
app/Services/ProviderBlockedDayService.php

@@ -25,8 +25,12 @@ class ProviderBlockedDayService
         return ProviderBlockedDay::create($data);
         return ProviderBlockedDay::create($data);
     }
     }
 
 
-    public function update(ProviderBlockedDay $blockedDay, array $data): ProviderBlockedDay
-    {
+    public function update(
+        ProviderBlockedDay $blockedDay,
+        array $data
+    ): ProviderBlockedDay {
+        $this->validateManualChange($blockedDay);
+
         $blockedDay->update($data);
         $blockedDay->update($data);
 
 
         return $blockedDay->fresh();
         return $blockedDay->fresh();
@@ -34,6 +38,18 @@ class ProviderBlockedDayService
 
 
     public function delete(ProviderBlockedDay $blockedDay): bool
     public function delete(ProviderBlockedDay $blockedDay): bool
     {
     {
+        $this->validateManualChange($blockedDay);
+
         return $blockedDay->delete();
         return $blockedDay->delete();
     }
     }
-}
+
+    private function validateManualChange(
+        ProviderBlockedDay $blockedDay
+    ): void {
+        if ($blockedDay->type === 'auto_cancel')  {
+            throw new \Exception(
+                'Este dia foi bloqueado como penalidade por falta do prestador e não pode ser alterado ou desbloqueado.'
+            );
+        }
+    }
+}

+ 75 - 13
app/Services/ScheduleService.php

@@ -25,6 +25,7 @@ use App\Notifications\Push\Cliente\Agendamento\AgendamentoProximoPrestadorPush;
 use App\Notifications\Push\Prestador\Agendamento\AgendamentoProximoClientePush;
 use App\Notifications\Push\Prestador\Agendamento\AgendamentoProximoClientePush;
 use App\Notifications\Push\Cliente\Agendamento\PrestadorCancelouPush;
 use App\Notifications\Push\Cliente\Agendamento\PrestadorCancelouPush;
 use App\Notifications\Push\Prestador\Agendamento\ClienteCancelouPush;
 use App\Notifications\Push\Prestador\Agendamento\ClienteCancelouPush;
+use App\Notifications\Push\Prestador\Agendamento\PrestadorFaltouPush;
 use App\Notifications\Push\Prestador\Agendamento\NewPushRequest;
 use App\Notifications\Push\Prestador\Agendamento\NewPushRequest;
 use App\Enums\BlockedPeriodEnum;
 use App\Enums\BlockedPeriodEnum;
 use App\Models\ProviderBlockedDay;
 use App\Models\ProviderBlockedDay;
@@ -269,10 +270,23 @@ class ScheduleService
                     break;
                     break;
                 //tem que chamar o status cancel por causa da regra de push
                 //tem que chamar o status cancel por causa da regra de push
                 case 'cancelled':
                 case 'cancelled':
+
                     $notificationService = app(NotificationService::class);
                     $notificationService = app(NotificationService::class);
 
 
+                    if ($schedule->cancelled_due_to_provider_absence) {
+
+                        // Cancelamento por falta do prestador.
+                        // Aqui enviamos a notificação específica para o prestador.
+
+                        $this->sendProviderAbsencePush($schedule);
+
+                        break;
+                    }
+
                     switch (Auth::user()?->type) {
                     switch (Auth::user()?->type) {
+
                         case UserTypeEnum::CLIENT:
                         case UserTypeEnum::CLIENT:
+
                             $user = $schedule->provider?->user;
                             $user = $schedule->provider?->user;
 
 
                             if (!$user) {
                             if (!$user) {
@@ -287,14 +301,18 @@ class ScheduleService
                                 'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_CANCELLED->value,
                                 'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_CANCELLED->value,
                                 'user_id'     => $user->id,
                                 'user_id'     => $user->id,
                             ]);
                             ]);
+
                             $this->sendClientCancelledPush($schedule);
                             $this->sendClientCancelledPush($schedule);
 
 
                             break;
                             break;
 
 
                         case UserTypeEnum::PROVIDER:
                         case UserTypeEnum::PROVIDER:
+
                             $notificationService->create([
                             $notificationService->create([
                                 'title'       => __('notifications.schedule_cancelled_title'),
                                 'title'       => __('notifications.schedule_cancelled_title'),
-                                'description' => __('notifications.provider_cancelled_schedule_description', ['provider' => $schedule->provider->user->name]),
+                                'description' => __('notifications.provider_cancelled_schedule_description', [
+                                    'provider' => $schedule->provider->user->name
+                                ]),
                                 'origin'      => 'schedule',
                                 'origin'      => 'schedule',
                                 'origin_id'   => $schedule->id,
                                 'origin_id'   => $schedule->id,
                                 'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_CANCELLED->value,
                                 'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_CANCELLED->value,
@@ -648,9 +666,11 @@ class ScheduleService
 
 
     //reporta por falta 
     //reporta por falta 
 
 
-    public function reportProviderAbsence(int $scheduleId): Schedule
-    {
-        return DB::transaction(function () use ($scheduleId) {
+    public function reportProviderAbsence(
+        int $scheduleId,
+        string $cancelText
+    ): Schedule {
+        return DB::transaction(function () use ($scheduleId, $cancelText) {
             $schedule = Schedule::findOrFail($scheduleId);
             $schedule = Schedule::findOrFail($scheduleId);
 
 
             // O agendamento precisa ter um prestador vinculado
             // O agendamento precisa ter um prestador vinculado
@@ -661,7 +681,6 @@ class ScheduleService
             }
             }
 
 
             // O cliente precisa ser o dono do agendamento
             // O cliente precisa ser o dono do agendamento
-
             if (Auth::user()->type !== UserTypeEnum::CLIENT) {
             if (Auth::user()->type !== UserTypeEnum::CLIENT) {
                 throw new \Exception(
                 throw new \Exception(
                     __('messages.provider_absence_only_client')
                     __('messages.provider_absence_only_client')
@@ -682,7 +701,13 @@ class ScheduleService
             }
             }
 
 
             // O agendamento não pode estar encerrado/cancelado/rejeitado
             // O agendamento não pode estar encerrado/cancelado/rejeitado
-            if (in_array($schedule->status, ['cancelled', 'rejected', 'finished'], true)) {
+            if (
+                in_array(
+                    $schedule->status,
+                    ['cancelled', 'rejected', 'finished'],
+                    true
+                )
+            ) {
                 throw new \Exception(
                 throw new \Exception(
                     __('messages.provider_absence_invalid_status')
                     __('messages.provider_absence_invalid_status')
                 );
                 );
@@ -691,20 +716,24 @@ class ScheduleService
             // Valida se o cliente pode informar a falta neste momento
             // Valida se o cliente pode informar a falta neste momento
             ScheduleBusinessRules::validateProviderAbsenceWindow($schedule);
             ScheduleBusinessRules::validateProviderAbsenceWindow($schedule);
 
 
-            // Motivo utilizado para o cancelamento por falta
-            $cancelText = 'Prestador não compareceu ao serviço.';
-
-            // Registra que o cancelamento ocorreu por falta do prestador
+            // Registra o motivo informado pelo cliente
             $schedule->update([
             $schedule->update([
                 'cancel_text' => $cancelText,
                 'cancel_text' => $cancelText,
                 'cancelled_by' => Auth::user()->type,
                 'cancelled_by' => Auth::user()->type,
                 'cancelled_due_to_provider_absence' => true,
                 'cancelled_due_to_provider_absence' => true,
             ]);
             ]);
 
 
-            $this->updateStatus($schedule->id, 'cancelled', false, true);
+            // Cancela o agendamento
+            $this->updateStatus(
+                $schedule->id,
+                'cancelled',
+                false,
+                true
+            );
 
 
             // Aplica a penalidade de bloqueio de 3 dias úteis do prestador.
             // Aplica a penalidade de bloqueio de 3 dias úteis do prestador.
             $this->blockProviderPenaltyDays($schedule);
             $this->blockProviderPenaltyDays($schedule);
+
             // TODO: Implementar estorno integral do pagamento.
             // TODO: Implementar estorno integral do pagamento.
             // Esta etapa será implementada posteriormente por outro responsável.
             // Esta etapa será implementada posteriormente por outro responsável.
 
 
@@ -717,15 +746,15 @@ class ScheduleService
     }
     }
 
 
     //penalidade por falta bloqueio de 3 dias validos
     //penalidade por falta bloqueio de 3 dias validos
+    // penalidade por falta - bloqueio de 3 dias válidos
     private function blockProviderPenaltyDays(Schedule $schedule): void
     private function blockProviderPenaltyDays(Schedule $schedule): void
     {
     {
         $providerId = $schedule->provider_id;
         $providerId = $schedule->provider_id;
-
         $date = Carbon::parse($schedule->date)->startOfDay();
         $date = Carbon::parse($schedule->date)->startOfDay();
-
         $blockedDaysCount = 0;
         $blockedDaysCount = 0;
 
 
         while ($blockedDaysCount < 3) {
         while ($blockedDaysCount < 3) {
+
             $date->addDay();
             $date->addDay();
 
 
             $dayOfWeek = $date->dayOfWeek;
             $dayOfWeek = $date->dayOfWeek;
@@ -769,8 +798,13 @@ class ScheduleService
                 'date' => $date->format('Y-m-d'),
                 'date' => $date->format('Y-m-d'),
                 'period' => BlockedPeriodEnum::ALL->value,
                 'period' => BlockedPeriodEnum::ALL->value,
                 'reason' => 'Bloqueio de 3 dias por falta do prestador.',
                 'reason' => 'Bloqueio de 3 dias por falta do prestador.',
+                'type' => 'auto_cancel',
                 'init_hour' => '07:00',
                 'init_hour' => '07:00',
                 'end_hour' => '20:00',
                 'end_hour' => '20:00',
+
+                // Identifica que este bloqueio é uma penalidade
+                // e não pode ser alterado/desbloqueado manualmente.
+                'blocked_due_to_provider_absence' => true,
             ]);
             ]);
 
 
             $blockedDaysCount++;
             $blockedDaysCount++;
@@ -986,6 +1020,34 @@ class ScheduleService
         }
         }
     }
     }
 
 
+    // cancelou por falta 
+
+    private function sendProviderAbsencePush(Schedule $schedule): void
+    {
+        $user = $schedule->provider?->user;
+
+        if (! $user) {
+            Log::warning('Push de falta ignorada: prestador sem usuário', [
+                'schedule_id' => $schedule->id,
+            ]);
+
+            return;
+        }
+
+        try {
+            app(PushNotificationService::class)->sendToUser(
+                $user,
+                new PrestadorFaltouPush()
+            );
+        } catch (\Throwable $exception) {
+            Log::error('Falha ao enviar push de falta do prestador', [
+                'schedule_id' => $schedule->id,
+                'user_id'     => $user->id,
+                'error'       => $exception->getMessage(),
+            ]);
+        }
+    }
+
     public function sendScheduleStartingSoonPushes(Schedule $schedule): void
     public function sendScheduleStartingSoonPushes(Schedule $schedule): void
     {
     {
         $pushNotificationService = app(PushNotificationService::class);
         $pushNotificationService = app(PushNotificationService::class);

+ 24 - 0
database/migrations/2026_09_14_115234_add_type_to_provider_blocked_days_table.php

@@ -0,0 +1,24 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('provider_blocked_days', function (Blueprint $table) {
+            $table->enum('type', ['normal', 'auto_cancel'])
+                ->default('normal')
+                ->after('reason');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('provider_blocked_days', function (Blueprint $table) {
+            $table->dropColumn('type');
+        });
+    }
+};