Преглед изворни кода

feat: :sparkles: feat (fluxo agendamento) pacote de agendamento e carrinho

alterado funcionamento do pacote de agendamentos e carrinho para so permitir mais de um agendamento no carrinho se for do mesmo prestador + bloco novo na dashboard para indicar que ha agendamentos sendo solicitados para o prestador + animacoes no bloco 'realize pagamento' e 'solicitando agendamento'

fase:dev | origin:escopo
Gustavo Zanatta пре 1 дан
родитељ
комит
dffd4df08e

+ 8 - 1
app/Http/Resources/ScheduleResource.php

@@ -15,7 +15,14 @@ class ScheduleResource extends JsonResource
             ->value('service_package_id');
 
         $servicePackageItemsCount = $this->service_package_items_count ?? (
-            $servicePackageId ? ServicePackageItem::query()->where('service_package_id', $servicePackageId)->count() : 0
+            $servicePackageId
+                ? ServicePackageItem::query()
+                    ->where('service_package_id', $servicePackageId)
+                    ->whereHas('schedule', fn ($query) => $query
+                        ->whereNotIn('status', ['cancelled', 'rejected'])
+                        ->where('schedule_type', 'default'))
+                    ->count()
+                : 0
         );
 
         return [

+ 3 - 1
app/Http/Resources/ServicePackageResource.php

@@ -30,7 +30,9 @@ class ServicePackageResource extends JsonResource
             )),
 
             'total_amount' => $this->whenLoaded('items', fn () =>
-                (float) $this->items->sum(fn($item) => (float) ($item->schedule?->total_amount ?? 0))
+                (float) $this->items
+                    ->filter(fn($item) => $item->schedule && ! in_array($item->schedule->status, ['cancelled', 'rejected'], true))
+                    ->sum(fn($item) => (float) $item->schedule->total_amount)
             ),
 
             'created_at'   => $this->created_at?->toISOString(),

+ 12 - 1
app/Services/DashboardService.php

