Lines
90.14%
366 / 406
Methods
27.77%
5 / 18
Classes
0.00%
0 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| resolveAllowDisabledServicesMix | 66.66% | 2 / 3 | 0.00% | 0 / 1 | 3.33 | ||
| resolveSharedBookingOfficeIds | 66.66% | 2 / 3 | 0.00% | 0 / 1 | 3.33 | ||
| mapScopeForProvider | 86.66% | 13 / 15 | 0.00% | 0 / 1 | 10.24 | ||
| extractReservationDuration | 83.33% | 5 / 6 | 0.00% | 0 / 1 | 4.07 | ||
| extractActivationDuration | 81.81% | 9 / 11 | 0.00% | 0 / 1 | 7.29 | ||
| mapOfficesWithScope | 91.80% | 56 / 61 | 0.00% | 0 / 1 | 50.32 | ||
| mapCombinable | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 2 | ||
| mapServicesWithCombinations | 97.36% | 37 / 38 | 0.00% | 0 / 1 | 15 | ||
| mapRelations | 91.66% | 11 / 12 | 0.00% | 0 / 1 | 4.01 | ||
| scopeToThinnedScope | 96.96% | 64 / 66 | 0.00% | 0 / 1 | 26 | ||
| processToThinnedProcess | 84.44% | 38 / 45 | 0.00% | 0 / 1 | 32.17 | ||
| thinnedProcessToProcess | 93.75% | 30 / 32 | 0.00% | 0 / 1 | 7.01 | ||
| createScope | 86.66% | 39 / 45 | 0.00% | 0 / 1 | 8.15 | ||
| createRequests | 59.09% | 13 / 22 | 0.00% | 0 / 1 | 5.10 | ||
| contactToThinnedContact | 100.00% | 19 / 19 | 100.00% | 1 / 1 | 6 | ||
| providerToThinnedProvider | 100.00% | 13 / 13 | 100.00% | 1 / 1 | 10 | ||
| contactCoordinate | 100.00% | 5 / 5 | 100.00% | 1 / 1 | 3 | ||
| generateIcsContent | 100.00% | 9 / 9 | 100.00% | 1 / 1 | 6 | ||
| 37 | class MapperService | |
| 38 | { | |
| 39 | private static function resolveAllowDisabledServicesMix(\BO\Zmsentities\Schema\Entity $provider): ?array | |
| 40 | { | |
| 41 | if (!isset($provider->data['allowDisabledServicesMix']) || !is_array($provider->data['allowDisabledServicesMix'])) { | |
| 42 | return null; | |
| 43 | } | |
| 44 | return array_map('intval', $provider->data['allowDisabledServicesMix']); | |
| 45 | } | |
| 46 | ||
| 47 | private static function resolveSharedBookingOfficeIds(\BO\Zmsentities\Schema\Entity $provider): ?array | |
| 48 | { | |
| 49 | if (!isset($provider->data['sharedBookingOfficeIds']) || !is_array($provider->data['sharedBookingOfficeIds'])) { | |
| 50 | return null; | |
| 51 | } | |
| 52 | return array_map('intval', $provider->data['sharedBookingOfficeIds']); | |
| 53 | } | |
| 54 | ||
| 55 | public static function mapScopeForProvider( | |
| 56 | int $providerId, | |
| 57 | ThinnedScopeList $scopes, | |
| 58 | ?string $providerSource = null | |
| 59 | ): ?ThinnedScope { | |
| 60 | foreach ($scopes->getScopes() as $scope) { | |
| 61 | if (!$scope instanceof ThinnedScope) { | |
| 62 | continue; | |
| 63 | } | |
| 64 | ||
| 65 | $prov = $scope->provider ?? null; | |
| 66 | if (!$prov) { | |
| 67 | continue; | |
| 68 | } | |
| 69 | ||
| 70 | $provId = is_object($prov) ? ($prov->id ?? null) : ($prov['id'] ?? null); | |
| 71 | $provSrc = is_object($prov) ? ($prov->source ?? null) : ($prov['source'] ?? null); | |
| 72 | ||
| 73 | if ((string)$provId !== (string)$providerId) { | |
| 74 | continue; | |
| 75 | } | |
| 76 | ||
| 77 | if ($providerSource === null || $providerSource === '') { | |
| 78 | return $scope; | |
| 79 | } | |
| 80 | ||
| 81 | if ((string)$provSrc === $providerSource) { | |
| 82 | return $scope; | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | return null; | |
| 87 | } | |
| 88 | ||
| 89 | public static function extractReservationDuration(Scope|ThinnedScope|null $scope): ?int | |
| 90 | { | |
| 91 | if ($scope === null) { | |
| 92 | return null; | |
| 93 | } | |
| 94 | if ($scope instanceof ThinnedScope) { | |
| 95 | return $scope->getReservationDuration(); | |
| 96 | } | |
| 97 | $reservationDuration = $scope->toProperty()->preferences?->appointment?->reservationDuration?->get(); | |
| 98 | return $reservationDuration !== null ? (int) $reservationDuration : null; | |
| 99 | } | |
| 100 | ||
| 101 | public static function extractActivationDuration(Scope|ThinnedScope|null $scope): ?int | |
| 102 | { | |
| 103 | if ($scope === null) { | |
| 104 | return null; | |
| 105 | } | |
| 106 | ||
| 107 | if ($scope instanceof ThinnedScope) { | |
| 108 | $activationDuration = $scope->getActivationDuration(); | |
| 109 | if ($activationDuration === null || $activationDuration === '') { | |
| 110 | return null; | |
| 111 | } | |
| 112 | return $activationDuration; | |
| 113 | } | |
| 114 | ||
| 115 | $activationDuration = $scope->toProperty()->preferences?->appointment?->activationDuration?->get(); | |
| 116 | if ($activationDuration === null || $activationDuration === '') { | |
| 117 | return null; | |
| 118 | } | |
| 119 | return (int) $activationDuration; | |
| 120 | } | |
| 121 | ||
| 122 | /** | |
| 123 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) | |
| 124 | * @SuppressWarnings(PHPMD.NPathComplexity) | |
| 125 | * @TODO: Extract mapping logic into specialized mapper classes for each entity type | |
| 126 | * | |
| 127 | */ | |
| 128 | public static function mapOfficesWithScope(ProviderList $providerList, bool $showUnpublished = false): OfficeList | |
| 129 | { | |
| 130 | $offices = []; | |
| 131 | $scopes = ZmsApiFacadeService::getScopes(); | |
| 132 | if (!$scopes instanceof ThinnedScopeList) { | |
| 133 | return new OfficeList(); | |
| 134 | } | |
| 135 | ||
| 136 | foreach ($providerList as $provider) { | |
| 137 | // ✅ Source normalisieren: leerer String -> Fallback auf App::$source_name | |
| 138 | $providerSource = isset($provider->source) && $provider->source !== '' | |
| 139 | ? (string)$provider->source | |
| 140 | : \App::$source_name; | |
| 141 | ||
| 142 | $providerScope = self::mapScopeForProvider( | |
| 143 | (int) $provider->id, | |
| 144 | $scopes, | |
| 145 | $providerSource | |
| 146 | ); | |
| 147 | ||
| 148 | if (!$showUnpublished && isset($provider->data['public']) && !(bool) $provider->data['public']) { | |
| 149 | continue; | |
| 150 | } | |
| 151 | ||
| 152 | $offices[] = new Office( | |
| 153 | id: isset($provider->id) ? (int) $provider->id : 0, | |
| 154 | name: isset($provider->displayName) ? $provider->displayName : (isset($provider->name) ? $provider->name : null), | |
| 155 | address: isset($provider->data['address']) ? $provider->data['address'] : null, | |
| 156 | showAlternativeLocations: isset($provider->data['showAlternativeLocations']) ? $provider->data['showAlternativeLocations'] : null, | |
| 157 | displayNameAlternatives: $provider->data['displayNameAlternatives'] ?? [], | |
| 158 | organization: $provider->data['organization'] ?? null, | |
| 159 | organizationUnit: $provider->data['organizationUnit'] ?? null, | |
| 160 | slotTimeInMinutes: $provider->data['slotTimeInMinutes'] ?? null, | |
| 161 | geo: isset($provider->data['geo']) ? $provider->data['geo'] : null, | |
| 162 | disabledByServices: isset($provider->data['dontShowByServices']) ? $provider->data['dontShowByServices'] : [], | |
| 163 | priority: isset($provider->data['prio']) ? $provider->data['prio'] : 1, | |
| 164 | scope: isset($providerScope) && !isset($providerScope['errors']) ? new ThinnedScope( | |
| 165 | id: isset($providerScope->id) ? $providerScope->id : 0, | |
| 166 | provider: isset($providerScope->provider) ? $providerScope->provider : null, | |
| 167 | shortName: isset($providerScope->shortName) ? $providerScope->shortName : null, | |
| 168 | emailFrom: isset($providerScope->emailFrom) ? $providerScope->emailFrom : null, | |
| 169 | emailRequired: isset($providerScope->emailRequired) ? $providerScope->emailRequired : null, | |
| 170 | telephoneActivated: isset($providerScope->telephoneActivated) ? $providerScope->telephoneActivated : null, | |
| 171 | telephoneRequired: isset($providerScope->telephoneRequired) ? $providerScope->telephoneRequired : null, | |
| 172 | customTextfieldActivated: isset($providerScope->customTextfieldActivated) ? $providerScope->customTextfieldActivated : null, | |
| 173 | customTextfieldRequired: isset($providerScope->customTextfieldRequired) ? $providerScope->customTextfieldRequired : null, | |
| 174 | customTextfieldLabel: isset($providerScope->customTextfieldLabel) ? $providerScope->customTextfieldLabel : null, | |
| 175 | customTextfield2Activated: isset($providerScope->customTextfield2Activated) ? $providerScope->customTextfield2Activated : null, | |
| 176 | customTextfield2Required: isset($providerScope->customTextfield2Required) ? $providerScope->customTextfield2Required : null, | |
| 177 | customTextfield2Label: isset($providerScope->customTextfield2Label) ? $providerScope->customTextfield2Label : null, | |
| 178 | captchaActivatedRequired: isset($providerScope->captchaActivatedRequired) ? $providerScope->captchaActivatedRequired : null, | |
| 179 | infoForAppointment: isset($providerScope->infoForAppointment) | |
| 180 | ? ($providerScope->infoForAppointment === '' ? null : $providerScope->infoForAppointment) | |
| 181 | : null, | |
| 182 | infoForAllAppointments: isset($providerScope->infoForAllAppointments) | |
| 183 | ? ($providerScope->infoForAllAppointments === '' ? null : $providerScope->infoForAllAppointments) | |
| 184 | : null, | |
| 185 | appointmentsPerMail: isset($providerScope->appointmentsPerMail) ? ($providerScope->appointmentsPerMail === '' ? null : $providerScope->appointmentsPerMail) : null, | |
| 186 | slotsPerAppointment: isset($providerScope->slotsPerAppointment) ? ($providerScope->slotsPerAppointment === '' ? null : $providerScope->slotsPerAppointment) : null, | |
| 187 | whitelistedMails: isset($providerScope->whitelistedMails) ? ($providerScope->whitelistedMails === '' ? null : $providerScope->whitelistedMails) : null, | |
| 188 | reservationDuration: (int) self::extractReservationDuration($providerScope), | |
| 189 | activationDuration: self::extractActivationDuration($providerScope), | |
| 190 | hint: isset($providerScope->hint) ? (trim($providerScope->hint) === '' ? null : $providerScope->hint) : null | |
| 191 | ) : null, | |
| 192 | slotsPerAppointment: isset($providerScope) && !isset($providerScope['errors']) && isset($providerScope->slotsPerAppointment) ? ($providerScope->slotsPerAppointment === '' ? null : $providerScope->slotsPerAppointment) : null, | |
| 193 | parentId: isset($provider->parent_id) ? (int) $provider->parent_id : null, | |
| 194 | allowDisabledServicesMix: self::resolveAllowDisabledServicesMix($provider), | |
| 195 | sharedBookingOfficeIds: self::resolveSharedBookingOfficeIds($provider) | |
| 196 | ); | |
| 197 | } | |
| 198 | ||
| 199 | return new OfficeList($offices); | |
| 200 | } | |
| 201 | ||
| 202 | public static function mapCombinable(array $serviceCombinations): ?Combinable | |
| 203 | { | |
| 204 | return !empty($serviceCombinations) ? new Combinable($serviceCombinations) : null; | |
| 205 | } | |
| 206 | ||
| 207 | /** | |
| 208 | * Map services with combinations based on request and relation lists. | |
| 209 | * | |
| 210 | * @SuppressWarnings(PHPMD.NPathComplexity) | |
| 211 | */ | |
| 212 | public static function mapServicesWithCombinations( | |
| 213 | RequestList $requestList, | |
| 214 | RequestRelationList $relationList, | |
| 215 | bool $showUnpublished = false | |
| 216 | ): ServiceList { | |
| 217 | /** @var array<string, array<int>> $servicesProviderIds */ | |
| 218 | $servicesProviderIds = []; | |
| 219 | foreach ($relationList as $relation) { | |
| 220 | if (!$showUnpublished && !$relation->isPublic()) { | |
| 221 | continue; | |
| 222 | } | |
| 223 | ||
| 224 | $serviceId = $relation->request->id; | |
| 225 | $servicesProviderIds[$serviceId] ??= []; | |
| 226 | $servicesProviderIds[$serviceId][] = $relation->provider->id; | |
| 227 | } | |
| 228 | ||
| 229 | $services = []; | |
| 230 | $requestArray = iterator_to_array($requestList); | |
| 231 | usort($requestArray, function ($a, $b) { | |
| 232 | ||
| 233 | return $a->getId() <=> $b->getId(); | |
| 234 | // Sorting by service ID (ascending order) | |
| 235 | }); | |
| 236 | foreach ($requestArray as $service) { | |
| 237 | if ( | |
| 238 | !$showUnpublished | |
| 239 | && isset($service->getAdditionalData()['public']) | |
| 240 | && !$service->getAdditionalData()['public'] | |
| 241 | ) { | |
| 242 | continue; | |
| 243 | } | |
| 244 | ||
| 245 | /** @var array<string, array<int>> $serviceCombinations */ | |
| 246 | $serviceCombinations = []; | |
| 247 | $combinableData = $service->getAdditionalData()['combinable'] ?? []; | |
| 248 | foreach ($combinableData as $combinationServiceId) { | |
| 249 | $commonProviders = array_intersect($servicesProviderIds[$service->getId()] ?? [], $servicesProviderIds[$combinationServiceId] ?? []); | |
| 250 | $serviceCombinations[$combinationServiceId] = !empty($commonProviders) ? array_values($commonProviders) : []; | |
| 251 | } | |
| 252 | ||
| 253 | $combinable = self::mapCombinable($serviceCombinations); | |
| 254 | ||
| 255 | $extra = $service->getAdditionalData() ?? []; | |
| 256 | $parentId = isset($service->parent_id) ? (int)$service->parent_id : (isset($extra['parent_id']) ? (int)$extra['parent_id'] : null); | |
| 257 | $variantId = isset($service->variant_id) ? (int)$service->variant_id : (isset($extra['variant_id']) ? (int)$extra['variant_id'] : null); | |
| 258 | ||
| 259 | if (!empty($servicesProviderIds[$service->getId()])) { | |
| 260 | $services[] = new Service( | |
| 261 | id: (int) $service->getId(), | |
| 262 | name: $service->getName(), | |
| 263 | maxQuantity: $service->getAdditionalData()['maxQuantity'] ?? 1, | |
| 264 | combinable: $combinable ?? new Combinable(), | |
| 265 | parentId: $parentId, | |
| 266 | variantId: $variantId, | |
| 267 | showOnStartPage: $service->getAdditionalData()['showOnStartPage'] ?? true, | |
| 268 | rootParentId: (int) $service->getRootParentId(), | |
| 269 | ); | |
| 270 | } | |
| 271 | } | |
| 272 | ||
| 273 | return new ServiceList($services); | |
| 274 | } | |
| 275 | ||
| 276 | ||
| 277 | public static function mapRelations( | |
| 278 | RequestRelationList $relationList, | |
| 279 | bool $showUnpublished = false | |
| 280 | ): OfficeServiceRelationList { | |
| 281 | $relations = []; | |
| 282 | foreach ($relationList as $relation) { | |
| 283 | if (!$showUnpublished && !$relation->isPublic()) { | |
| 284 | continue; | |
| 285 | } | |
| 286 | ||
| 287 | $relations[] = new OfficeServiceRelation( | |
| 288 | officeId: (int) $relation->provider->id, | |
| 289 | serviceId: (int) $relation->request->id, | |
| 290 | slots: intval($relation->slots), | |
| 291 | public: (bool) $relation->isPublic(), | |
| 292 | maxQuantity: (int) $relation->maxQuantity | |
| 293 | ); | |
| 294 | } | |
| 295 | ||
| 296 | return new OfficeServiceRelationList($relations); | |
| 297 | } | |
| 298 | ||
| 299 | /** | |
| 300 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) | |
| 301 | * @SuppressWarnings(PHPMD.NPathComplexity) | |
| 302 | */ | |
| 303 | public static function scopeToThinnedScope(Scope $scope): ThinnedScope | |
| 304 | { | |
| 305 | if (!$scope || !isset($scope->id)) { | |
| 306 | return new ThinnedScope(); | |
| 307 | } | |
| 308 | ||
| 309 | $thinnedProvider = null; | |
| 310 | try { | |
| 311 | $provider = null; | |
| 312 | if (isset($scope->provider) && $scope->provider instanceof Provider) { | |
| 313 | $provider = $scope->provider; | |
| 314 | } elseif (isset($scope->provider) || method_exists($scope, 'getProvider')) { | |
| 315 | $provider = $scope->getProvider(); | |
| 316 | } | |
| 317 | if ($provider instanceof Provider) { | |
| 318 | $thinnedProvider = self::providerToThinnedProvider($provider); | |
| 319 | } | |
| 320 | } catch (\BO\Zmsentities\Exception\ScopeMissingProvider $e) { | |
| 321 | $thinnedProvider = null; | |
| 322 | } | |
| 323 | ||
| 324 | return new ThinnedScope( | |
| 325 | id: (int) ($scope->id ?? 0), | |
| 326 | provider: $thinnedProvider, | |
| 327 | shortName: isset($scope->shortName) ? (string) $scope->shortName : null, | |
| 328 | emailFrom: (string) $scope->getEmailFrom(), | |
| 329 | emailRequired: $scope->getEmailRequired() === null | |
| 330 | ? null | |
| 331 | : (bool) $scope->getEmailRequired(), | |
| 332 | telephoneActivated: $scope->getTelephoneActivated() === null | |
| 333 | ? null | |
| 334 | : (bool) $scope->getTelephoneActivated(), | |
| 335 | telephoneRequired: $scope->getTelephoneRequired() === null | |
| 336 | ? null | |
| 337 | : (bool) $scope->getTelephoneRequired(), | |
| 338 | customTextfieldActivated: $scope->getCustomTextfieldActivated() === null | |
| 339 | ? null | |
| 340 | : (bool) $scope->getCustomTextfieldActivated(), | |
| 341 | customTextfieldRequired: $scope->getCustomTextfieldRequired() === null | |
| 342 | ? null | |
| 343 | : (bool) $scope->getCustomTextfieldRequired(), | |
| 344 | customTextfieldLabel: $scope->getCustomTextfieldLabel() === null | |
| 345 | ? null | |
| 346 | : (string) $scope->getCustomTextfieldLabel(), | |
| 347 | customTextfield2Activated: $scope->getCustomTextfield2Activated() === null | |
| 348 | ? null | |
| 349 | : (bool) $scope->getCustomTextfield2Activated(), | |
| 350 | customTextfield2Required: $scope->getCustomTextfield2Required() === null | |
| 351 | ? null | |
| 352 | : (bool) $scope->getCustomTextfield2Required(), | |
| 353 | customTextfield2Label: $scope->getCustomTextfield2Label() === null | |
| 354 | ? null | |
| 355 | : (string) $scope->getCustomTextfield2Label(), | |
| 356 | captchaActivatedRequired: $scope->getCaptchaActivatedRequired() === null | |
| 357 | ? null | |
| 358 | : (bool) $scope->getCaptchaActivatedRequired(), | |
| 359 | infoForAppointment: $scope->getInfoForAppointment() === null | |
| 360 | ? null | |
| 361 | : (string) $scope->getInfoForAppointment(), | |
| 362 | infoForAllAppointments: $scope->getInfoForAllAppointments() === null | |
| 363 | ? null | |
| 364 | : (string) $scope->getInfoForAllAppointments(), | |
| 365 | slotsPerAppointment: $scope->getSlotsPerAppointment() === null | |
| 366 | ? null | |
| 367 | : (string) $scope->getSlotsPerAppointment(), | |
| 368 | appointmentsPerMail: $scope->getAppointmentsPerMail() !== null | |
| 369 | ? (string) $scope->getAppointmentsPerMail() | |
| 370 | : null, | |
| 371 | whitelistedMails: $scope->getWhitelistedMails() === null | |
| 372 | ? null | |
| 373 | : (string) $scope->getWhitelistedMails(), | |
| 374 | reservationDuration: MapperService::extractReservationDuration($scope), | |
| 375 | activationDuration: MapperService::extractActivationDuration($scope), | |
| 376 | hint: (trim((string) ($scope->getScopeHint() ?? '')) === '') ? null : (string) $scope->getScopeHint() | |
| 377 | ); | |
| 378 | } | |
| 379 | ||
| 380 | /** | |
| 381 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) | |
| 382 | * @SuppressWarnings(PHPMD.NPathComplexity) | |
| 383 | * @TODO: Break down process mapping into smaller, focused methods | |
| 384 | */ | |
| 385 | public static function processToThinnedProcess(Process $myProcess): ThinnedProcess | |
| 386 | { | |
| 387 | $subRequestCounts = []; | |
| 388 | $mainServiceId = null; | |
| 389 | $mainServiceName = null; | |
| 390 | $mainServiceCount = 0; | |
| 391 | $requests = iterator_to_array($myProcess->getRequests()); | |
| 392 | if (count($requests) > 0) { | |
| 393 | $mainServiceId = $requests[0]->id; | |
| 394 | foreach ($requests as $request) { | |
| 395 | if ($request->id === $mainServiceId) { | |
| 396 | $mainServiceCount++; | |
| 397 | if (!$mainServiceName && isset($request->name)) { | |
| 398 | $mainServiceName = $request->name; | |
| 399 | } | |
| 400 | } else { | |
| 401 | if (!isset($subRequestCounts[$request->id])) { | |
| 402 | $subRequestCounts[$request->id] = [ | |
| 403 | 'id' => (int) $request->id, | |
| 404 | 'name' => $request->name, | |
| 405 | 'count' => 0, | |
| 406 | ]; | |
| 407 | } | |
| 408 | $subRequestCounts[$request->id]['count']++; | |
| 409 | } | |
| 410 | } | |
| 411 | } | |
| 412 | ||
| 413 | // Generate ICS content if process has appointments with time | |
| 414 | $icsContent = self::generateIcsContent($myProcess); | |
| 415 | $scope = $myProcess->getCurrentScope(); | |
| 416 | $contactName = $scope['contact']['name'] ?? null; | |
| 417 | $providerId = $scope['provider']['id'] ?? 0; | |
| 418 | ||
| 419 | return new ThinnedProcess( | |
| 420 | processId: (int) $myProcess->id, | |
| 421 | timestamp: (isset($myProcess->appointments[0]) && isset($myProcess->appointments[0]->date)) ? strval($myProcess->appointments[0]->date) : null, | |
| 422 | authKey: $myProcess->authKey, | |
| 423 | captchaToken: isset($myProcess->captchaToken) ? $myProcess->captchaToken : null, | |
| 424 | familyName: (isset($myProcess->clients[0]) && isset($myProcess->clients[0]->familyName)) ? $myProcess->clients[0]->familyName : null, | |
| 425 | customTextfield: isset($myProcess->customTextfield) ? $myProcess->customTextfield : null, | |
| 426 | customTextfield2: isset($myProcess->customTextfield2) ? $myProcess->customTextfield2 : null, | |
| 427 | email: (isset($myProcess->clients[0]) && isset($myProcess->clients[0]->email)) ? $myProcess->clients[0]->email : null, | |
| 428 | telephone: (isset($myProcess->clients[0]) && isset($myProcess->clients[0]->telephone)) ? $myProcess->clients[0]->telephone : null, | |
| 429 | officeName: $contactName !== null && $contactName !== '' ? $contactName : null, | |
| 430 | officeId: (int) $providerId, | |
| 431 | scope: isset($myProcess['scope']) ? self::scopeToThinnedScope($scope) : null, | |
| 432 | subRequestCounts: array_values($subRequestCounts), | |
| 433 | serviceId: isset($mainServiceId) ? (int) $mainServiceId : 0, | |
| 434 | serviceName: isset($mainServiceName) ? $mainServiceName : null, | |
| 435 | serviceCount: $mainServiceCount, | |
| 436 | status: (isset($myProcess->queue) && isset($myProcess->queue->status)) ? $myProcess->queue->status : null, | |
| 437 | slotCount: (isset($myProcess->appointments[0]) && isset($myProcess->appointments[0]->slotCount)) ? (int) $myProcess->appointments[0]->slotCount : null, | |
| 438 | displayNumber: ($myProcess->getDisplayNumber() ?: null), | |
| 439 | icsContent: isset($icsContent) ? $icsContent : null | |
| 440 | ); | |
| 441 | } | |
| 442 | ||
| 443 | public static function thinnedProcessToProcess(ThinnedProcess $thinnedProcess): Process | |
| 444 | { | |
| 445 | if (!$thinnedProcess || !isset($thinnedProcess->processId)) { | |
| 446 | return new Process(); | |
| 447 | } | |
| 448 | ||
| 449 | $processEntity = new Process(); | |
| 450 | $processEntity->id = $thinnedProcess->processId; | |
| 451 | $processEntity->authKey = $thinnedProcess->authKey ?? null; | |
| 452 | $customTextfield = $thinnedProcess->customTextfield ?? null; | |
| 453 | $processEntity->customTextfield = $customTextfield === null ? null : ProcessPlainText::normalize($customTextfield); | |
| 454 | $customTextfield2 = $thinnedProcess->customTextfield2 ?? null; | |
| 455 | $processEntity->customTextfield2 = $customTextfield2 === null | |
| 456 | ? null | |
| 457 | : ProcessPlainText::normalize($customTextfield2); | |
| 458 | $processEntity->captchaToken = $thinnedProcess->captchaToken ?? null; | |
| 459 | ||
| 460 | $client = new Client(); | |
| 461 | $client->familyName = $thinnedProcess->familyName ?? null; | |
| 462 | $client->email = $thinnedProcess->email ?? null; | |
| 463 | $client->telephone = $thinnedProcess->telephone ?? null; | |
| 464 | $processEntity->clients = [$client]; | |
| 465 | ||
| 466 | $appointment = new Appointment(); | |
| 467 | $appointment->slotCount = $thinnedProcess->slotCount ?? null; | |
| 468 | $appointment->date = $thinnedProcess->timestamp ?? null; | |
| 469 | $processEntity->appointments = [$appointment]; | |
| 470 | $processEntity->scope = self::createScope($thinnedProcess); | |
| 471 | $processEntity->requests = self::createRequests($thinnedProcess); | |
| 472 | ||
| 473 | if (isset($thinnedProcess->status)) { | |
| 474 | $processEntity->queue = new Queue(['status' => $thinnedProcess->status]); | |
| 475 | $processEntity->status = $thinnedProcess->status; | |
| 476 | } | |
| 477 | ||
| 478 | if (isset($thinnedProcess->displayNumber)) { | |
| 479 | $processEntity->displayNumber = $thinnedProcess->displayNumber; | |
| 480 | } | |
| 481 | ||
| 482 | $processEntity->lastChange = time(); | |
| 483 | $processEntity->createIP = ClientIpHelper::getClientIp(); | |
| 484 | $processEntity->createTimestamp = time(); | |
| 485 | return $processEntity; | |
| 486 | } | |
| 487 | ||
| 488 | private static function createScope(ThinnedProcess $thinnedProcess): Scope | |
| 489 | { | |
| 490 | $scope = new Scope(); | |
| 491 | if ($thinnedProcess->scope) { | |
| 492 | $providerSource = $thinnedProcess->scope->provider->source ?? 'dldb'; | |
| 493 | ||
| 494 | $scope->id = $thinnedProcess->scope->id; | |
| 495 | $scope->source = $providerSource; | |
| 496 | ||
| 497 | $scope->preferences = [ | |
| 498 | 'client' => [ | |
| 499 | 'appointmentsPerMail' => $thinnedProcess->scope->getAppointmentsPerMail() ?? null, | |
| 500 | 'slotsPerAppointment' => $thinnedProcess->scope->getSlotsPerAppointment() ?? null, | |
| 501 | "whitelistedMails" => $thinnedProcess->scope->getWhitelistedMails() ?? null, | |
| 502 | 'emailFrom' => $thinnedProcess->scope->getEmailFrom() ?? null, | |
| 503 | 'emailRequired' => $thinnedProcess->scope->getEmailRequired() ?? false, | |
| 504 | 'telephoneActivated' => $thinnedProcess->scope->getTelephoneActivated() ?? false, | |
| 505 | 'telephoneRequired' => $thinnedProcess->scope->getTelephoneRequired() ?? false, | |
| 506 | 'customTextfieldActivated' => $thinnedProcess->scope->getCustomTextfieldActivated() ?? false, | |
| 507 | 'customTextfieldRequired' => $thinnedProcess->scope->getCustomTextfieldRequired() ?? false, | |
| 508 | 'customTextfieldLabel' => $thinnedProcess->scope->getCustomTextfieldLabel() ?? null, | |
| 509 | 'customTextfield2Activated' => $thinnedProcess->scope->getCustomTextfield2Activated() ?? false, | |
| 510 | 'customTextfield2Required' => $thinnedProcess->scope->getCustomTextfield2Required() ?? false, | |
| 511 | 'customTextfield2Label' => $thinnedProcess->scope->getCustomTextfield2Label() ?? null | |
| 512 | ] | |
| 513 | ]; | |
| 514 | } | |
| 515 | if (isset($thinnedProcess->officeName)) { | |
| 516 | $scope->contact = new Contact(); | |
| 517 | $scope->contact->name = $thinnedProcess->officeName; | |
| 518 | } | |
| 519 | if (isset($thinnedProcess->officeId)) { | |
| 520 | $scope->provider = new Provider(); | |
| 521 | $scope->provider->id = $thinnedProcess->officeId; | |
| 522 | if (isset($thinnedProcess->scope->provider)) { | |
| 523 | $provider = $thinnedProcess->scope->provider; | |
| 524 | $scope->provider->name = $provider->name ?? null; | |
| 525 | $scope->provider->displayName = $provider->displayName ?? null; | |
| 526 | ||
| 527 | if (isset($provider->contact)) { | |
| 528 | $scope->provider->contact = new Contact(); | |
| 529 | $scope->provider->contact->street = $provider->contact->street ?? null; | |
| 530 | $scope->provider->contact->streetNumber = $provider->contact->streetNumber ?? null; | |
| 531 | } | |
| 532 | if ($provider->lat !== null || $provider->lon !== null) { | |
| 533 | $scope->provider->data = [ | |
| 534 | 'geo' => [ | |
| 535 | 'lat' => $provider->lat, | |
| 536 | 'lon' => $provider->lon, | |
| 537 | ], | |
| 538 | ]; | |
| 539 | } | |
| 540 | } | |
| 541 | ||
| 542 | $scope->provider->source = $thinnedProcess->scope->provider->source ?? null; | |
| 543 | } | |
| 544 | ||
| 545 | return $scope; | |
| 546 | } | |
| 547 | ||
| 548 | private static function createRequests(ThinnedProcess $thinnedProcess): array | |
| 549 | { | |
| 550 | $providerSource = $thinnedProcess->scope->provider->source ?? 'dldb'; | |
| 551 | ||
| 552 | $requests = []; | |
| 553 | $mainServiceId = $thinnedProcess->serviceId ?? null; | |
| 554 | $mainServiceName = $thinnedProcess->serviceName ?? null; | |
| 555 | $mainServiceCount = $thinnedProcess->serviceCount ?? 0; | |
| 556 | ||
| 557 | for ($i = 0; $i < $mainServiceCount; $i++) { | |
| 558 | $request = new Request(); | |
| 559 | $request->id = $mainServiceId; | |
| 560 | $request->name = $mainServiceName; | |
| 561 | $request->source = $providerSource; | |
| 562 | $requests[] = $request; | |
| 563 | } | |
| 564 | ||
| 565 | foreach ($thinnedProcess->subRequestCounts ?? [] as $subRequest) { | |
| 566 | $subId = $subRequest['id'] ?? null; | |
| 567 | $subName = $subRequest['name'] ?? null; | |
| 568 | $count = (int)($subRequest['count'] ?? 0); | |
| 569 | ||
| 570 | for ($i = 0; $i < $count; $i++) { | |
| 571 | $request = new Request(); | |
| 572 | $request->id = $subId; | |
| 573 | $request->name = $subName; | |
| 574 | $request->source = $providerSource; | |
| 575 | $requests[] = $request; | |
| 576 | } | |
| 577 | } | |
| 578 | ||
| 579 | return $requests; | |
| 580 | } | |
| 581 | ||
| 582 | public static function contactToThinnedContact($contact): ThinnedContact | |
| 583 | { | |
| 584 | if (is_array($contact)) { | |
| 585 | return new ThinnedContact( | |
| 586 | city: $contact['city'] ?? null, | |
| 587 | country: $contact['country'] ?? null, | |
| 588 | name: $contact['name'] ?? null, | |
| 589 | postalCode: isset($contact['postalCode']) ? (is_null($contact['postalCode']) ? null : (string)$contact['postalCode']) : null, | |
| 590 | region: $contact['region'] ?? null, | |
| 591 | street: $contact['street'] ?? null, | |
| 592 | streetNumber: $contact['streetNumber'] ?? null | |
| 593 | ); | |
| 594 | } | |
| 595 | ||
| 596 | return new ThinnedContact( | |
| 597 | city: $contact->city ?? null, | |
| 598 | country: $contact->country ?? null, | |
| 599 | name: $contact->name ?? null, | |
| 600 | postalCode: isset($contact->postalCode) ? (is_null($contact->postalCode) ? null : (string)$contact->postalCode) : null, | |
| 601 | region: $contact->region ?? null, | |
| 602 | street: $contact->street ?? null, | |
| 603 | streetNumber: $contact->streetNumber ?? null | |
| 604 | ); | |
| 605 | } | |
| 606 | ||
| 607 | public static function providerToThinnedProvider(Provider $provider): ThinnedProvider | |
| 608 | { | |
| 609 | $geoLat = $provider->data['geo']['lat'] ?? null; | |
| 610 | $geoLon = $provider->data['geo']['lon'] ?? null; | |
| 611 | $contactLat = self::contactCoordinate($provider->contact ?? null, 'lat'); | |
| 612 | $contactLon = self::contactCoordinate($provider->contact ?? null, 'lon'); | |
| 613 | ||
| 614 | return new ThinnedProvider( | |
| 615 | id: isset($provider->id) ? (int) $provider->id : null, | |
| 616 | name: isset($provider->name) ? $provider->name : null, | |
| 617 | displayName: isset($provider->displayName) ? $provider->displayName : null, | |
| 618 | source: isset($provider->source) ? $provider->source : null, | |
| 619 | lon: isset($geoLon) ? (float) $geoLon : (isset($contactLon) ? (float) $contactLon : null), | |
| 620 | lat: isset($geoLat) ? (float) $geoLat : (isset($contactLat) ? (float) $contactLat : null), | |
| 621 | contact: isset($provider->contact) ? self::contactToThinnedContact($provider->contact) : null | |
| 622 | ); | |
| 623 | } | |
| 624 | ||
| 625 | /** | |
| 626 | * Read lat/lon from contact whether it is stored as array or object (see contact.json). | |
| 627 | */ | |
| 628 | private static function contactCoordinate(mixed $contact, string $key): mixed | |
| 629 | { | |
| 630 | if ($contact === null) { | |
| 631 | return null; | |
| 632 | } | |
| 633 | if (is_array($contact)) { | |
| 634 | return $contact[$key] ?? null; | |
| 635 | } | |
| 636 | return $contact->$key ?? null; | |
| 637 | } | |
| 638 | ||
| 639 | private static function generateIcsContent(Process $process): ?string | |
| 640 | { | |
| 641 | if (!isset($process->appointments[0]) || !$process->appointments[0]->hasTime()) { | |
| 642 | return null; | |
| 643 | } | |
| 644 | ||
| 645 | $status = $process->getStatus(); | |
| 646 | $queueStatus = $process->toProperty()->queue->status->get(); | |
| 647 | if ( | |
| 648 | in_array($status, [Process::STATUS_DELETED, Process::STATUS_BLOCKED], true) | |
| 649 | || in_array($queueStatus, [Process::STATUS_DELETED, Process::STATUS_BLOCKED], true) | |
| 650 | ) { | |
| 651 | return null; | |
| 652 | } | |
| 653 | ||
| 654 | $content = ZmsApiClientService::getIcsContent((int) $process->getId(), (string) $process->getAuthKey()); | |
| 655 | return $content ?: null; | |
| 656 | } | |
| 657 | } |