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