Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.71% covered (warning)
84.71%
288 / 340
67.11% covered (warning)
67.11%
51 / 76
CRAP
0.00% covered (danger)
0.00%
0 / 1
Process
84.71% covered (warning)
84.71%
288 / 340
67.11% covered (warning)
67.11%
51 / 76
255.89
0.00% covered (danger)
0.00%
0 / 1
 getDefaults
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
1
 createFromScope
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 getRequests
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getRequestIds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDisplayNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRequestCSV
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addScope
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addQueue
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 addRequests
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 updateRequests
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 hasScopeAdmin
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sendAdminMailOnConfirmation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sendAdminMailOnDeleted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sendAdminMailOnUpdated
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 shouldSendAdminMailOnClerkMail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 withUpdatedData
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 addAppointmentFromRequest
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
4
 addClientFromForm
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 setStatus
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getStatus
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getReminderTimestamp
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 addReminderTimestamp
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getAppointments
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getClients
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 hasAppointment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isWithAppointment
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 hasProcessCredentials
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
4
 withReassignedCredentials
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 hasQueueNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 getQueueNumber
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addAppointment
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getScopeId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDereferenced
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getCurrentScope
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAmendment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getShowUpTime
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getWaitingTime
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getProcessingTime
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFinishTime
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addAmendment
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getCustomTextfield
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addCustomTextfield
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getCustomTextfield2
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addCustomTextfield2
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 addPriority
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 getAuthKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPriority
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setRandomAuthKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setCallTime
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getCallTimeString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCallTime
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getFirstClient
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getFirstAppointment
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 setStatusBySettings
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
4.05
 setClientsCount
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 withoutPersonalData
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 withLessData
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
21
 toCalendar
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 toQueue
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
6.02
 hasArrivalTime
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getArrivalTime
72.73% covered (warning)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
4.32
 setArrivalTime
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getWaitedSeconds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getWaitedMinutes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getWaySeconds
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWayMinutes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setWasMissed
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getWasMissed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toDerefencedAmendment
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 toDerefencedCustomTextfield
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 toDerefencedCustomTextfield2
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 __toString
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
 getExternalUserId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setExternalUserId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getParkedBy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setParkedBy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace BO\Zmsentities;
