Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.12% covered (success)
94.12%
32 / 34
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
AppointmentCancelService
94.12% covered (success)
94.12%
32 / 34
71.43% covered (warning)
71.43%
5 / 7
19.07
0.00% covered (danger)
0.00%
0 / 1
 processCancel
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
6.01
 extractClientData
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
6
 validateClientData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getProcess
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canBeCancelled
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 cancelProcess
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 sendCancellationEmail
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi\Services\Appointment;
6
7use BO\Zmscitizenapi\Models\ThinnedProcess;
8use BO\Zmscitizenapi\Services\Core\ValidationService;
9use BO\Zmscitizenapi\Services\Core\ZmsApiFacadeService;
10use BO\Zmscitizenapi\Services\Core\MapperService;
11
12class AppointmentCancelService
13{
14    public function processCancel(array $body): ThinnedProcess|array
15    {
16        $clientData = $this->extractClientData($body);
17        $errors = $this->validateClientData($clientData);
18        if (!empty($errors['errors'])) {
19            return $errors;
20        }
21
22        $process = $this->getProcess($clientData->processId, $clientData->authKey);
23        if (is_array($process) && !empty($process['errors'])) {
24            return $process;
25        }
26
27        if (!$this->canBeCancelled($process)) {
28            return ['errors' => [
29                [
30                    'errorCode' => 'appointmentCanNotBeCanceled',
31                    'statusCode' => 406
32                ]
33            ]];
34        }
35
36        if ($process->status !== 'reserved') {
37            // Todo: check if the email template cancelled exists for the scope before submitting and sending
38            $this->sendCancellationEmail($process);
39        }
40
41        return $this->cancelProcess($process);
42    }
43
44    private function extractClientData(array $body): object
45    {
46        return (object) [
47            'processId' => isset($body['processId']) && is_numeric($body['processId'])
48                ? (int) $body['processId']
49                : null,
50            'authKey' => isset($body['authKey']) && is_string($body['authKey']) && trim($body['authKey']) !== ''
51                ? htmlspecialchars(trim($body['authKey']), ENT_QUOTES, 'UTF-8')
52                : null
53        ];
54    }
55
56    private function validateClientData(object $data): array
57    {
58        return ValidationService::validateGetProcessById($data->processId, $data->authKey);
59    }
60
61    private function getProcess(int $processId, string $authKey): ThinnedProcess|array
62    {
63        return ZmsApiFacadeService::getThinnedProcessById($processId, $authKey);
64    }
65
66    private function canBeCancelled(ThinnedProcess $process): bool
67    {
68        $appointmentTime = new \DateTimeImmutable("@{$process->timestamp}");
69        return $appointmentTime > \App::$now;
70    }
71
72    private function cancelProcess(ThinnedProcess $process): ThinnedProcess|array
73    {
74        $processEntity = MapperService::thinnedProcessToProcess($process);
75        $result = ZmsApiFacadeService::cancelAppointment($processEntity);
76        if (is_array($result) && !empty($result['errors'])) {
77            return $result;
78        }
79
80        return MapperService::processToThinnedProcess($result);
81    }
82
83    private function sendCancellationEmail(ThinnedProcess $process): void
84    {
85        $processEntity = MapperService::thinnedProcessToProcess($process);
86        ZmsApiFacadeService::sendCancellationEmail($processEntity);
87    }
88}