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