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