Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.62% |
387 / 409 |
|
24.00% |
6 / 25 |
CRAP | |
0.00% |
0 / 1 |
ZmsApiFacadeService | |
94.62% |
387 / 409 |
|
24.00% |
6 / 25 |
137.84 | |
0.00% |
0 / 1 |
setLanguageContext | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMappedCache | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
getOffices | |
95.24% |
40 / 42 |
|
0.00% |
0 / 1 |
11 | |||
getScopes | |
96.97% |
32 / 33 |
|
0.00% |
0 / 1 |
9 | |||
getServices | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
8.02 | |||
getServicesAndOffices | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
4.00 | |||
getScopeByOfficeId | |
97.56% |
40 / 41 |
|
0.00% |
0 / 1 |
6 | |||
getOfficeListByServiceId | |
89.29% |
25 / 28 |
|
0.00% |
0 / 1 |
14.24 | |||
getScopeById | |
97.22% |
35 / 36 |
|
0.00% |
0 / 1 |
10 | |||
getServicesByOfficeId | |
96.15% |
25 / 26 |
|
0.00% |
0 / 1 |
13 | |||
getServicesProvidedAtOffice | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
5 | |||
getBookableFreeDays | |
100.00% |
26 / 26 |
|
100.00% |
1 / 1 |
6 | |||
getFreeAppointments | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 | |||
processFreeSlots | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
11.02 | |||
getAvailableAppointments | |
95.24% |
20 / 21 |
|
0.00% |
0 / 1 |
8 | |||
reserveTimeslot | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
getThinnedProcessById | |
96.88% |
31 / 32 |
|
0.00% |
0 / 1 |
8 | |||
updateClientData | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
preconfirmAppointment | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
confirmAppointment | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
cancelAppointment | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
sendPreconfirmationEmail | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
sendConfirmationEmail | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
sendCancellationEmail | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace BO\Zmscitizenapi\Services\Core; |
6 | |
7 | use BO\Zmscitizenapi\Helper\DateTimeFormatHelper; |
8 | use BO\Zmscitizenapi\Localization\ErrorMessages; |
9 | use BO\Zmscitizenapi\Models\AvailableAppointmentsByOffice; |
10 | use BO\Zmscitizenapi\Models\AvailableDays; |
11 | use BO\Zmscitizenapi\Models\AvailableAppointments; |
12 | use BO\Zmscitizenapi\Models\Office; |
13 | use BO\Zmscitizenapi\Models\Service; |
14 | use BO\Zmscitizenapi\Models\ThinnedProcess; |
15 | use BO\Zmscitizenapi\Models\ThinnedScope; |
16 | use BO\Zmscitizenapi\Models\Collections\OfficeList; |
17 | use BO\Zmscitizenapi\Models\Collections\OfficeServiceRelationList; |
18 | use BO\Zmscitizenapi\Models\Collections\OfficeServiceAndRelationList; |
19 | use BO\Zmscitizenapi\Models\Collections\ServiceList; |
20 | use BO\Zmscitizenapi\Models\Collections\ThinnedScopeList; |
21 | use BO\Zmscitizenapi\Services\Core\ZmsApiClientService; |
22 | use BO\Zmsentities\Calendar; |
23 | use BO\Zmsentities\Collection\RequestRelationList; |
24 | use BO\Zmsentities\Process; |
25 | use BO\Zmsentities\Scope; |
26 | use BO\Zmsentities\Collection\ScopeList; |
27 | use BO\Zmsentities\Collection\ProviderList; |
28 | use BO\Zmsentities\Collection\RequestList; |
29 | use BO\Zmsentities\Collection\ProcessList; |
30 | |
31 | /** |
32 | * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
33 | * @TODO: Break down this facade into smaller domain-specific facades or use the Command pattern |
34 | */ |
35 | class ZmsApiFacadeService |
36 | { |
37 | private const CACHE_KEY_OFFICES = 'processed_offices'; |
38 | private const CACHE_KEY_SCOPES = 'processed_scopes'; |
39 | private const CACHE_KEY_SERVICES = 'processed_services'; |
40 | private const CACHE_KEY_OFFICES_AND_SERVICES = 'processed_offices_and_services'; |
41 | private const CACHE_KEY_OFFICES_BY_SERVICE_PREFIX = 'processed_offices_by_service_'; |
42 | private const CACHE_KEY_SERVICES_BY_OFFICE_PREFIX = 'processed_services_by_office_'; |
43 | |
44 | private static ?string $currentLanguage = null; |
45 | public static function setLanguageContext(?string $language): void |
46 | { |
47 | self::$currentLanguage = $language; |
48 | } |
49 | |
50 | private static function getError(string $key): array |
51 | { |
52 | return ErrorMessages::get($key, self::$currentLanguage); |
53 | } |
54 | |
55 | private static function setMappedCache(string $cacheKey, mixed $data): void |
56 | { |
57 | if (\App::$cache) { |
58 | \App::$cache->set($cacheKey, $data, \App::$SOURCE_CACHE_TTL); |
59 | LoggerService::logInfo('Second-level cache set', [ |
60 | 'key' => $cacheKey, |
61 | 'ttl' => \App::$SOURCE_CACHE_TTL |
62 | ]); |
63 | } |
64 | } |
65 | |
66 | public static function getOffices(bool $showUnpublished = false): OfficeList |
67 | { |
68 | $cacheKey = self::CACHE_KEY_OFFICES . ($showUnpublished ? '_unpublished' : ''); |
69 | |
70 | if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { |
71 | return $cachedData; |
72 | } |
73 | |
74 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
75 | $scopeList = ZmsApiClientService::getScopes() ?? new ScopeList(); |
76 | $offices = []; |
77 | $scopeMap = []; |
78 | foreach ($scopeList as $scope) { |
79 | if ($scope->getProvider()) { |
80 | $scopeMap[$scope->getProvider()->source . '_' . $scope->getProvider()->id] = $scope; |
81 | } |
82 | } |
83 | |
84 | foreach ($providerList as $provider) { |
85 | if (!$showUnpublished && isset($provider->data['public']) && !(bool) $provider->data['public']) { |
86 | continue; |
87 | } |
88 | |
89 | $matchingScope = $scopeMap[$provider->source . '_' . $provider->id] ?? null; |
90 | $offices[] = new Office( |
91 | id: (int) $provider->id, |
92 | name: $provider->displayName ?? $provider->name, |
93 | address: $provider->data['address'] ?? null, |
94 | showAlternativeLocations: $provider->data['showAlternativeLocations'] ?? null, |
95 | displayNameAlternatives: $provider->data['displayNameAlternatives'] ?? [], |
96 | organization: $provider->data['organization'] ?? null, |
97 | organizationUnit: $provider->data['organizationUnit'] ?? null, |
98 | slotTimeInMinutes: $provider->data['slotTimeInMinutes'] ?? null, |
99 | geo: $provider->data['geo'] ?? null, |
100 | scope: $matchingScope ? new ThinnedScope( |
101 | id: (int) $matchingScope->id, |
102 | provider: MapperService::providerToThinnedProvider($provider), |
103 | shortName: (string) $matchingScope->getShortName(), |
104 | emailFrom: (string) $matchingScope->getEmailFrom(), |
105 | emailRequired: (bool) $matchingScope->getEmailRequired(), |
106 | telephoneActivated: (bool) $matchingScope->getTelephoneActivated(), |
107 | telephoneRequired: (bool) $matchingScope->getTelephoneRequired(), |
108 | customTextfieldActivated: (bool) $matchingScope->getCustomTextfieldActivated(), |
109 | customTextfieldRequired: (bool) $matchingScope->getCustomTextfieldRequired(), |
110 | customTextfieldLabel: $matchingScope->getCustomTextfieldLabel(), |
111 | captchaActivatedRequired: (bool) $matchingScope->getCaptchaActivatedRequired(), |
112 | displayInfo: $matchingScope->getDisplayInfo() |
113 | ) : null |
114 | ); |
115 | } |
116 | |
117 | $result = new OfficeList($offices); |
118 | |
119 | self::setMappedCache($cacheKey, $result); |
120 | |
121 | return $result; |
122 | } |
123 | |
124 | public static function getScopes(): ThinnedScopeList|array |
125 | { |
126 | $cacheKey = self::CACHE_KEY_SCOPES; |
127 | |
128 | if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { |
129 | return $cachedData; |
130 | } |
131 | |
132 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
133 | $scopeList = ZmsApiClientService::getScopes() ?? new ScopeList(); |
134 | $scopeMap = []; |
135 | foreach ($scopeList as $scope) { |
136 | $scopeProvider = $scope->getProvider(); |
137 | if ($scopeProvider && $scopeProvider->id && $scopeProvider->source) { |
138 | $key = $scopeProvider->source . '_' . $scopeProvider->id; |
139 | $scopeMap[$key] = $scope; |
140 | } |
141 | } |
142 | |
143 | $scopesProjectionList = []; |
144 | foreach ($providerList as $provider) { |
145 | $key = $provider->source . '_' . $provider->id; |
146 | if (isset($scopeMap[$key])) { |
147 | $matchingScope = $scopeMap[$key]; |
148 | $scopesProjectionList[] = new ThinnedScope( |
149 | id: (int) $matchingScope->id, |
150 | provider: MapperService::providerToThinnedProvider($provider), |
151 | shortName: (string) $matchingScope->getShortName(), |
152 | emailFrom: (string) $matchingScope->getEmailFrom(), |
153 | emailRequired: (bool) $matchingScope->getEmailRequired(), |
154 | telephoneActivated: (bool) $matchingScope->getTelephoneActivated(), |
155 | telephoneRequired: (bool) $matchingScope->getTelephoneRequired(), |
156 | customTextfieldActivated: (bool) $matchingScope->getCustomTextfieldActivated(), |
157 | customTextfieldRequired: (bool) $matchingScope->getCustomTextfieldRequired(), |
158 | customTextfieldLabel: $matchingScope->getCustomTextfieldLabel(), |
159 | captchaActivatedRequired: (bool) $matchingScope->getCaptchaActivatedRequired(), |
160 | displayInfo: $matchingScope->getDisplayInfo() |
161 | ); |
162 | } |
163 | } |
164 | |
165 | $result = new ThinnedScopeList($scopesProjectionList); |
166 | |
167 | self::setMappedCache($cacheKey, $result); |
168 | |
169 | return $result; |
170 | } |
171 | |
172 | public static function getServices(bool $showUnpublished = false): ServiceList|array |
173 | { |
174 | $cacheKey = self::CACHE_KEY_SERVICES . ($showUnpublished ? '_unpublished' : ''); |
175 | |
176 | if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { |
177 | return $cachedData; |
178 | } |
179 | |
180 | $requestList = ZmsApiClientService::getServices() ?? new RequestList(); |
181 | $services = []; |
182 | foreach ($requestList as $request) { |
183 | $additionalData = $request->getAdditionalData(); |
184 | if ( |
185 | !$showUnpublished |
186 | && isset($additionalData['public']) |
187 | && !$additionalData['public'] |
188 | ) { |
189 | continue; |
190 | } |
191 | |
192 | $services[] = new Service(id: (int) $request->getId(), name: $request->getName(), maxQuantity: $additionalData['maxQuantity'] ?? 1); |
193 | } |
194 | |
195 | $result = new ServiceList($services); |
196 | |
197 | self::setMappedCache($cacheKey, $result); |
198 | |
199 | return $result; |
200 | } |
201 | |
202 | public static function getServicesAndOffices(bool $showUnpublished = false): OfficeServiceAndRelationList|array |
203 | { |
204 | $cacheKey = self::CACHE_KEY_OFFICES_AND_SERVICES . ($showUnpublished ? '_unpublished' : ''); |
205 | |
206 | if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { |
207 | return $cachedData; |
208 | } |
209 | |
210 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
211 | $requestList = ZmsApiClientService::getServices() ?? new RequestList(); |
212 | $relationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); |
213 | |
214 | $offices = MapperService::mapOfficesWithScope($providerList, $showUnpublished) ?? new OfficeList(); |
215 | $services = MapperService::mapServicesWithCombinations( |
216 | $requestList, |
217 | $relationList, |
218 | $showUnpublished |
219 | ) ?? new ServiceList(); |
220 | $relations = MapperService::mapRelations($relationList) ?? new OfficeServiceRelationList(); |
221 | |
222 | $result = new OfficeServiceAndRelationList($offices, $services, $relations); |
223 | |
224 | self::setMappedCache($cacheKey, $result); |
225 | |
226 | return $result; |
227 | } |
228 | |
229 | /* Todo add method |
230 | * getCombinableServicesByIds |
231 | * |
232 | * |
233 | * |
234 | */ |
235 | |
236 | public static function getScopeByOfficeId(int $officeId): ThinnedScope|array |
237 | { |
238 | $matchingScope = ZmsApiClientService::getScopesByProviderId(\App::$source_name, $officeId)->getIterator()->current(); |
239 | if (!$matchingScope instanceof Scope) { |
240 | return ['errors' => [self::getError('scopeNotFound')]]; |
241 | } |
242 | |
243 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
244 | $providerMap = []; |
245 | foreach ($providerList as $provider) { |
246 | $key = $provider->source . '_' . $provider->id; |
247 | $providerMap[$key] = $provider; |
248 | } |
249 | |
250 | $scopeProvider = $matchingScope->getProvider(); |
251 | $providerKey = $scopeProvider ? ($scopeProvider->source . '_' . $scopeProvider->id) : null; |
252 | $finalProvider = $providerKey && isset($providerMap[$providerKey]) |
253 | ? $providerMap[$providerKey] |
254 | : $scopeProvider; |
255 | $result = [ |
256 | 'id' => $matchingScope->id, |
257 | 'provider' => MapperService::providerToThinnedProvider($finalProvider) ?? null, |
258 | 'shortName' => (string) $matchingScope->getShortName() ?? null, |
259 | 'emailFrom' => (string) $matchingScope->getEmailFrom() ?? null, |
260 | 'emailRequired' => (bool) $matchingScope->getEmailRequired() ?? null, |
261 | 'telephoneActivated' => (bool) $matchingScope->getTelephoneActivated() ?? null, |
262 | 'telephoneRequired' => (bool) $matchingScope->getTelephoneRequired() ?? null, |
263 | 'customTextfieldActivated' => (bool) $matchingScope->getCustomTextfieldActivated() ?? null, |
264 | 'customTextfieldRequired' => (bool) $matchingScope->getCustomTextfieldRequired() ?? null, |
265 | 'customTextfieldLabel' => $matchingScope->getCustomTextfieldLabel() ?? null, |
266 | 'captchaActivatedRequired' => (bool) $matchingScope->getCaptchaActivatedRequired() ?? null, |
267 | 'displayInfo' => $matchingScope->getDisplayInfo() ?? null, |
268 | ]; |
269 | return new ThinnedScope( |
270 | id: (int) $result['id'], |
271 | provider: $result['provider'], |
272 | shortName: $result['shortName'], |
273 | emailFrom: $result['emailFrom'], |
274 | emailRequired: $result['emailRequired'], |
275 | telephoneActivated: $result['telephoneActivated'], |
276 | telephoneRequired: $result['telephoneRequired'], |
277 | customTextfieldActivated: $result['customTextfieldActivated'], |
278 | customTextfieldRequired: $result['customTextfieldRequired'], |
279 | customTextfieldLabel: $result['customTextfieldLabel'], |
280 | captchaActivatedRequired: $result['captchaActivatedRequired'], |
281 | displayInfo: $result['displayInfo'] |
282 | ); |
283 | } |
284 | |
285 | /* Todo add method |
286 | * getOfficeById |
287 | * |
288 | * |
289 | * |
290 | */ |
291 | |
292 | /** |
293 | * @SuppressWarnings(PHPMD.NPathComplexity) |
294 | * @TODO: Extract providerMap mapping logic into MapperService |
295 | */ |
296 | public static function getOfficeListByServiceId(int $serviceId, bool $showUnpublished = false): OfficeList|array |
297 | { |
298 | $cacheKey = self::CACHE_KEY_OFFICES_BY_SERVICE_PREFIX . $serviceId . ($showUnpublished ? '_unpublished' : ''); |
299 | |
300 | if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { |
301 | return $cachedData; |
302 | } |
303 | |
304 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
305 | $requestRelationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); |
306 | $providerMap = []; |
307 | foreach ($providerList as $provider) { |
308 | if (!$showUnpublished && isset($provider->data['public']) && !(bool) $provider->data['public']) { |
309 | continue; |
310 | } |
311 | |
312 | $providerMap[$provider->id] = $provider; |
313 | } |
314 | |
315 | $offices = []; |
316 | foreach ($requestRelationList as $relation) { |
317 | if ((int) $relation->request->id === $serviceId) { |
318 | $providerId = $relation->provider->id; |
319 | if (!isset($providerMap[$providerId])) { |
320 | continue; |
321 | } |
322 | |
323 | $provider = $providerMap[$providerId]; |
324 | $scope = null; |
325 | $scopeData = self::getScopeByOfficeId((int) $provider->id); |
326 | if ($scopeData instanceof ThinnedScope) { |
327 | $scope = $scopeData; |
328 | } |
329 | |
330 | $offices[] = new Office(id: (int) $provider->id, name: $provider->name, showAlternativeLocations: $provider->data['showAlternativeLocations'] ?? null, displayNameAlternatives: $provider->data['displayNameAlternatives'] ?? [], organization: $provider->data['organization'] ?? null, organizationUnit: $provider->data['organizationUnit'] ?? null, slotTimeInMinutes: $provider->data['slotTimeInMinutes'] ?? null, address: $provider->address ?? null, geo: $provider->geo ?? null, scope: $scope); |
331 | } |
332 | } |
333 | |
334 | $errors = ValidationService::validateOfficesNotFound($offices); |
335 | if (is_array($errors) && !empty($errors['errors'])) { |
336 | return $errors; |
337 | } |
338 | |
339 | $result = new OfficeList($offices); |
340 | |
341 | self::setMappedCache($cacheKey, $result); |
342 | |
343 | return $result; |
344 | } |
345 | |
346 | public static function getScopeById(?int $scopeId): ThinnedScope|array |
347 | { |
348 | $scopeList = ZmsApiClientService::getScopes() ?? new ScopeList(); |
349 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
350 | $matchingScope = null; |
351 | foreach ($scopeList as $scope) { |
352 | if ((int) $scope->id === (int) $scopeId) { |
353 | $matchingScope = $scope; |
354 | break; |
355 | } |
356 | } |
357 | |
358 | $tempScopeList = new ScopeList(); |
359 | if ($matchingScope !== null) { |
360 | $tempScopeList->addEntity($matchingScope); |
361 | } |
362 | $errors = ValidationService::validateScopesNotFound($tempScopeList); |
363 | if (is_array($errors) && !empty($errors['errors'])) { |
364 | return $errors; |
365 | } |
366 | |
367 | $providerMap = []; |
368 | foreach ($providerList as $provider) { |
369 | $key = $provider->source . '_' . $provider->id; |
370 | $providerMap[$key] = $provider; |
371 | } |
372 | |
373 | $scopeProvider = $matchingScope->getProvider(); |
374 | $providerKey = $scopeProvider ? ($scopeProvider->source . '_' . $scopeProvider->id) : null; |
375 | $matchingProv = ($providerKey && isset($providerMap[$providerKey])) |
376 | ? $providerMap[$providerKey] |
377 | : $scopeProvider; |
378 | return new ThinnedScope( |
379 | id: (int) $matchingScope->id, |
380 | provider: MapperService::providerToThinnedProvider($matchingProv), |
381 | shortName: (string) $matchingScope->getShortName() ?? null, |
382 | emailFrom: (string) $matchingScope->getEmailFrom() ?? null, |
383 | emailRequired: (bool) $matchingScope->getEmailRequired() ?? null, |
384 | telephoneActivated: (bool) $matchingScope->getTelephoneActivated() ?? null, |
385 | telephoneRequired: (bool) $matchingScope->getTelephoneRequired() ?? null, |
386 | customTextfieldActivated: (bool) $matchingScope->getCustomTextfieldActivated() ?? null, |
387 | customTextfieldRequired: (bool) $matchingScope->getCustomTextfieldRequired() ?? null, |
388 | customTextfieldLabel: $matchingScope->getCustomTextfieldLabel() ?? null, |
389 | captchaActivatedRequired: (bool) $matchingScope->getCaptchaActivatedRequired() ?? null, |
390 | displayInfo: $matchingScope->getDisplayInfo() ?? null |
391 | ); |
392 | } |
393 | |
394 | public static function getServicesByOfficeId(int $officeId, bool $showUnpublished = false): ServiceList|array |
395 | { |
396 | $cacheKey = self::CACHE_KEY_SERVICES_BY_OFFICE_PREFIX . $officeId . ($showUnpublished ? '_unpublished' : ''); |
397 | |
398 | if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { |
399 | return $cachedData; |
400 | } |
401 | |
402 | $requestList = ZmsApiClientService::getServices() ?? new RequestList(); |
403 | $requestRelationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); |
404 | $requestMap = []; |
405 | foreach ($requestList as $request) { |
406 | $additionalData = $request->getAdditionalData(); |
407 | if ( |
408 | !$showUnpublished |
409 | && isset($additionalData['public']) |
410 | && !$additionalData['public'] |
411 | ) { |
412 | continue; |
413 | } |
414 | |
415 | $requestMap[$request->id] = $request; |
416 | } |
417 | |
418 | $services = []; |
419 | foreach ($requestRelationList as $relation) { |
420 | if ((int) $relation->provider->id === $officeId) { |
421 | $requestId = $relation->request->id; |
422 | if (isset($requestMap[$requestId])) { |
423 | $request = $requestMap[$requestId]; |
424 | $services[] = new Service(id: (int) $request->id, name: $request->name, maxQuantity: $request->getAdditionalData()['maxQuantity'] ?? 1); |
425 | } |
426 | } |
427 | } |
428 | |
429 | $errors = ValidationService::validateServicesNotFound($services); |
430 | if (is_array($errors) && !empty($errors['errors'])) { |
431 | return $errors; |
432 | } |
433 | |
434 | $result = new ServiceList($services); |
435 | |
436 | self::setMappedCache($cacheKey, $result); |
437 | |
438 | return $result; |
439 | } |
440 | |
441 | public static function getServicesProvidedAtOffice(int $officeId): RequestList|array |
442 | { |
443 | $requestRelationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); |
444 | $requestRelationArray = []; |
445 | foreach ($requestRelationList as $relation) { |
446 | $requestRelationArray[] = $relation; |
447 | } |
448 | |
449 | $serviceIds = array_filter($requestRelationArray, function ($relation) use ($officeId) { |
450 | |
451 | return $relation->provider->id === $officeId || (string) $relation->provider->id === (string) $officeId; |
452 | }); |
453 | $serviceIds = array_map(function ($relation) { |
454 | |
455 | return $relation->request->id; |
456 | }, $serviceIds); |
457 | $requestList = ZmsApiClientService::getServices() ?? new RequestList(); |
458 | $requestArray = []; |
459 | foreach ($requestList as $request) { |
460 | $requestArray[] = $request; |
461 | } |
462 | |
463 | $filteredRequests = array_filter($requestArray, function ($request) use ($serviceIds) { |
464 | |
465 | return in_array($request->id, $serviceIds); |
466 | }); |
467 | $resultRequestList = new RequestList(); |
468 | foreach ($filteredRequests as $request) { |
469 | $resultRequestList->addEntity($request); |
470 | } |
471 | |
472 | return $resultRequestList; |
473 | } |
474 | |
475 | public static function getBookableFreeDays(array $officeIds, array $serviceIds, array $serviceCounts, string $startDate, string $endDate): AvailableDays|array |
476 | { |
477 | $firstDay = DateTimeFormatHelper::getInternalDateFromISO($startDate); |
478 | $lastDay = DateTimeFormatHelper::getInternalDateFromISO($endDate); |
479 | $services = []; |
480 | $providers = []; |
481 | $serviceNumber = 0; |
482 | foreach ($serviceIds as $serviceId) { |
483 | $services[] = [ |
484 | 'id' => $serviceId, |
485 | 'source' => \App::$source_name, |
486 | 'slotCount' => $serviceCounts[$serviceNumber], |
487 | ]; |
488 | $serviceNumber++; |
489 | } |
490 | |
491 | foreach ($officeIds as $officeId) { |
492 | $providers[] = [ |
493 | 'id' => $officeId, |
494 | 'source' => \App::$source_name, |
495 | ]; |
496 | } |
497 | |
498 | $freeDays = ZmsApiClientService::getFreeDays(new ProviderList($providers), new RequestList($services), $firstDay, $lastDay,) ?? new Calendar(); |
499 | $daysCollection = $freeDays->days; |
500 | $formattedDays = []; |
501 | foreach ($daysCollection as $day) { |
502 | $formattedDays[] = sprintf('%04d-%02d-%02d', $day->year, $day->month, $day->day); |
503 | } |
504 | |
505 | $errors = ValidationService::validateAppointmentDaysNotFound($formattedDays); |
506 | if (is_array($errors) && !empty($errors['errors'])) { |
507 | return $errors; |
508 | } |
509 | |
510 | return new AvailableDays($formattedDays); |
511 | } |
512 | |
513 | public static function getFreeAppointments(int $officeId, array $serviceIds, array $serviceCounts, array $date): ProcessList|array |
514 | { |
515 | $office = [ |
516 | 'id' => $officeId, |
517 | 'source' => \App::$source_name |
518 | ]; |
519 | $requests = []; |
520 | foreach ($serviceIds as $index => $serviceId) { |
521 | $service = [ |
522 | 'id' => $serviceId, |
523 | 'source' => \App::$source_name, |
524 | 'slotCount' => $serviceCounts[$index] |
525 | ]; |
526 | $requests = array_merge($requests, array_fill(0, $service['slotCount'], $service)); |
527 | } |
528 | |
529 | return ZmsApiClientService::getFreeTimeslots(new ProviderList([$office]), new RequestList($requests), $date, $date); |
530 | } |
531 | |
532 | private static function processFreeSlots(ProcessList $freeSlots): array |
533 | { |
534 | $errors = ValidationService::validateGetProcessFreeSlots($freeSlots); |
535 | if (is_array($errors) && !empty($errors['errors'])) { |
536 | return $errors; |
537 | } |
538 | |
539 | $currentTimestamp = time(); |
540 | $allTimestamps = []; |
541 | |
542 | foreach ($freeSlots as $slot) { |
543 | if (isset($slot->appointments) && is_iterable($slot->appointments)) { |
544 | foreach ($slot->appointments as $appointment) { |
545 | if (isset($appointment->date)) { |
546 | $timestamp = (int) $appointment->date; |
547 | if ($timestamp > $currentTimestamp) { |
548 | $allTimestamps[] = $timestamp; |
549 | } |
550 | } |
551 | } |
552 | } |
553 | } |
554 | |
555 | $uniqueTimestamps = array_values(array_unique($allTimestamps)); |
556 | sort($uniqueTimestamps); |
557 | |
558 | $errors = ValidationService::validateGetProcessByIdTimestamps($uniqueTimestamps); |
559 | if (is_array($errors) && !empty($errors['errors'])) { |
560 | return $errors; |
561 | } |
562 | |
563 | return $uniqueTimestamps; |
564 | } |
565 | |
566 | public static function getAvailableAppointments(string $date, array $officeIds, array $serviceIds, array $serviceCounts, ?bool $groupByOffice = false): AvailableAppointments|AvailableAppointmentsByOffice|array |
567 | { |
568 | $requests = []; |
569 | $providers = []; |
570 | foreach ($serviceIds as $index => $serviceId) { |
571 | $slotCount = isset($serviceCounts[$index]) ? intval($serviceCounts[$index]) : 1; |
572 | for ($i = 0; $i < $slotCount; $i++) { |
573 | $requests[] = [ |
574 | 'id' => $serviceId, |
575 | 'source' => \App::$source_name |
576 | ]; |
577 | } |
578 | } |
579 | |
580 | foreach ($officeIds as $officeId) { |
581 | $providers[] = [ |
582 | 'id' => $officeId, |
583 | 'source' => \App::$source_name |
584 | ]; |
585 | } |
586 | |
587 | $freeSlots = ZmsApiClientService::getFreeTimeslots(new ProviderList($providers), new RequestList($requests), DateTimeFormatHelper::getInternalDateFromISO($date), DateTimeFormatHelper::getInternalDateFromISO($date)) ?? new ProcessList(); |
588 | $timestamps = self::processFreeSlots($freeSlots); |
589 | if (isset($timestamps['errors']) && !empty($timestamps['errors'])) { |
590 | return $timestamps; |
591 | } |
592 | |
593 | if ($groupByOffice) { |
594 | return new AvailableAppointmentsByOffice(['appointmentTimestamps' => $timestamps]); |
595 | } |
596 | |
597 | return new AvailableAppointments($timestamps); |
598 | } |
599 | |
600 | public static function reserveTimeslot(Process $appointmentProcess, array $serviceIds, array $serviceCounts): ThinnedProcess|array |
601 | { |
602 | $errors = ValidationService::validateServiceArrays($serviceIds, $serviceCounts); |
603 | if (!empty($errors)) { |
604 | return $errors; |
605 | } |
606 | $process = ZmsApiClientService::reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts); |
607 | return MapperService::processToThinnedProcess($process); |
608 | } |
609 | |
610 | public static function getThinnedProcessById(?int $processId, ?string $authKey): ThinnedProcess|array |
611 | { |
612 | |
613 | $process = ZmsApiClientService::getProcessById($processId, $authKey); |
614 | $errors = ValidationService::validateGetProcessNotFound($process); |
615 | if (is_array($errors) && !empty($errors['errors'])) { |
616 | return $errors; |
617 | } |
618 | $thinnedProcess = MapperService::processToThinnedProcess($process); |
619 | |
620 | $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); |
621 | $providerMap = []; |
622 | foreach ($providerList as $provider) { |
623 | $key = $provider->getSource() . '_' . $provider->id; |
624 | $providerMap[$key] = $provider; |
625 | } |
626 | |
627 | $thinnedScope = null; |
628 | if ($process->scope instanceof Scope) { |
629 | $scopeProvider = $process->scope->getProvider(); |
630 | $providerKey = $scopeProvider ? ($scopeProvider->getSource() . '_' . $scopeProvider->id) : null; |
631 | $matchingProvider = $providerKey && isset($providerMap[$providerKey]) ? $providerMap[$providerKey] : $scopeProvider; |
632 | $thinnedProvider = MapperService::providerToThinnedProvider($matchingProvider); |
633 | $thinnedScope = new ThinnedScope( |
634 | id: (int) $process->scope->id, |
635 | provider: $thinnedProvider, |
636 | shortName: (string) $process->scope->getShortName() ?? null, |
637 | emailFrom: (string) $process->scope->getEmailFrom() ?? null, |
638 | emailRequired: (bool) $process->scope->getEmailRequired() ?? false, |
639 | telephoneActivated: (bool) $process->scope->getTelephoneActivated() ?? false, |
640 | telephoneRequired: (bool) $process->scope->getTelephoneRequired() ?? false, |
641 | customTextfieldActivated: (bool) $process->scope->getCustomTextfieldActivated() ?? false, |
642 | customTextfieldRequired: (bool) $process->scope->getCustomTextfieldRequired() ?? false, |
643 | customTextfieldLabel: $process->scope->getCustomTextfieldLabel() ?? null, |
644 | captchaActivatedRequired: (bool) $process->scope->getCaptchaActivatedRequired() ?? false, |
645 | displayInfo: $process->scope->getDisplayInfo() ?? null |
646 | ); |
647 | } |
648 | |
649 | $thinnedProcess->scope = $thinnedScope; |
650 | return $thinnedProcess; |
651 | } |
652 | |
653 | public static function updateClientData(Process $reservedProcess): Process|array |
654 | { |
655 | $clientUpdateResult = ZmsApiClientService::submitClientData($reservedProcess); |
656 | if (isset($clientUpdateResult['error'])) { |
657 | return $clientUpdateResult; |
658 | } |
659 | return $clientUpdateResult; |
660 | } |
661 | |
662 | public static function preconfirmAppointment(Process $reservedProcess): Process|array |
663 | { |
664 | $clientUpdateResult = ZmsApiClientService::preconfirmProcess($reservedProcess); |
665 | if (isset($clientUpdateResult['error'])) { |
666 | return $clientUpdateResult; |
667 | } |
668 | return $clientUpdateResult; |
669 | } |
670 | |
671 | public static function confirmAppointment(Process $preconfirmedProcess): Process|array |
672 | { |
673 | $clientUpdateResult = ZmsApiClientService::confirmProcess($preconfirmedProcess); |
674 | if (isset($clientUpdateResult['error'])) { |
675 | return $clientUpdateResult; |
676 | } |
677 | return $clientUpdateResult; |
678 | } |
679 | |
680 | public static function cancelAppointment(Process $confirmedProcess): Process|array |
681 | { |
682 | $clientUpdateResult = ZmsApiClientService::cancelAppointment($confirmedProcess); |
683 | if (isset($clientUpdateResult['error'])) { |
684 | return $clientUpdateResult; |
685 | } |
686 | return $clientUpdateResult; |
687 | } |
688 | |
689 | public static function sendPreconfirmationEmail(Process $reservedProcess): Process|array |
690 | { |
691 | $clientUpdateResult = ZmsApiClientService::sendPreconfirmationEmail($reservedProcess); |
692 | if (isset($clientUpdateResult['error'])) { |
693 | return $clientUpdateResult; |
694 | } |
695 | return $clientUpdateResult; |
696 | } |
697 | |
698 | public static function sendConfirmationEmail(Process $preconfirmedProcess): Process|array |
699 | { |
700 | $clientUpdateResult = ZmsApiClientService::sendConfirmationEmail($preconfirmedProcess); |
701 | if (isset($clientUpdateResult['error'])) { |
702 | return $clientUpdateResult; |
703 | } |
704 | return $clientUpdateResult; |
705 | } |
706 | |
707 | public static function sendCancellationEmail(Process $confirmedProcess): Process|array |
708 | { |
709 | $clientUpdateResult = ZmsApiClientService::sendCancellationEmail($confirmedProcess); |
710 | if (isset($clientUpdateResult['error'])) { |
711 | return $clientUpdateResult; |
712 | } |
713 | return $clientUpdateResult; |
714 | } |
715 | } |