Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.95% covered (success)
95.95%
71 / 74
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
AppointmentReserveService
95.95% covered (success)
95.95%
71 / 74
60.00% covered (warning)
60.00%
3 / 5
25
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 processReservation
97.22% covered (success)
97.22%
35 / 36
0.00% covered (danger)
0.00%
0 / 1
4
 extractClientData
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 findMatchingProcess
88.24% covered (warning)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 1
8.10
 reserveAppointment
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi\Services\Appointment;
6
7use BO\Zmscitizenapi\Utils\DateTimeFormatHelper;
8use BO\Zmscitizenapi\Models\ThinnedProcess;
9use BO\Zmscitizenapi\Services\Captcha\CaptchaRequirementTrait;
10use BO\Zmscitizenapi\Services\Captcha\TokenValidationService;
11use BO\Zmscitizenapi\Services\Core\ValidationService;
12use BO\Zmscitizenapi\Services\Core\ZmsApiFacadeService;
13use BO\Zmsentities\Process;
14
15class AppointmentReserveService
16{
17    use CaptchaRequirementTrait;
18
19    private TokenValidationService $tokenValidator;
20    private ZmsApiFacadeService $zmsApiFacadeService;
21
22    public function __construct()
23    {
24        $this->tokenValidator = new TokenValidationService();
25        $this->zmsApiFacadeService = new ZmsApiFacadeService();
26    }
27
28    public function processReservation(array $body, bool $showUnpublished = false): ThinnedProcess|array
29    {
30        $clientData = $this->extractClientData($body);
31
32        $captchaRequired = $this->isCaptchaRequiredForOfficeId($clientData->officeId);
33        $captchaToken = $body['captchaToken'] ?? null;
34
35        $errors = ValidationService::validatePostAppointmentReserve(
36            $clientData->officeId,
37            $clientData->serviceIds,
38            $clientData->serviceCounts,
39            $clientData->timestamp,
40            $captchaRequired,
41            $captchaToken,
42            $this->tokenValidator
43        );
44        if (!empty($errors['errors'])) {
45            return $errors;
46        }
47
48        $errors = ValidationService::validateServiceLocationCombination(
49            $clientData->officeId,
50            $clientData->serviceIds,
51            $showUnpublished
52        );
53        if (!empty($errors['errors'])) {
54            return $errors;
55        }
56
57        $selectedProcess = $this->findMatchingProcess(
58            $clientData->officeId,
59            $clientData->serviceIds,
60            $clientData->serviceCounts,
61            $clientData->timestamp
62        );
63
64        $errors = ValidationService::validateGetProcessNotFound($selectedProcess);
65        if (!empty($errors['errors'])) {
66            return $errors;
67        }
68
69        return $this->reserveAppointment(
70            $selectedProcess,
71            $clientData->serviceIds,
72            $clientData->serviceCounts,
73            $clientData->officeId
74        );
75    }
76
77    private function extractClientData(array $body): object
78    {
79        return (object) [
80            'officeId' => isset($body['officeId']) && is_numeric($body['officeId']) ? (int) $body['officeId'] : null,
81            'serviceIds' => $body['serviceId'] ?? null,
82            'serviceCounts' => $body['serviceCount'] ?? [1],
83            'timestamp' => isset($body['timestamp']) && is_numeric($body['timestamp']) ? (int) $body['timestamp'] : null,
84        ];
85    }
86
87    private function findMatchingProcess(int $officeId, array $serviceIds, array $serviceCounts, int $timestamp): ?Process
88    {
89        $freeAppointments = ZmsApiFacadeService::getFreeAppointments($officeId, $serviceIds, $serviceCounts, DateTimeFormatHelper::getInternalDateFromTimestamp($timestamp));
90        foreach ($freeAppointments as $process) {
91            if (!isset($process->appointments) || empty($process->appointments)) {
92                continue;
93            }
94
95            foreach ($process->appointments as $appointment) {
96                if ((int) $appointment->date === $timestamp) {
97                    $requestIds = [];
98                    if ($process->requests) {
99                        foreach ($process->requests as $request) {
100                            $requestIds[] = $request->getId();
101                        }
102                    }
103
104                    $processData = [
105                        'requests' => $requestIds,
106                        'appointments' => [$appointment]
107                    ];
108                    $process->withUpdatedData($processData, new \DateTime("@$timestamp"), $process->scope);
109                    return $process;
110                }
111            }
112        }
113
114        return null;
115    }
116
117    private function reserveAppointment(Process $process, array $serviceIds, array $serviceCounts, int $officeId): ThinnedProcess
118    {
119        $process->clients = [
120            [
121                'email' => 'test@muenchen.de'
122            ]
123        ];
124        $reservedProcess = ZmsApiFacadeService::reserveTimeslot($process, $serviceIds, $serviceCounts);
125        if ($reservedProcess && $reservedProcess->scope && $reservedProcess->scope->id) {
126            $scopeId = $reservedProcess->scope->id;
127            $scope = ZmsApiFacadeService::getScopeById((int) $scopeId);
128            if (!isset($scope['errors']) && isset($scope) && !empty($scope)) {
129                $reservedProcess->scope = $scope;
130                $reservedProcess->officeId = $officeId;
131            }
132        }
133
134        return $reservedProcess;
135    }
136}