@@ -108,6 +108,9 @@ class DashboardService
                 (
                     SELECT COUNT(*)
                     FROM service_package_items spi_count
+                    JOIN schedules s_count ON s_count.id = spi_count.schedule_id
+                        AND s_count.status NOT IN ('cancelled', 'rejected')
+                        AND s_count.schedule_type = 'default'
                     WHERE spi_count.service_package_id = (
                         SELECT spi.service_package_id
                         FROM service_package_items spi
@@ -331,6 +334,7 @@ class DashboardService
                 ->where('schedules.client_id', $cliente->id)
                 ->whereIn('schedules.status', ['pending', 'accepted'])
                 ->where('schedules.schedule_type', 'default')
+                ->whereDate('schedules.date', '>=', now()->toDateString())
                 ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
                 ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
                 ->select(
@@ -378,6 +382,9 @@ class DashboardService
                 (
                     SELECT COUNT(*)
                     FROM service_package_items spi_count
+                    JOIN schedules s_count ON s_count.id = spi_count.schedule_id
+                        AND s_count.status NOT IN ('cancelled', 'rejected')
+                        AND s_count.schedule_type = 'default'
                     WHERE spi_count.service_package_id = (
                         SELECT spi.service_package_id
                         FROM service_package_items spi
@@ -535,6 +542,9 @@ class DashboardService
                 (
                     SELECT COUNT(*)
                     FROM service_package_items spi_count
+                    JOIN schedules s_count ON s_count.id = spi_count.schedule_id
+                        AND s_count.status NOT IN ('cancelled', 'rejected')
+                        AND s_count.schedule_type = 'default'
                     WHERE spi_count.service_package_id = (
                         SELECT spi.service_package_id
                         FROM service_package_items spi
@@ -606,7 +616,8 @@ class DashboardService
         $pendingServicePackages = ServicePackage::query()
             ->where('client_id', $cliente->id)
             ->where('status', ServicePackageStatusEnum::OPEN->value)
-            ->whereDoesntHave('items.schedule', fn($q) => $q->where('status', '!=', 'accepted'))
+            ->whereHas('items.schedule', fn($q) => $q->where('status', 'accepted'))
+            ->whereDoesntHave('items.schedule', fn($q) => $q->where('status', 'pending'))
             ->with(['items.schedule' => function ($query) {
                 $query->with(['provider.user', 'address']);
             }])

+ 11 - 2
app/Services/Pagarme/PagarmePaymentService.php

@@ -65,7 +65,9 @@ class PagarmePaymentService
 
     private function platformFeeRate(string $paymentMethod, ?Schedule $schedule = null): float
     {
-        if ($schedule && $this->scheduleBelongsToServicePackageWithAtLeastThreeItems($schedule)) {
+        if ($schedule
+            && $schedule->schedule_type !== 'custom'
+            && $this->scheduleBelongsToServicePackageWithAtLeastThreeItems($schedule)) {
             return $paymentMethod === 'credit_card'
                 ? $this->configuredPlatformFeeRate('platform_service_package_min_3_schedules_credit_card_fee_rate')
                 : $this->configuredPlatformFeeRate('platform_service_package_min_3_schedules_pix_fee_rate');
@@ -399,7 +401,14 @@ class PagarmePaymentService
     {
         return ServicePackage::query()
             ->whereHas('items', fn ($query) => $query->where('schedule_id', $schedule->id))
-            ->whereHas('items', null, '>=', 3)
+            ->whereHas(
+                'items.schedule',
+                fn ($query) => $query
+                    ->whereNotIn('status', ['cancelled', 'rejected'])
+                    ->where('schedule_type', 'default'),
+                '>=',
+                3,
+            )
             ->exists();
     }
 

+ 45 - 5
app/Services/PaymentService.php

@@ -3,10 +3,12 @@
 namespace App\Services;
 
 use App\Enums\ServicePackageStatusEnum;
+use App\Enums\NotificationTypeEnum;
 use App\Enums\PaymentSplitStatusEnum;
 use App\Enums\PaymentStatusEnum;
 use App\Exceptions\PaymentFailedException;
 use App\Exceptions\PaymentException;
+use App\Jobs\StartScheduleJob;
 use App\Models\ServicePackage;
 use App\Models\ClientPaymentMethod;
 use App\Models\Payment;
@@ -106,7 +108,7 @@ class PaymentService
                 throw new AuthorizationException;
             }
 
-            $schedules = $servicePackage->items->pluck('schedule')->filter()->values();
+            $schedules = $this->activePackageSchedules($servicePackage);
 
             $this->validateServicePackageForPayment($servicePackage, $schedules);
 
@@ -324,7 +326,7 @@ class PaymentService
                 throw new AuthorizationException;
             }
 
-            $schedules = $servicePackage->items->pluck('schedule')->filter()->values();
+            $schedules = $this->activePackageSchedules($servicePackage);
 
             $this->validateServicePackageForPayment($servicePackage, $schedules);
 
@@ -459,25 +461,35 @@ class PaymentService
         }
 
         if ($payment->service_package_id) {
-            DB::transaction(function () use ($payment): void {
+            $paidSchedules = DB::transaction(function () use ($payment): SupportCollection {
                 $servicePackage = ServicePackage::query()->lockForUpdate()->with('items')->find($payment->service_package_id);
 
                 if (! $servicePackage) {
-                    return;
+                    return collect();
                 }
 
                 $scheduleIds = $servicePackage->items->pluck('schedule_id')->filter()->unique();
 
-                Schedule::query()
+                $schedulesToPay = Schedule::query()
+                    ->with('provider')
                     ->whereIn('id', $scheduleIds)
                     ->where('status', 'accepted')
+                    ->get();
+
+                Schedule::query()
+                    ->whereIn('id', $schedulesToPay->pluck('id'))
+                    ->where('status', 'accepted')
                     ->update(['status' => 'paid']);
 
                 if ($servicePackage->status !== ServicePackageStatusEnum::PAID) {
                     $servicePackage->update(['status' => ServicePackageStatusEnum::PAID->value]);
                 }
+
+                return $schedulesToPay->toBase();
             });
 
+            $paidSchedules->each(fn (Schedule $schedule) => $this->notifyProviderAndScheduleStart($schedule));
+
             return;
         }
 
@@ -488,6 +500,25 @@ class PaymentService
         }
     }
 
+    private function notifyProviderAndScheduleStart(Schedule $schedule): void
+    {
+        if ($schedule->provider_id && $schedule->provider) {
+            app(NotificationService::class)->create([
+                'title'       => __('notifications.payment_confirmed_title'),
+                'description' => __('notifications.payment_confirmed_description'),
+                'origin'      => 'schedule',
+                'origin_id'   => $schedule->id,
+                'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_START->value,
+                'user_id'     => $schedule->provider->user_id,
+            ]);
+        }
+
+        $dateCleaned = Carbon::parse($schedule->date)->format('Y-m-d');
+
+        StartScheduleJob::dispatch($schedule->id)
+            ->delay(Carbon::parse($dateCleaned . ' ' . $schedule->start_time)->subHour());
+    }
+
     private function syncServicePackagesForSchedule(Schedule $schedule): void
     {
         ServicePackage::query()
@@ -525,6 +556,15 @@ class PaymentService
         }
     }
 
+    private function activePackageSchedules(ServicePackage $servicePackage): SupportCollection
+    {
+        return $servicePackage->items
+            ->pluck('schedule')
+            ->filter()
+            ->reject(fn (Schedule $schedule) => in_array($schedule->status, ['cancelled', 'rejected'], true))
+            ->values();
+    }
+
     private function validateServicePackageForPayment(ServicePackage $servicePackage, SupportCollection $schedules): void
     {
         if (! in_array($servicePackage->status, [ServicePackageStatusEnum::OPEN, ServicePackageStatusEnum::PAID], true)) {

+ 42 - 0
app/Services/ScheduleService.php

@@ -3,11 +3,13 @@
 namespace App\Services;
 
 use App\Exceptions\ScheduleStatusTransitionException;
+use App\Enums\ServicePackageStatusEnum;
 use App\Enums\UserTypeEnum;
 use App\Enums\NotificationTypeEnum;
 use App\Jobs\StartScheduleJob;
 use App\Models\Provider;
 use App\Models\Schedule;
+use App\Models\ServicePackage;
 use App\Rules\ScheduleBusinessRules;
 use App\Services\NotificationService;
 use Carbon\Carbon;
@@ -498,6 +500,8 @@ class ScheduleService
                 'cancelled_by' => $cancelled_by,
             ]);
 
+            $this->cascadeCancelServicePackages($schedule, $cancelText, $cancelled_by);
+
             DB::commit();
 
             return $schedule->fresh(['client.user', 'provider.user', 'address']);
@@ -514,6 +518,44 @@ class ScheduleService
         }
     }
 
+    private function cascadeCancelServicePackages(Schedule $schedule, string $cancelText, $cancelledBy): void
+    {
+        $packageIds = DB::table('service_package_items')
+            ->where('schedule_id', $schedule->id)
+            ->pluck('service_package_id');
+
+        if ($packageIds->isEmpty()) {
+            return;
+        }
+
+        $packages = ServicePackage::query()
+            ->with('items.schedule')
+            ->whereIn('id', $packageIds)
+            ->get();
+
+        foreach ($packages as $package) {
+            $siblingSchedules = $package->items->pluck('schedule')->filter();
+
+            $siblingSchedules
+                ->filter(fn (Schedule $sibling) => $sibling->id !== $schedule->id
+                    && in_array($sibling->status, ['pending', 'accepted', 'paid'], true))
+                ->each(fn (Schedule $sibling) => $sibling->update([
+                    'status'       => 'cancelled',
+                    'cancel_text'  => $cancelText,
+                    'cancelled_by' => $cancelledBy,
+                ]));
+
+            $hasRealizedSchedule = $siblingSchedules->contains(
+                fn (Schedule $sibling) => in_array($sibling->status, ['started', 'finished'], true),
+            );
+
+            if ($package->status === ServicePackageStatusEnum::OPEN
+                || ($package->status === ServicePackageStatusEnum::PAID && ! $hasRealizedSchedule)) {
+                $package->update(['status' => ServicePackageStatusEnum::CANCELLED->value]);
+            }
+        }
+    }
+
     //
 
     private function calculateAmount(Provider $provider, string $periodType): float