Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.94% covered (success)
93.94%
31 / 33
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
AppointmentCancelService
93.94% covered (success)
93.94%
31 / 33
71.43% covered (warning)
71.43%
5 / 7
18.07
0.00% covered (danger)
0.00%
0 / 1
 processCancel
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
5.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        // Todo: check if the email template cancelled exists for the scope before submitting and sending
37        $this->sendCancellationEmail($process);
38        return $this->cancelProcess($process);
39    }
40
41    private function extractClientData(array $body): object
42    {
43        return (object) [
44            'processId' => isset($body['processId']) && is_numeric($body['processId'])
45                ? (int) $body['processId']
46                : null,
47            'authKey' => isset($body['authKey']) && is_string($body['authKey']) && trim($body['authKey']) !== ''
48                ? htmlspecialchars(trim($body['authKey']), ENT_QUOTES, 'UTF-8')
49                : null
50        ];
51    }
52
53    private function validateClientData(object $data): array
54    {
55        return ValidationService::validateGetProcessById($data->processId, $data->authKey);
56    }
57
58    private function getProcess(int $processId, string $authKey): ThinnedProcess|array
59    {
60        return ZmsApiFacadeService::getThinnedProcessById($processId, $authKey);
61    }
62
63    private function canBeCancelled(ThinnedProcess $process): bool
64    {
65        $appointmentTime = new \DateTimeImmutable("@{$process->timestamp}");
66        return $appointmentTime > \App::$now;
67    }
68
69    private function cancelProcess(ThinnedProcess $process): ThinnedProcess|array
70    {
71        $processEntity = MapperService::thinnedProcessToProcess($process);
72        $result = ZmsApiFacadeService::cancelAppointment($processEntity);
73        if (is_array($result) && !empty($result['errors'])) {
74            return $result;
75        }
76
77        return MapperService::processToThinnedProcess($result);
78    }
79
80    private function sendCancellationEmail(ThinnedProcess $process): void
81    {
82        $processEntity = MapperService::thinnedProcessToProcess($process);
83        ZmsApiFacadeService::sendCancellationEmail($processEntity);
84    }
85}