4
5use BO\Zmsentities\Helper\ProcessPlainText;
6use BO\Zmsentities\Helper\Property;
7
8/**
9 * @SuppressWarnings(Complexity)
10 * @SuppressWarnings(Coupling)
11 * @SuppressWarnings(Public)
12 * @SuppressWarnings(TooManyMethods)
13 *
14 * Schema-backed entity (ArrayObject::ARRAY_AS_PROPS); document dynamic keys for Psalm.
15 *
16 * @property int|string $id
17 * @property string $authKey
18 * @property int $createTimestamp
19 * @property Scope|array $scope
20 * @property string $status
21 * @property string $displayNumber
22 * @property string|null $showUpTime
23 * @property string|null $parkedBy
24 */
25class Process extends Schema\Entity
26{
27    public const string PRIMARY = 'id';
28    public const string STATUS_FREE = 'free';
29    public const string STATUS_RESERVED = 'reserved';
30    public const string STATUS_CONFIRMED = 'confirmed';
31    public const string STATUS_PRECONFIRMED = 'preconfirmed';
32    public const string STATUS_QUEUED = 'queued';
33    public const string STATUS_CALLED = 'called';
34    public const string STATUS_PROCESSING = 'processing';
35    public const string STATUS_PENDING = 'pending';
36    public const string STATUS_FINISHED = 'finished';
37    public const string STATUS_MISSED = 'missed';
38    public const string STATUS_PARKED = 'parked';
39    public const string STATUS_ARCHIVED = 'archived';
40    public const string STATUS_DELETED = 'deleted';
41    public const string STATUS_ANONYMIZED = 'anonymized';
42    public const string STATUS_BLOCKED = 'blocked';
43    public const string STATUS_CONFLICT = 'conflict';
44    public static $schema = "process.json";
45    /**
46     * @return (Apiclient|Collection\AppointmentList|Collection\ClientList|Collection\RequestList|Queue|Scope|false|int|null|string)[]
47     *
48     */
49    #[\Override]
50    public function getDefaults()
51    {
52        return [
53            'amendment' => '',
54            'customTextfield' => '',
55            'customTextfield2' => '',
56            'appointments' => new Collection\AppointmentList(),
57            'apiclient' => new Apiclient(),
58            'authKey' => '',
59            'captchaToken' => '',
60            'clients' => new Collection\ClientList(),
61            'createIP' => '',
62            'createTimestamp' => time(),
63            'id' => 0,
64            'archiveId' => 0,
65            'queue' => new Queue(),
66            'reminderTimestamp' => 0,
67            'requests' => new Collection\RequestList(),
68            'scope' => new Scope(),
69            'status' => 'free',
70            'displayNumber' => '',
71            'dbstatus' => 'free',
72            'lastChange' => time(),
73            'wasMissed' => false,
74            'priority' => null,
75            'externalUserId' => null,
76            'parkedBy' => null,
77        ];
78    }
79
80    public static function createFromScope(Scope $scope, \DateTimeInterface $dateTime): static
81    {
82        $appointment = new Appointment();
83        $appointment->addScope($scope->id);
84        $appointment->addSlotCount(0);
85        $appointment->addDate(Helper\DateTime::create($dateTime)->modify('00:00:00')->getTimestamp());
86        $process = new static();
87        $process->scope = $scope;
88        $process->setStatus('queued');
89        $process->addAppointment($appointment);
90        return $process;
91    }
92
93    /**
94     * @return Collection\RequestList
95     *
96     */
97    public function getRequests()
98    {
99        if (!$this->requests instanceof Collection\RequestList) {
100            $requestList = new Collection\RequestList();
101            foreach ($this->requests as $request) {
102                $request = ($request instanceof Request) ? $request : new Request($request);
103                $requestList->addEntity($request);
104            }
105            $this->requests = $requestList;
106        }
107        return $this->requests;
108    }
109
110    public function getRequestIds(): mixed
111    {
112        return $this->getRequests()->getIds();
113    }
114
115    public function getDisplayNumber(): mixed
116    {
117        return $this->displayNumber;
118    }
119
120    public function getRequestCSV(): string
121    {
122        return $this->getRequests()->getIdsCsv();
123    }
124
125    public function addScope(mixed $scopeId): static
126    {
127        $this->scope = new Scope(array('id' => $scopeId));
128        return $this;
129    }
130
131    public function addQueue(mixed $number, \DateTimeInterface $dateTime): static
132    {
133        $this->queue = new Queue(array(
134            'number' => $number,
135            'arrivalTime' => $dateTime->getTimestamp(),
136            'priority' => $this->getPriority()
137        ));
138        return $this;
139    }
140
141    public function addRequests(mixed $source, mixed $requestCSV): static
142    {
143        $requestList = $this->getRequests();
144        foreach (explode(',', $requestCSV) as $id) {
145            if (!$requestList->hasRequests($id)) {
146                $this->requests[] = new Request(array(
147                    'source' => $source,
148                    'id' => $id
149                ));
150            }
151        }
152        return $this;
153    }
154
155    public function updateRequests(mixed $source, int|string $requestCSV = ''): static
156    {
157        $this->requests = new Collection\RequestList();
158        if ($requestCSV) {
159            foreach (explode(',', (string) $requestCSV) as $id) {
160                $this->requests->addEntity(new Request(array(
161                            'source' => $source,
162                            'id' => $id
163                        )));
164            }
165        }
166        return $this;
167    }
168
169    public function hasScopeAdmin(): bool
170    {
171        return ('' != $this->toProperty()->scope->contact->email->get());
172    }
173
174    public function sendAdminMailOnConfirmation(): bool
175    {
176        return (bool) ((int) $this->toProperty()->scope->preferences->client->adminMailOnAppointment->get());
177    }
178
179    public function sendAdminMailOnDeleted(): bool
180    {
181        return (bool) ((int) $this->toProperty()->scope->preferences->client->adminMailOnDeleted->get());
182    }
183
184    public function sendAdminMailOnUpdated(): bool
185    {
186        return (bool) ((int) $this->toProperty()->scope->preferences->client->adminMailOnUpdated->get());
187    }
188
189    public function shouldSendAdminMailOnClerkMail(): bool
190    {
191        return (bool) ((int) $this->toProperty()->scope->preferences->client->adminMailOnMailSent->get());
192    }
193
194    public function withUpdatedData(mixed $requestData, \DateTimeInterface $dateTime, mixed $scope = null, string $notice = ''): static
195    {
196        $this->scope = ($scope) ? $scope : $this->scope;
197        $this->addAppointmentFromRequest($requestData, $dateTime);
198        $requestCsv = isset($requestData['requests']) ? implode(',', $requestData['requests']) : 0;
199        $this->updateRequests($scope->getSource(), $requestCsv);
200        $this->addClientFromForm($requestData);
201        $this->addReminderTimestamp($requestData, $dateTime);
202        $this->addAmendment($requestData, $notice);
203        $this->addCustomTextfield($requestData);
204        $this->addCustomTextfield2($requestData);
205        $this->addPriority($requestData);
206        return $this;
207    }
208
209    public function addAppointmentFromRequest(mixed $requestData, \DateTimeInterface $dateTime): static
210    {
211        $this->appointments = null;
212        if (isset($requestData['selecteddate'])) {
213            $dateTime = new \DateTime($requestData['selecteddate']);
214        }
215        $dateTime = Helper\DateTime::create($dateTime);
216        if (isset($requestData['selectedtime'])) {
217            $time = explode('-', $requestData['selectedtime']);
218            $dateTime = $dateTime->setTime((int) $time[0], (int) $time[1]);
219        }
220
221        $appointment = (new Appointment())
222            ->addDate($dateTime->getTimestamp())
223            ->addScope($this->scope['id']);
224        if (isset($requestData['slotCount'])) {
225            $appointment->addSlotCount($requestData['slotCount']);
226        }
227        $this->addAppointment($appointment);
228        return $this;
229    }
230
231    public function addClientFromForm(mixed $requestData): static
232    {
233        $client = new Client();
234        foreach ($requestData as $key => $value) {
235            if (null !== $value && $client->offsetExists($key)) {
236                $client[$key] = (isset($value['value'])) ? $value['value'] : $value;
237            }
238        }
239        $this->clients = array();
240        $this->clients[] = $client;
241        return $this;
242    }
243
244    public function setStatus(string $status): static
245    {
246        $this->status = $status;
247        return $this;
248    }
249
250    public function getStatus(): mixed
251    {
252        return $this->status;
253    }
254
255    public function getReminderTimestamp(): mixed
256    {
257        $timestamp = $this->toProperty()->reminderTimestamp->get();
258        return ($timestamp) ? $timestamp : 0;
259    }
260
261    public function addReminderTimestamp(mixed $input, \DateTimeInterface $dateTime): static
262    {
263        $this->reminderTimestamp = (
264            Property::__keyExists('headsUpTime', $input) &&
265            $input['headsUpTime'] > 0
266        ) ? $dateTime->getTimestamp() - $input['headsUpTime'] : 0;
267        return $this;
268    }
269
270    /**
271     * @return \BO\Zmsentities\Collection\AppointmentList
272     *
273     */
274    public function getAppointments()
275    {
276        $appointments = $this['appointments'];
277        if (!$appointments instanceof Collection\AppointmentList) {
278            $appointments = new Collection\AppointmentList($appointments);
279            foreach ($appointments as $index => $appointment) {
280                if (!$appointment instanceof Appointment) {
281                    $appointments[$index] = new Appointment($appointment);
282                }
283            }
284            $this['appointments'] = $appointments;
285        }
286        return $appointments;
287    }
288
289    /**
290     * @return \BO\Zmsentities\Collection\ClientList
291     *
292     */
293    public function getClients()
294    {
295        $clients = $this['clients'];
296        if (!$clients instanceof Collection\ClientList) {
297            $clients = new Collection\ClientList($clients);
298            foreach ($clients as $index => $client) {
299                if (!$client instanceof Client) {
300                    $clients[$index] = new Client($client);
301                }
302            }
303            $this['clients'] = $clients;
304        }
305        return $clients;
306    }
307
308    public function hasAppointment(mixed $date, mixed $scopeId): bool
309    {
310        return $this->getAppointments()->hasDateScope($date, $scopeId);
311    }
312
313    /**
314     * check if process is with appointment and not only queued
315     * return Boolean
316     */
317    public function isWithAppointment(): bool
318    {
319        $appointment = $this->getFirstAppointment();
320        if ($appointment->hasTime()) {
321            return true;
322        }
323        return (1 == $this->toProperty()->queue->withAppointment->get());
324    }
325
326    public function hasProcessCredentials(): bool
327    {
328        return (isset($this['id']) && isset($this['authKey']) && $this['id'] && $this['authKey']);
329    }
330
331    public function withReassignedCredentials(mixed $process): static
332    {
333        $this->id = $process->getId();
334        $this->authKey = $process->getAuthKey();
335        return $this;
336    }
337
338    public function hasQueueNumber(): bool
339    {
340        return (isset($this['queue']) && isset($this['queue']['number']) && $this['queue']['number']);
341    }
342
343    public function getQueueNumber(): mixed
344    {
345        return $this['queue']['number'];
346    }
347
348    public function addAppointment(Appointment $newappointment): static
349    {
350        $this->appointments[] = $newappointment;
351        return $this;
352    }
353
354    /**
355     * Reminder: A process might have multiple scopes. Each appointment can
356     * have his own scope. The scope in $this->scope is the current/next scope.
357     * This function returns the original scope ID and ignores internal scope
358     * which are used for processing like to pick up documents
359     *
360     */
361    public function getScopeId(): mixed
362    {
363        //as current scope - see zmsbackend Query/Process EntityMapping
364        return $this->toProperty()->scope->id->get();
365    }
366
367    public function isDereferenced(): bool
368    {
369        if ($this->authKey === 'deref!0') {
370            return true;
371        }
372        $client = $this->getFirstClient();
373        return isset($client->familyName) && $client->familyName === 'dereferenced';
374    }
375
376    public function getCurrentScope(): Scope
377    {
378        return $this->getProperty('scope');
379    }
380
381    public function getAmendment(): mixed
382    {
383        return $this->toProperty()->amendment->get();
384    }
385
386    /*public function getShowUpTime()
387    {
388        return $this->toProperty()->showUpTime->get();
389    }*/
390
391    public function getShowUpTime(string|\DateTimeInterface $default = 'now', mixed $timezone = null): \BO\Zmsentities\Helper\DateTime
392    {
393        $showUpTime = $this->toProperty()->showUpTime->get();
394        $showDateTime = Helper\DateTime::create($default, $timezone);
395        if ($showUpTime) {
396            list($hours, $minutes, $seconds) = explode(':', $showUpTime);
397            $showDateTime = $showDateTime->setTime(intval($hours), intval($minutes), intval($seconds));
398        }
399        return $showDateTime;
400    }
401
402    public function getWaitingTime(): mixed
403    {
404        return $this->toProperty()->queue->waitingTime->get();
405    }
406
407    public function getProcessingTime(): mixed
408    {
409        return $this->toProperty()->processingTime->get();
410    }
411
412    public function getFinishTime(): mixed
413    {
414        return $this->toProperty()->finishTime->get();
415    }
416
417    public function addAmendment(mixed $input, string $notice = ''): static
418    {
419        $this->amendment = $notice;
420        $this->amendment .= (isset($input['amendment']) && $input['amendment']) ? $input['amendment'] : '';
421        $this->amendment = trim(ProcessPlainText::normalize($this->amendment));
422        return $this;
423    }
424
425    public function getCustomTextfield(): mixed
426    {
427        return $this->toProperty()->customTextfield->get();
428    }
429
430    public function addCustomTextfield(mixed $input): static
431    {
432        $this->customTextfield = (
433            isset($input['customTextfield']) && $input['customTextfield']
434        ) ? $input['customTextfield'] : '';
435        $this->customTextfield = trim(ProcessPlainText::normalize($this->customTextfield));
436        return $this;
437    }
438
439    public function getCustomTextfield2(): mixed
440    {
441        return $this->toProperty()->customTextfield2->get();
442    }
443
444    public function addCustomTextfield2(mixed $input): static
445    {
446        $this->customTextfield2 = (
447            isset($input['customTextfield2']) && $input['customTextfield2']
448        ) ? $input['customTextfield2'] : '';
449        $this->customTextfield2 = trim(ProcessPlainText::normalize($this->customTextfield2));
450        return $this;
451    }
452
453    public function addPriority(mixed $input): static
454    {
455        $this->priority = isset($input['priority']) && $input['priority'] ? $input['priority'] : null;
456
457        return $this;
458    }
459
460    public function getAuthKey(): mixed
461    {
462        return $this->toProperty()->authKey->get();
463    }
464
465    public function getPriority(): int
466    {
467        return (int) $this->toProperty()->priority->get();
468    }
469
470    public function setRandomAuthKey(): void
471    {
472        $this->authKey = bin2hex(random_bytes(32));
473    }
474
475    public function setCallTime(mixed $dateTime = null): static
476    {
477        $this->queue['callTime'] = ($dateTime) ? $dateTime->getTimestamp() : 0;
478        return $this;
479    }
480
481    public function getCallTimeString(): mixed
482    {
483        return $this->getCallTime()->format('H:i:s');
484    }
485
486    public function getCallTime(string|\DateTimeInterface $default = 'now', mixed $timezone = null): \BO\Zmsentities\Helper\DateTime
487    {
488        $callTime = $this->toProperty()->queue->callTime->get();
489        $callDateTime = Helper\DateTime::create($default, $timezone);
490        if ($callTime) {
491            $callDateTime = $callDateTime->setTimestamp($callTime);
492        }
493        return $callDateTime;
494    }
495
496    public function getFirstClient(): Client
497    {
498        $client = $this->getClients()->getFirst();
499        if (!$client) {
500            $client = new Client();
501            $this->clients->addEntity($client);
502        }
503        return $client;
504    }
505
506    public function getFirstAppointment(): Appointment
507    {
508        $appointment = $this->getAppointments()->getFirst();
509        if (!$appointment) {
510            $appointment = new Appointment();
511            $appointment->scope = $this->scope;
512            $this->appointments->addEntity($appointment);
513        }
514        return $appointment;
515    }
516
517    public function setStatusBySettings(): static
518    {
519        $scope = new Scope($this->scope);
520        if ('called' == $this->status && $this->queue['callCount'] > $scope->getPreference('queue', 'callCountMax')) {
521            $this->status = 'missed';
522        } elseif ('parked' == $this->status) {
523            $this->status = 'parked';
524        } else {
525            $this->status = 'confirmed';
526        }
527        return $this;
528    }
529
530    public function setClientsCount(mixed $count): static
531    {
532        $clientList = $this->getClients();
533        while ($clientList->count() < $count) {
534            $clientList->addEntity(new Client());
535        }
536        return $this;
537    }
538
539
540    public function withoutPersonalData(): static
541    {
542        $entity = clone $this;
543        if ($this->toProperty()->clients->isAvailable()) {
544            $client = $entity->getFirstClient();
545            unset($client['familyName']);
546            unset($client['email']);
547        }
548        return $entity;
549    }
550
551    /**
552     * Reduce data of dereferenced entities to a required minimum
553     *
554     * @return static
555     */
556    #[\Override]
557    public function withLessData(array $keepArray = [])
558    {
559        $entity = clone $this;
560        if (!in_array('availability', $keepArray)) {
561            foreach ($entity['appointments'] as $appointment) {
562                if ($appointment->toProperty()->scope->isAvailable()) {
563                    $scopeId = $appointment['scope']['id'];
564                    $appointment['scope'] = [
565                        'id' => $scopeId,
566                        'provider' => $appointment['scope']['provider'] ?? [],
567                        'shortName' => $appointment['scope']['shortName'] ?? ''
568                    ];
569                }
570                if ($appointment->toProperty()->availability->isAvailable()) {
571                    unset($appointment['availability']);
572                }
573            }
574        }
575
576        if (!in_array('createTimestamp', $keepArray)) {
577            unset($entity['createTimestamp']);
578        }
579        unset($entity['createIP']);
580        if ($entity->toProperty()->scope->status->isAvailable()) {
581            unset($entity['scope']['status']);
582        }
583
584        if ($entity->status == 'free') {
585// delete keys
586            foreach (['authKey', 'queue', 'requests',] as $key) {
587                if (!in_array($key, $keepArray) && $entity->toProperty()->$key->isAvailable()) {
588                    unset($entity[$key]);
589                }
590            }
591            // delete if empty
592            foreach (['amendment', 'id', 'authKey', 'archiveId', 'reminderTimestamp',] as $key) {
593                if (!in_array($key, $keepArray) && $entity->toProperty()->$key->isAvailable() && !$entity[$key]) {
594                    unset($entity[$key]);
595                }
596            }
597            if (!in_array('provider', $keepArray) && $entity->toProperty()->scope->provider->data->isAvailable()) {
598                unset($entity['scope']['provider']['data']);
599            }
600        }
601
602        if (!in_array('dayoff', $keepArray) && $entity->toProperty()->scope->dayoff->isAvailable()) {
603            unset($entity['scope']['dayoff']);
604        }
605        if (!in_array('scope', $keepArray) && $entity->toProperty()->scope->preferences->isAvailable()) {
606            unset($entity['scope']['preferences']);
607        }
608        return $entity;
609    }
610
611    public function toCalendar(): Calendar
612    {
613        $calendar = new Calendar();
614        $dateTime = $this->getFirstAppointment()->toDateTime();
615        $day = new Day();
616        $day->setDateTime($dateTime);
617        $calendar->firstDay = $day;
618        $calendar->lastDay = $day;
619        $calendar->requests = clone $this->getRequests();
620        $calendar->scopes = new Collection\ScopeList([$this->scope]);
621        return $calendar;
622    }
623
624    public function toQueue(\DateTimeInterface $dateTime): mixed
625    {
626        $queue = new Queue($this->queue);
627        $queue->withAppointment = ($this->getFirstAppointment()->hasTime()) ? true : false;
628        $queue->waitingTime = ($queue->waitingTime) ? $queue->waitingTime : 0;
629        $queue->wayTime = ($queue->wayTime) ? $queue->wayTime : 0;
630        if ($queue->withAppointment) {
631            $queue->number = !empty($this->getDisplayNumber())
632                ? $this->getDisplayNumber()
633                : $this->id;
634        } else {
635            $queue->number = $this->toProperty()->queue->number->get();
636        }
637        $queue->displayNumber = $this->getDisplayNumber();
638        $queue->arrivalTime = $this->getArrivalTime($dateTime)->getTimestamp();
639        $queue->priority = $this->priority;
640        return $queue->setProcess($this);
641    }
642
643    public function hasArrivalTime(): bool
644    {
645        if ($this->isWithAppointment()) {
646            $arrivalTime = $this->getFirstAppointment()->date;
647        } else {
648            $arrivalTime = $this->toProperty()->queue->arrivalTime->get();
649        }
650        return ($arrivalTime) ? true : false;
651    }
652
653    /**
654     * @param \DateTimeInterface|string $default
655     *
656     */
657    public function getArrivalTime(string|\DateTimeInterface $default = 'now', mixed $timezone = null): \BO\Zmsentities\Helper\DateTime
658    {
659        $queueArrivalTime = $this->toProperty()->queue->arrivalTime->get();
660
661        if ($queueArrivalTime) {
662            // Falls der Queue-Wert vorhanden ist â€“ auch wenn ein Termin existiert â€“ verwende diesen (dabei ist handelt es sich, um verpasste Termine)
663            $arrivalTime = $queueArrivalTime;
664        } elseif ($this->isWithAppointment()) {
665            $arrivalTime = $this->getFirstAppointment()->date;
666        } else {
667            $arrivalTime = 0;
668        }
669
670        $arrivalTime = (int)$arrivalTime;
671        $arrivalDateTime = Helper\DateTime::create($default, $timezone);
672        if ($arrivalTime) {
673            $arrivalDateTime = $arrivalDateTime->setTimestamp($arrivalTime);
674        }
675        return $arrivalDateTime;
676    }
677
678    public function setArrivalTime(?\DateTimeInterface $dateTime = null): static
679    {
680        $this->queue['arrivalTime'] = ($dateTime) ? $dateTime->getTimestamp() : 0;
681        return $this;
682    }
683
684    /**
685     * Calculate real waiting time, only available after called
686     */
687    public function getWaitedSeconds(string|\DateTimeInterface $defaultTime = 'now'): mixed
688    {
689        return $this->getCallTime($defaultTime)->getTimestamp() - $this->getArrivalTime($defaultTime)->getTimestamp();
690    }
691
692    public function getWaitedMinutes(string|\DateTimeInterface $defaultTime = 'now'): mixed
693    {
694        return $this->getWaitedSeconds($defaultTime) / 60;
695    }
696
697    public function getWaySeconds(string|\DateTimeInterface $defaultTime = 'now'): mixed
698    {
699        return $this->getShowUpTime($defaultTime)->getTimestamp() - $this->getCallTime($defaultTime)->getTimestamp();
700    }
701
702    public function getWayMinutes(string|\DateTimeInterface $defaultTime = 'now'): mixed
703    {
704        return $this->getWaySeconds($defaultTime) / 60;
705    }
706
707    public function setWasMissed(bool $bool): static
708    {
709        $this->wasMissed = $bool;
710        $this->status = self::STATUS_MISSED;
711        return $this;
712    }
713
714    public function getWasMissed(): bool
715    {
716        return (bool) $this->wasMissed;
717    }
718
719    public function toDerefencedAmendment(): string|null
720    {
721        $lastChange = (new \DateTimeImmutable())->setTimestamp($this->createTimestamp)->format('c');
722        return var_export(array(
723                'BuergerID' => $this->id,
724                'StandortID' => $this->scope['id'],
725                'Anmerkung' => null,
726                'IPTimeStamp' => $this->createTimestamp,
727                'LastChange' => $lastChange,
728            ), true);
729    }
730
731    public function toDerefencedCustomTextfield(): string|null
732    {
733        $lastChange = (new \DateTimeImmutable())->setTimestamp($this->createTimestamp)->format('c');
734        return var_export(array(
735                'BuergerID' => $this->id,
736                'StandortID' => $this->scope['id'],
737                'CustomTextfield' => null,
738                'IPTimeStamp' => $this->createTimestamp,
739                'LastChange' => $lastChange,
740            ), true);
741    }
742
743    public function toDerefencedCustomTextfield2(): string|null
744    {
745        $lastChange = (new \DateTimeImmutable())->setTimestamp($this->createTimestamp)->format('c');
746        return var_export(array(
747                'BuergerID' => $this->id,
748                'StandortID' => $this->scope['id'],
749                'CustomTextfield2' => null,
750                'IPTimeStamp' => $this->createTimestamp,
751                'LastChange' => $lastChange,
752            ), true);
753    }
754
755    public function __toString()
756    {
757        $string = "process#";
758        $string .= $this->getDisplayNumber() ?: $this->id ?: $this->archiveId;
759        $string .= ":" . $this->authKey;
760        $string .= " (" . $this->status . ")";
761        $string .= " " . $this->getFirstAppointment()->toDateTime()->format('c');
762        $string .= " " . ($this->isWithAppointment() ? "appoint" : "arrival:" . $this->getArrivalTime()->format('c'));
763        $string .= " " . $this->getFirstAppointment()->slotCount . "slots";
764        $string .= "*" . count($this->appointments);
765        foreach ($this->getRequests() as $request) {
766            $string .= " " . $request['source'] . "." . $request['id'];
767        }
768        $string .= " scope." . $this['scope']['id'];
769        $string .= " ~" . base_convert($this['lastChange'], 10, 35);
770        $string .= " client:" . $this['apiclient']['shortname'];
771        $string .= " token:" . ($this['captchaToken'] ?? '(none)');
772        return $string;
773    }
774
775    public function getExternalUserId(): mixed
776    {
777        return $this->toProperty()->externalUserId->get();
778    }
779
780    public function setExternalUserId(mixed $externalUserId): static
781    {
782        $this->externalUserId = $externalUserId;
783        return $this;
784    }
785
786    public function getParkedBy(): mixed
787    {
788        return $this->toProperty()->parkedBy->get();
789    }
790
791    public function setParkedBy(mixed $name): void
792    {
793        $this->parkedBy = $name;
794    }
795}