Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
RescheduleVisitAction
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
2 / 2
3
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Visits\Application\Actions;
6
7use App\Models\User;
8use Carbon\Carbon;
9use Illuminate\Support\Facades\DB;
10use ImovelZapAi\Visits\Application\Services\AgendaConflictChecker;
11use ImovelZapAi\Visits\Application\Services\VisitLogWriter;
12use ImovelZapAi\Visits\Application\Services\VisitReminderScheduler;
13use ImovelZapAi\Visits\Application\Services\VisitSnapshotter;
14use ImovelZapAi\Visits\Domain\Enums\VisitLogEvent;
15use ImovelZapAi\Visits\Domain\Enums\VisitStatus;
16use ImovelZapAi\Visits\Domain\Events\VisitRescheduled;
17use ImovelZapAi\Visits\Domain\Exceptions\VisitException;
18use ImovelZapAi\Visits\Infrastructure\Persistence\Eloquent\VisitModel;
19
20final class RescheduleVisitAction
21{
22    public function __construct(
23        private readonly AgendaConflictChecker $conflictChecker,
24        private readonly VisitReminderScheduler $reminderScheduler,
25        private readonly VisitLogWriter $logWriter,
26        private readonly VisitSnapshotter $snapshotter,
27    ) {}
28
29    public function execute(VisitModel $visit, User $actor, Carbon $start, ?Carbon $end = null): VisitModel
30    {
31        if (! $visit->status->canReschedule()) {
32            throw VisitException::invalidState($visit->status->value, 'reschedule');
33        }
34
35        $settings = $this->conflictChecker->settingsFor($visit->tenant_id);
36        $end ??= $start->copy()->addMinutes((int) $settings->default_duration_minutes);
37        $brokerId = (int) $visit->responsible_user_id;
38
39        $this->conflictChecker->assertNoConflict(
40            $visit->tenant_id,
41            $brokerId,
42            $start,
43            $end,
44            $visit->id,
45        );
46
47        return DB::transaction(function () use ($visit, $actor, $start, $end): VisitModel {
48            $before = $this->snapshotter->snapshot($visit);
49
50            $visit->forceFill([
51                'scheduled_start' => $start,
52                'scheduled_end' => $end,
53                'status' => VisitStatus::Confirmed,
54                'confirmed_at' => now(),
55            ])->save();
56
57            $this->logWriter->write(
58                $visit->tenant_id,
59                $visit->id,
60                VisitLogEvent::Rescheduled,
61                actorUserId: (int) $actor->id,
62                before: $before,
63                after: $this->snapshotter->snapshot($visit->fresh()),
64                message: 'Visita remarcada.',
65            );
66
67            VisitRescheduled::dispatch($visit->tenant_id, $visit->id);
68            $this->reminderScheduler->scheduleForVisit($visit->fresh(), sendInitialNow: true);
69
70            return $visit->fresh(['reminders', 'contact', 'property']);
71        });
72    }
73}