Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.26% covered (warning)
87.26%
555 / 636
78.00% covered (warning)
78.00%
39 / 50
CRAP
0.00% covered (danger)
0.00%
0 / 1
Process
87.26% covered (warning)
87.26%
555 / 636
78.00% covered (warning)
78.00%
39 / 50
273.55
0.00% covered (danger)
0.00%
0 / 1
 getQueryNewProcessId
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getLockProcessId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addJoin
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addJoinAvailability
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 addJoinScope
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 calculateStatus
77.78% covered (warning)
77.78%
28 / 36
0.00% covered (danger)
0.00%
0 / 1
71.24
 getEntityMapping
100.00% covered (success)
100.00%
64 / 64
100.00% covered (success)
100.00%
1 / 1
1
 addCountValue
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addConditionHasTelephone
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 addConditionProcessDeleteInterval
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 addConditionProcessExpiredIPTimeStamp
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 addConditionProcessReminderInterval
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 addConditionProcessMailReminder
100.00% covered (success)
100.00%
44 / 44
100.00% covered (success)
100.00%
1 / 1
1
 addConditionProcessId
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 addConditionProcessIdFollow
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 addConditionIgnoreSlots
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 addConditionScopeId
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 addConditionQueueNumber
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 addConditionWorkstationId
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 addConditionTime
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addConditionTimeframe
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 addConditionAuthKey
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 addConditionAssigned
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addConditionStatus
97.18% covered (success)
97.18%
69 / 71
0.00% covered (danger)
0.00%
0 / 1
16
 addConditionIsReserved
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 addConditionSearch
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 addConditionName
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 addConditionMail
62.50% covered (warning)
62.50%
5 / 8
0.00% covered (danger)
0.00%
0 / 1
2.21
 addConditionCustomTextfield
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 addConditionCustomTextfield2
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 addConditionAmendment
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addConditionRequestId
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 addConditionDeallocate
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 addValuesNewProcess
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 addValuesUpdateProcess
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 addValuesIPAdress
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 addValuesFollowingProcessData
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 addValuesAppointmentData
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 addValuesScopeData
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 addValues
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 addValuesStatusData
87.50% covered (warning)
87.50%
28 / 32
0.00% covered (danger)
0.00%
0 / 1
14.38
 addValuesClientData
91.30% covered (success)
91.30%
21 / 23
0.00% covered (danger)
0.00%
0 / 1
17.19
 addProcessingTimeData
30.43% covered (danger)
30.43%
14 / 46
0.00% covered (danger)
0.00%
0 / 1
79.98
 addValuesQueueData
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
11
 addValuesWaitingTimeData
62.50% covered (warning)
62.50%
10 / 16
0.00% covered (danger)
0.00%
0 / 1
17.38
 addValuesWayTimeData
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
3.58
 addValuesWasMissed
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 postProcess
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
7
 removeDuplicates
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addRequiredJoins
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace BO\Zmsdb\Query;
4
5/**
6 * @SuppressWarnings(Methods)
7 * @SuppressWarnings(Complexity)
8 */
9class Process extends Base implements MappingInterface
10{
11    /**
12     *     * @var String TABLE mysql table reference
13     */
14    const TABLE = 'buerger';
15
16    const QUERY_DEREFERENCED = "UPDATE `buerger` process LEFT JOIN `standort` s USING(StandortID)
17        SET
18            process.Anmerkung = ?,
19            process.custom_text_field = ?,
20            process.custom_text_field2 = ?,
21            process.StandortID = 0,
22            process.AbholortID = 0,
23            process.Abholer = 0,
24            process.Name = 'dereferenced',
25            process.IPadresse = '',
26            process.IPTimeStamp = 0,
27            process.NutzerID = 0,
28            process.vorlaeufigeBuchung = 0,
29            process.bestaetigt = 1,
30            process.absagecode = 'deref!0',
31            process.EMail = '',
32            process.NutzerID = 0
33        WHERE
34            (process.BuergerID = ? AND process.absagecode = ?)
35            OR process.istFolgeterminvon = ?
36        ";
37
38    const QUERY_CANCELED = "
39        UPDATE `buerger` process LEFT JOIN `standort` s USING(StandortID)
40            SET
41                process.Anmerkung = CONCAT(
42                    'Abgesagter Termin gebucht am: ',
43                    FROM_UNIXTIME(process.IPTimeStamp,'%d.%m.%Y, %H:%i'),' Uhr | ',
44                    IFNULL(process.Anmerkung,'')
45                ),              
46                process.Name = '(abgesagt)',
47                process.IPadresse = '',
48                process.IPTimeStamp = :canceledTimestamp + (IFNULL(s.loeschdauer, 15) * 60),
49                process.NutzerID = 0,
50                process.vorlaeufigeBuchung = 1,
51                process.absagecode = RIGHT(MD5(CONCAT(process.absagecode, 'QUERY_CANCELED')), 4)
52            WHERE
53                (process.BuergerID = :processId AND process.absagecode = :authKey)
54                OR process.istFolgeterminvon = :processId
55        ";
56
57    const QUERY_DELETE = "DELETE FROM `buerger`
58        WHERE
59            BuergerID = ?
60            OR istFolgeterminvon = ?
61        ";
62
63    const QUERY_REASSIGN_PROCESS_CREDENTIALS = "UPDATE `buerger` process
64       SET 
65            process.BuergerID = :newProcessId, 
66            process.absagecode = :newAuthKey
67        WHERE BuergerID = :processId
68    ";
69
70    const QUERY_REASSIGN_PROCESS_REQUESTS = "UPDATE `buergeranliegen` requests
71        SET 
72            requests.BuergerID = :newProcessId
73        WHERE BuergerID = :processId
74    ";
75
76    const QUERY_REASSIGN_FOLLWING_PROCESS = "UPDATE `buerger` process
77        SET process.istFolgeterminvon = :newProcessId
78        WHERE istFolgeterminvon = :processId
79    ";
80
81    const QUERY_UPDATE_FOLLOWING_PROCESS = "UPDATE buerger 
82        SET vorlaeufigeBuchung = :reserved 
83        WHERE istFolgeterminvon = :processID
84        ";
85
86    public function getQueryNewProcessId()
87    {
88        $random = rand(20, 999);
89        return 'SELECT pseq.processId AS `nextid`
90            FROM process_sequence pseq
91            WHERE pseq.processId = (
92                SELECT ps.processID FROM `process_sequence` ps LEFT JOIN `' . self::getTablename() . '` p
93                    ON ps.processId = p.BuergerID
94                WHERE p.`BuergerID` IS NULL
95                LIMIT ' . $random . ',1)
96            FOR UPDATE';
97    }
98
99    public function getLockProcessId()
100    {
101        return 'SELECT p.`BuergerID` FROM `' . self::getTablename() . '` p WHERE p.`BuergerID` = :processId FOR UPDATE';
102    }
103
104    public function addJoin()
105    {
106        return [
107            $this->addJoinAvailability(),
108            $this->addJoinScope(),
109        ];
110    }
111
112    /**
113     * Add Availability to the dataset
114     */
115    protected function addJoinAvailability()
116    {
117        $this->leftJoin(
118            new Alias(Availability::TABLE, 'availability'),
119            Availability::getJoinExpression('`process`', '`availability`')
120        );
121        $joinQuery = new Availability($this, $this->getPrefixed('appointments__0__availability__'));
122        return $joinQuery;
123    }
124
125    /**
126     * Add Scope to the dataset
127     */
128    protected function addJoinScope()
129    {
130        $this->leftJoin(
131            new Alias(Scope::TABLE, 'scope'),
132            self::expression(
133                'IF(`process`.`AbholortID`,
134                    `process`.`AbholortID`,
135                    `process`.`StandortID`
136                )'
137            ),
138            '=',
139            'scope.StandortID'
140        );
141        $joinQuery = new Scope($this, $this->getPrefixed('scope__'));
142        return $joinQuery;
143    }
144
145    protected function calculateStatus($data)
146    {
147        if (isset($data['Name']) && $data['Name'] === '(abgesagt)') {
148            return 'deleted';
149        }
150
151        if (
152            isset($data['StandortID']) && $data['StandortID'] == 0
153            && isset($data['AbholortID']) && $data['AbholortID'] == 0
154        ) {
155            return 'blocked';
156        }
157
158        if (
159            isset($data['vorlaeufigeBuchung']) && $data['vorlaeufigeBuchung'] == 1
160            && isset($data['bestaetigt']) && $data['bestaetigt'] == 0
161        ) {
162            return 'reserved';
163        }
164
165        if (isset($data['nicht_erschienen']) && $data['nicht_erschienen'] != 0) {
166            return 'missed';
167        }
168
169        if (isset($data['parked']) && $data['parked'] != 0) {
170            return 'parked';
171        }
172
173        if (
174            isset($data['Abholer']) && $data['Abholer'] != 0
175            && isset($data['AbholortID']) && $data['AbholortID'] != 0
176            && isset($data['NutzerID']) && $data['NutzerID'] == 0
177        ) {
178            return 'pending';
179        }
180
181        if (
182            isset($data['AbholortID']) && $data['AbholortID'] != 0
183            && isset($data['NutzerID']) && $data['NutzerID'] != 0
184        ) {
185            return 'pickup';
186        }
187
188        if (
189            isset($data['AbholortID']) && $data['AbholortID'] == 0
190            && isset($data['aufruferfolgreich']) && $data['aufruferfolgreich'] != 0
191            && isset($data['NutzerID']) && $data['NutzerID'] != 0
192        ) {
193            return 'processing';
194        }
195
196        if (
197            isset($data['aufrufzeit']) && $data['aufrufzeit'] != "00:00:00"
198            && isset($data['NutzerID']) && $data['NutzerID'] != 0
199            && isset($data['AbholortID']) && $data['AbholortID'] == 0
200        ) {
201            return 'called';
202        }
203
204        if (isset($data['Uhrzeit']) && $data['Uhrzeit'] == "00:00:00") {
205            return 'queued';
206        }
207
208        if (
209            isset($data['vorlaeufigeBuchung']) && $data['vorlaeufigeBuchung'] == 0
210            && isset($data['bestaetigt']) && $data['bestaetigt'] == 0
211        ) {
212            return 'preconfirmed';
213        }
214
215        if (
216            isset($data['vorlaeufigeBuchung']) && $data['vorlaeufigeBuchung'] == 0
217            && isset($data['bestaetigt']) && $data['bestaetigt'] == 1
218        ) {
219            return 'confirmed';
220        }
221
222        return null;
223    }
224
225    public function getEntityMapping()
226    {
227        $status_expression = self::expression(
228            'CASE
229                WHEN process.Name = "(abgesagt)"
230                    THEN "deleted"
231                WHEN process.StandortID = 0 AND process.AbholortID = 0
232                    THEN "blocked"
233                WHEN process.vorlaeufigeBuchung = 1 AND process.bestaetigt = 0 
234                    THEN "reserved"
235                WHEN process.nicht_erschienen != 0
236                    THEN "missed"
237                WHEN process.parked != 0
238                    THEN "parked"
239                WHEN process.Abholer != 0 AND process.AbholortID != 0 AND process.NutzerID = 0
240                    THEN "pending"
241                WHEN process.AbholortID != 0 AND process.NutzerID != 0
242                    THEN "pickup"
243                WHEN process.AbholortID = 0 AND process.aufruferfolgreich != 0 AND process.NutzerID != 0
244                    THEN "processing"
245                WHEN process.aufrufzeit != "00:00:00" AND process.NutzerID != 0 AND process.AbholortID = 0
246                    THEN "called"
247                WHEN process.Uhrzeit = "00:00:00"
248                    THEN "queued"
249                WHEN process.vorlaeufigeBuchung = 0 AND process.bestaetigt = 0 
250                    THEN "preconfirmed"
251                WHEN process.vorlaeufigeBuchung = 0 AND process.bestaetigt = 1
252                    THEN "confirmed"
253                ELSE "free"
254            END'
255        );
256        return [
257            'amendment' => 'process.Anmerkung',
258            'id' => 'process.BuergerID',
259            'appointments__0__date' => self::expression(
260                'CONCAT(`process`.`Datum`, " ", `process`.`Uhrzeit`)'
261            ),
262            'scope__id' => self::expression(
263                'IF(`process`.`AbholortID`,
264                    `process`.`AbholortID`,
265                    `process`.`StandortID`
266)'
267            ),
268            'appointments__0__scope__id' => 'process.StandortID',
269            // 'appointments__0__slotCount' => 'process.hatFolgetermine',
270            'appointments__0__slotCount' => self::expression('process.hatFolgetermine + 1'),
271            'authKey' => 'process.absagecode',
272            'clients__0__email' => 'process.EMail',
273            'clients__0__emailSendCount' => 'process.EMailverschickt',
274            'clients__0__familyName' => 'process.Name',
275            'clients__0__notificationsSendCount' => 'process.SMSverschickt',
276            'clients__0__surveyAccepted' => 'process.zustimmung_kundenbefragung',
277            'clients__0__telephone' => self::expression(
278                'IF(`process`.`telefonnummer_fuer_rueckfragen`!="",
279                    `process`.`telefonnummer_fuer_rueckfragen`,
280                    `process`.`Telefonnummer`
281                )'
282            ),
283            'customTextfield' => 'process.custom_text_field',
284            'customTextfield2' => 'process.custom_text_field2',
285            'createIP' => 'process.IPAdresse',
286            'createTimestamp' => 'process.IPTimeStamp',
287            'lastChange' => 'process.updateTimestamp',
288            'showUpTime' => 'process.showUpTime',
289            'processingTime' => 'process.processingTime',
290            'timeoutTime' => 'process.timeoutTime',
291            'finishTime' => 'process.finishTime',
292            'dbstatus' => 'process.status',
293            'status' => $status_expression,
294            'queue__status' => $status_expression,
295            'queue__arrivalTime' => self::expression(
296                'CONCAT(
297                    `process`.`Datum`,
298                    " ",
299                    IF(`process`.`wsm_aufnahmezeit`, `process`.`wsm_aufnahmezeit`, `process`.`Uhrzeit`)
300                )'
301            ),
302            'queue__callCount' => 'process.AnzahlAufrufe',
303            'queue__callTime' => 'process.aufrufzeit',
304            'queue__lastCallTime' => 'process.Timestamp',
305            'queue__number' => self::expression(
306                'IF(`process`.`wartenummer`,
307                    `process`.`wartenummer`,
308                    `process`.`BuergerID`
309                )'
310            ),
311            'queue__destination' => self::expression(
312                'IF(`process`.`AbholortID`,
313                    `processscope`.`ausgabeschaltername`,
314                    `processuser`.`Arbeitsplatznr`
315)'
316            ),
317            'queue__destinationHint' => 'processuser.aufrufzusatz',
318            'queue__waitingTime' => 'process.wartezeit',
319            'queue__wayTime' => 'process.wegezeit',
320            'queue__withAppointment' => self::expression(
321                'IF(`process`.`wartenummer`,
322                    "0",
323                    "1"
324                )'
325            ),
326            'reminderTimestamp' => 'process.Erinnerungszeitpunkt',
327            '__clientsCount' => 'process.AnzahlPersonen',
328            'wasMissed' => 'process.wasMissed'
329        ];
330    }
331
332    public function addCountValue()
333    {
334        $this->query->select([
335            'processCount' => self::expression('COUNT(*)'),
336        ]);
337        return $this;
338    }
339
340    public function addConditionHasTelephone()
341    {
342        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) {
343            $condition
344                ->andWith('process.telefonnummer_fuer_rueckfragen', '!=', '')
345                ->orWith('process.Telefonnummer', '!=', '');
346        });
347        return $this;
348    }
349
350    public function addConditionProcessDeleteInterval(\DateTimeInterface $expirationDate)
351    {
352        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($expirationDate) {
353            $query->andWith(
354                self::expression(
355                    'CONCAT(`process`.`Datum`, " ", `process`.`Uhrzeit`)'
356                ),
357                '<=',
358                $expirationDate->format('Y-m-d H:i:s')
359            );
360        });
361        $this->query->orderBy('appointments__0__date', 'ASC');
362        return $this;
363    }
364
365    public function addConditionProcessExpiredIPTimeStamp(\DateTimeInterface $expirationDate)
366    {
367        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($expirationDate) {
368            $query->andWith('process.IPTimeStamp', '<=', $expirationDate->getTimestamp());
369        });
370        $this->query->orderBy('appointments__0__date', 'ASC');
371        return $this;
372    }
373
374    public function addConditionProcessReminderInterval(\DateTimeInterface $dateTime)
375    {
376        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($dateTime) {
377            $query
378                ->andWith('process.Erinnerungszeitpunkt', '<=', $dateTime->getTimestamp())
379                ->andWith('process.Erinnerungszeitpunkt', '>=', $dateTime->modify("-5 Minutes")->getTimestamp());
380        });
381        $this->query->orderBy('reminderTimestamp', 'ASC');
382        return $this;
383    }
384
385    public function addConditionProcessMailReminder(
386        \DateTimeInterface $now,
387        \DateTimeInterface $lastRun,
388        $defaultReminderInMinutes
389    ) {
390        $this->query
391            ->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($now, $lastRun, $defaultReminderInMinutes) {
392                $query
393                    ->andWith(
394                        self::expression(
395                            'CONCAT(`process`.`Datum`, " ", `process`.`Uhrzeit`)'
396                        ),
397                        '>',
398                        $lastRun->format('Y-m-d H:i:s')
399                    )
400                    ->andWith(
401                        self::expression(
402                            'CONCAT(`process`.`Datum`, " ", `process`.`Uhrzeit`)'
403                        ),
404                        '>',
405                        $now->format('Y-m-d H:i:s')
406                    )
407                    ->andWith(
408                        'scopemail.send_reminder',
409                        '=',
410                        1
411                    )
412                    ->andWith(
413                        'process.EMail',
414                        '<>',
415                        ""
416                    )
417                    ->andWith(
418                        'process.EMailverschickt',
419                        '=',
420                        0
421                    )
422                    ->andWith(
423                        self::expression(
424                            'DATE_SUB(CONCAT(`process`.`Datum`, " ", `process`.`Uhrzeit`), INTERVAL '
425                                . 'IFNULL(scopemail.send_reminder_minutes_before, ' . $defaultReminderInMinutes
426                                . ') MINUTE)'
427                        ),
428                        '<=',
429                        $now->format('Y-m-d H:i:s')
430                    );
431            });
432        $this->query->orderBy('appointments__0__date', 'ASC');
433        return $this;
434    }
435
436    public function addConditionProcessId($processId)
437    {
438        $this->query->where('process.BuergerID', '=', $processId);
439        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) {
440            $condition
441                ->andWith('process.istFolgeterminvon', 'IS', null)
442                ->orWith('process.istFolgeterminvon', '=', 0);
443        });
444        return $this;
445    }
446
447    public function addConditionProcessIdFollow($processId)
448    {
449        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) use ($processId) {
450            $condition
451                ->andWith('process.BuergerID', '=', $processId)
452                ->orWith('process.istFolgeterminvon', '=', $processId);
453        });
454        return $this;
455    }
456
457    public function addConditionIgnoreSlots()
458    {
459        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) {
460            $condition
461                ->andWith('process.istFolgeterminvon', 'IS', null)
462                ->orWith('process.istFolgeterminvon', '=', 0);
463        });
464        return $this;
465    }
466
467    public function addConditionScopeId($scopeId)
468    {
469        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($scopeId) {
470            $query
471                ->andWith('process.StandortID', '=', $scopeId)
472                ->orWith('process.AbholortID', '=', $scopeId);
473        });
474        return $this;
475    }
476
477    public function addConditionQueueNumber($queueNumber, $queueLimit = 10000)
478    {
479        ($queueLimit > $queueNumber)
480            ? $this->query->where('process.wartenummer', '=', $queueNumber)
481            : $this->query->where('process.BuergerID', '=', $queueNumber);
482        return $this;
483    }
484
485    public function addConditionWorkstationId($workstationId)
486    {
487        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($workstationId) {
488            $query->andWith('process.NutzerID', '=', $workstationId);
489            $query->andWith('process.StandortID', '>', 0);
490        });
491        return $this;
492    }
493
494    public function addConditionTime($dateTime)
495    {
496        $this->query->where('process.Datum', '=', $dateTime->format('Y-m-d'));
497        return $this;
498    }
499
500    /**
501     * Identify processes between two dates
502     */
503    public function addConditionTimeframe(\DateTimeInterface $startDate, \DateTimeInterface $endDate)
504    {
505        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) use ($startDate, $endDate) {
506            $condition
507                ->andWith('process.Datum', '<=', $endDate->format('Y-m-d'))
508                ->andWith('process.Datum', '>=', $startDate->format('Y-m-d'));
509        });
510        return $this;
511    }
512
513    public function addConditionAuthKey($authKey)
514    {
515        $authKey = urldecode($authKey);
516        $this->query
517            ->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) use ($authKey) {
518                $condition
519                    ->andWith('process.absagecode', '=', $authKey)
520                    ->orWith('process.Name', '=', $authKey);
521            });
522        return $this;
523    }
524
525    public function addConditionAssigned()
526    {
527        $this->query->where('process.StandortID', '!=', "0");
528        return $this;
529    }
530
531    public function addConditionStatus($status, $scopeId = 0)
532    {
533        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($status, $scopeId) {
534            if ('deleted' == $status) {
535                $query
536                    ->andWith('process.Name', '=', '(abgesagt)');
537            }
538            if ('blocked' == $status) {
539                $query
540                    ->andWith('process.StandortID', '=', 0)
541                    ->andWith('process.AbholortID', '=', 0);
542            }
543            if ('reserved' == $status) {
544                $query
545                    ->andWith('process.name', '!=', '(abgesagt)')
546                    ->andWith('process.vorlaeufigeBuchung', '=', 1)
547                    ->andWith('process.StandortID', '!=', 0)
548                    ->andWith('process.istFolgeterminvon', 'is', null);
549            }
550            if ('missed' == $status) {
551                $query->andWith('process.nicht_erschienen', '!=', 0)
552                    ->andWith('process.StandortID', '!=', 0);
553            }
554            if ('parked' == $status) {
555                $query
556                    ->andWith('process.parked', '!=', 0)
557                    ->andWith('process.StandortID', '!=', 0);
558            }
559            if ('pending' == $status) {
560                $query
561                    ->andWith('process.StandortID', '!=', 0)
562                    ->andWith('process.Abholer', '!=', 0)
563                    ->andWith('process.NutzerID', '=', 0);
564                if (0 != $scopeId) {
565                    $query->andWith('process.AbholortID', '=', $scopeId);
566                } else {
567                    $query->andWith('process.AbholortID', '!=', 0);
568                }
569            }
570            if ('processing' == $status) {
571                $query
572                    ->andWith('process.aufruferfolgreich', '!=', 0)
573                    ->andWith('process.NutzerID', '!=', 0)
574                    ->andWith('process.StandortID', '!=', 0);
575            }
576            if ('pickup' == $status) {
577                $query
578                    ->andWith('process.StandortID', '!=', 0)
579                    ->andWith('process.NutzerID', '!=', 0);
580                if (0 != $scopeId) {
581                    $query->andWith('process.AbholortID', '=', $scopeId);
582                } else {
583                    $query->andWith('process.AbholortID', '!=', 0);
584                }
585            }
586            if ('called' == $status) {
587                $query
588                    ->andWith('process.aufrufzeit', '!=', '00:00:00')
589                    ->andWith('process.NutzerID', '!=', 0)
590                    ->andWith('process.StandortID', '!=', 0)
591                    ->andWith('process.AbholortID', '=', 0);
592            }
593            if ('queued' == $status) {
594                $query->andWith('process.Uhrzeit', '=', '00:00:00')
595                    ->andWith('process.StandortID', '!=', 0)
596                    ->andWith('process.AbholortID', '=', 0);
597                ;
598            }
599            if ('confirmed' == $status) {
600                $query
601                    ->andWith('process.vorlaeufigeBuchung', '=', 0)
602                    ->andWith('process.Abholer', '=', 0)
603                    ->andWith('process.Uhrzeit', '!=', '00:00:00')
604                    ->andWith('process.bestaetigt', '=', 1)
605                    ->andWith('process.IPTimeStamp', '!=', 0);
606            }
607            if ('preconfirmed' == $status) {
608                $query
609                    ->andWith('process.vorlaeufigeBuchung', '=', 0)
610                    ->andWith('process.Abholer', '=', 0)
611                    ->andWith('process.StandortID', '!=', 0)
612                    ->andWith('process.Uhrzeit', '!=', '00:00:00')
613                    ->andWith('process.bestaetigt', '=', 0)
614                    ->andWith('process.IPTimeStamp', '!=', 0);
615                if (0 != $scopeId) {
616                    $query
617                        ->andWith('process.StandortID', '=', $scopeId);
618                }
619            }
620        });
621        return $this;
622    }
623
624    public function addConditionIsReserved()
625    {
626        $this->query->where('process.name', 'NOT IN', array(
627            'dereferenced',
628            '(abgesagt)'
629        ))
630            ->where('process.vorlaeufigeBuchung', '=', 1)
631            ->where('process.StandortID', '>', 0);
632        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $condition) {
633            $condition
634                ->andWith('process.istFolgeterminvon', 'IS', null)
635                ->orWith('process.istFolgeterminvon', '=', 0);
636        });
637        return $this;
638    }
639
640    public function addConditionSearch($queryString, $orWhere = false)
641    {
642        $condition = function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($queryString) {
643            $queryString = trim($queryString);
644            $query->orWith('process.Name', 'LIKE', "%$queryString%");
645            $query->orWith('process.EMail', 'LIKE', "%$queryString%");
646            $query->orWith('process.Telefonnummer', 'LIKE', "%$queryString%");
647            $query->orWith('process.telefonnummer_fuer_rueckfragen', 'LIKE', "%$queryString%");
648        };
649        if ($orWhere) {
650            $this->query->orWhere($condition);
651        } else {
652            $this->query->where($condition);
653        }
654        return $this;
655    }
656
657    public function addConditionName($name, $exactMatching = false)
658    {
659        if ($exactMatching) {
660            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($name) {
661                $query->andWith('process.Name', '=', $name);
662            });
663        } else {
664            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($name) {
665                $query->andWith('process.Name', 'LIKE', "%$name%");
666            });
667        }
668        return $this;
669    }
670
671    public function addConditionMail($mailAddress, $exactMatching = false)
672    {
673        if ($exactMatching) {
674            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($mailAddress) {
675                $query->andWith('process.Email', '=', $mailAddress);
676            });
677        } else {
678            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($mailAddress) {
679                $query->andWith('process.Email', 'LIKE', "%$mailAddress%");
680            });
681        }
682        return $this;
683    }
684
685    public function addConditionCustomTextfield($customText, $exactMatching = false)
686    {
687        if ($exactMatching) {
688            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($customText) {
689                $query->andWith('process.custom_text_field', '=', $customText);
690            });
691        } else {
692            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($customText) {
693                $query->andWith('process.custom_text_field', 'LIKE', "%$customText%");
694            });
695        }
696        return $this;
697    }
698
699    public function addConditionCustomTextfield2($customText2, $exactMatching = false)
700    {
701        if ($exactMatching) {
702            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($customText2) {
703                $query->andWith('process.custom_text_field2', '=', $customText2);
704            });
705        } else {
706            $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($customText2) {
707                $query->andWith('process.custom_text_field2', 'LIKE', "%$customText2%");
708            });
709        }
710        return $this;
711    }
712
713    public function addConditionAmendment($amendment)
714    {
715        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($amendment) {
716            $query->andWith('process.Anmerkung', 'LIKE', "%$amendment%");
717        });
718        return $this;
719    }
720
721    /**
722     * Add Requests Join
723     */
724    public function addConditionRequestId($requestId)
725    {
726        $this->leftJoin(
727            new Alias("buergeranliegen", 'buergeranliegen'),
728            'buergeranliegen.BuergerID',
729            '=',
730            'process.BuergerID'
731        );
732        $this->query->where('buergeranliegen.AnliegenID', '=', $requestId);
733        return $this;
734    }
735
736    /**
737     * add condition to get process if deallocation time < now
738     */
739    public function addConditionDeallocate($now)
740    {
741        $this->query->where(function (\BO\Zmsdb\Query\Builder\ConditionBuilder $query) use ($now) {
742            $query
743                ->andWith('process.Name', '=', '(abgesagt)')
744                ->andWith('process.IPTimeStamp', '<', $now->getTimestamp());
745        });
746        $this->query->orderBy('process.IPTimeStamp', 'ASC');
747        return $this;
748    }
749
750    public function addValuesNewProcess(\BO\Zmsentities\Process $process, $parentProcess = 0, $childProcessCount = 0)
751    {
752        $values = [
753            'BuergerID' => $process->id,
754            'IPTimeStamp' => $process->createTimestamp,
755            'absagecode' => $process->authKey,
756            'hatFolgetermine' => $childProcessCount,
757            'istFolgeterminvon' => $parentProcess,
758            'wartenummer' => $process->queue['number']
759        ];
760        if ($process->toProperty()->apiclient->apiClientID->isAvailable()) {
761            $values['apiClientID'] = $process->apiclient->apiClientID;
762        }
763        $this->addValues($values);
764    }
765
766    public function addValuesUpdateProcess(
767        \BO\Zmsentities\Process $process,
768        \DateTimeInterface $dateTime,
769        $parentProcess = 0,
770        $previousStatus = null
771    ) {
772        $this->addValuesIPAdress($process);
773        $this->addValuesStatusData($process, $dateTime);
774        if (0 === $parentProcess) {
775            $this->addValuesClientData($process);
776            $this->addProcessingTimeData($process, $dateTime, $previousStatus);
777            $this->addValuesQueueData($process);
778            $this->addValuesWaitingTimeData($process, $previousStatus);
779            $this->addValuesWayTimeData($process);
780        }
781        if ($process->isWithAppointment()) {
782            $this->addValuesFollowingProcessData($process, $parentProcess);
783        }
784        $this->addValuesWasMissed($process);
785    }
786
787    public function addValuesIPAdress($process)
788    {
789        $data = array();
790        $data['IPAdresse'] = $process['createIP'];
791        $this->addValues($data);
792    }
793
794    public function addValuesFollowingProcessData($process, $parentProcess)
795    {
796        $data = array();
797        if (0 === $parentProcess) {
798            $data['hatFolgetermine'] = (1 <= $process->getFirstAppointment()->getSlotCount()) ?
799                $process->getFirstAppointment()->getSlotCount() - 1 :
800                0;
801        } else {
802            $data['Name'] = '(Folgetermin)';
803        }
804        $this->addValues($data);
805    }
806
807    public function addValuesAppointmentData(
808        \BO\Zmsentities\Process $process
809    ) {
810        $data = array();
811        $appointment = $process->getFirstAppointment();
812        if (null !== $appointment) {
813            $datetime = $appointment->toDateTime();
814            $data['Datum'] = $datetime->format('Y-m-d');
815            $data['Uhrzeit'] = $datetime->format('H:i:s');
816        }
817        $this->addValues($data);
818    }
819
820    public function addValuesScopeData(
821        \BO\Zmsentities\Process $process
822    ) {
823        $data = array();
824        $data['StandortID'] = $process->getScopeId();
825        $this->addValues($data);
826    }
827
828    public function addValues($values)
829    {
830        $status = $this->calculateStatus($values);
831
832        if (!empty($status)) {
833            $values['status'] = $status;
834        }
835
836        $this->query->values($values);
837        return $this;
838    }
839
840    public function addValuesStatusData($process, \DateTimeInterface $dateTime)
841    {
842        $data = array();
843        $data['vorlaeufigeBuchung'] = ($process['status'] == 'reserved') ? 1 : 0;
844        $data['aufruferfolgreich'] = ($process['status'] == 'processing') ? 1 : 0;
845        if ($process->status == 'called') {
846            $data['parked'] = 0;
847            $data['nicht_erschienen'] = 0;
848        }
849        if ($process->status == 'pending') {
850            $data['AbholortID'] = $process->scope['id'];
851            $data['Abholer'] = 1;
852            $data['nicht_erschienen'] = 0;
853            $data['parked'] = 0;
854        }
855        if ($process->status == 'pickup') {
856            $data['AbholortID'] = $process->scope['id'];
857            $data['Abholer'] = 1;
858            $data['Timestamp'] = 0;
859            $data['nicht_erschienen'] = 0;
860            $data['parked'] = 0;
861        }
862        if ($process->status == 'queued') {
863            $data['nicht_erschienen'] = 0;
864            $data['parked'] = 0;
865            if (
866                $process->hasArrivalTime() &&
867                (isset($process->queue['withAppointment']) && $process->queue['withAppointment'])
868            ) {
869                $data['wsm_aufnahmezeit'] = $dateTime->format('H:i:s');
870            }
871        }
872        if ($process->status == 'missed') {
873            $data['nicht_erschienen'] = 1;
874        }
875        if ($process->status == 'parked') {
876            $data['parked'] = 1;
877        }
878        if ($process->status == 'confirmed') {
879            $data['bestaetigt'] = 1;
880        }
881        if ($process->status == 'preconfirmed') {
882            $data['bestaetigt'] = 0;
883        }
884
885        $this->addValues($data);
886    }
887
888    protected function addValuesClientData($process)
889    {
890        $data = array();
891        $client = $process->getFirstClient();
892        if ($client && $client->hasFamilyName()) {
893            $data['Name'] = $client->familyName;
894        }
895        if ($client && $client->hasEmail()) {
896            $data['EMail'] = $client->email;
897        }
898        if ($client && $client->offsetExists('telephone')) {
899            $data['telefonnummer_fuer_rueckfragen'] = $client->telephone;
900            $data['Telefonnummer'] = $client->telephone; // to stay compatible with ZMS1
901        }
902        if ($client && $client->offsetExists('emailSendCount')) {
903            $data['EMailverschickt'] = ('-1' == $client->emailSendCount) ? 0 : $client->emailSendCount;
904        }
905        if ($client && $client->offsetExists('notificationsSendCount')) {
906            $data['SMSverschickt'] = ('-1' == $client->notificationsSendCount) ? 0 : $client->notificationsSendCount;
907        }
908        if ($process->getAmendment()) {
909            $data['Anmerkung'] = $process->getAmendment();
910        }
911        if ($process->getCustomTextfield()) {
912            $data['custom_text_field'] = $process->getCustomTextfield();
913        }
914        if ($process->getCustomTextfield2()) {
915            $data['custom_text_field2'] = $process->getCustomTextfield2();
916        }
917        $data['zustimmung_kundenbefragung'] = ($client->surveyAccepted) ? 1 : 0;
918        $data['Erinnerungszeitpunkt'] = $process->getReminderTimestamp();
919        $data['AnzahlPersonen'] = $process->getClients()->count();
920        $this->addValues($data);
921    }
922
923    protected function addProcessingTimeData($process, \DateTimeInterface $dateTime, $previousStatus = null)
924    {
925        $data = array();
926        $timeoutTime = null;
927        $showUpTime = null;
928        $finishTime = null;
929
930        if (
931            isset($previousStatus) &&
932            (($process->status == 'called' && $previousStatus == 'called') ||
933                ($process->status == 'processing' && $previousStatus == 'processing'))
934        ) {
935            $timeoutTime = $dateTime->format('Y-m-d H:i:s');
936            $data['timeoutTime'] = $timeoutTime;
937        } elseif ($process->status == 'processing') {
938            $showUpTime = $dateTime->format('Y-m-d H:i:s');
939            $data['showUpTime'] = $showUpTime;
940        } elseif ($process->status == 'finished') {
941            $finishTime = $dateTime->format('Y-m-d H:i:s');
942            $data['finishTime'] = $finishTime;
943        }
944
945
946        if (isset($finishTime) && isset($process->showUpTime)) {
947            $showUpDateTime = new \DateTime($process->showUpTime);
948            $finishTime = new \DateTime($finishTime);
949
950            $processingTimeStr = $process->getProcessingTime();
951            $previousProcessingTimeInSeconds = 0; // Default to 0 if not set
952
953            if (!empty($processingTimeStr)) {
954                // Assume the format is HH:MM:SS and parse it
955                list($hours, $minutes, $seconds) = explode(':', $processingTimeStr);
956                // Convert hours, minutes, and seconds to total seconds
957                $previousProcessingTimeInSeconds = (int)$hours * 3600 + (int)$minutes * 60 + (int)$seconds;
958            }
959
960            $interval = $showUpDateTime->diff($finishTime);
961            $totalSeconds = ($interval->days * 24 * 60 * 60) + ($interval->h * 60 * 60) + ($interval->i * 60) + $interval->s;
962
963            $totalSeconds += $previousProcessingTimeInSeconds;
964
965            $hours = intdiv($totalSeconds, 3600);
966            $minutes = intdiv($totalSeconds % 3600, 60);
967            $seconds = $totalSeconds % 60;
968
969            $data['processingTime'] = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
970        } elseif (isset($timeoutTime) && isset($process->showUpTime)) {
971            $showUpDateTime = new \DateTime($process->showUpTime);
972            $timeoutDateTime = new \DateTime($timeoutTime);
973            $processingTimeStr = $process->getProcessingTime();
974
975            $previousProcessingTimeInSeconds = 0; // Default to 0 if not set
976            if (!empty($processingTimeStr)) {
977                // Assume the format is HH:MM:SS and parse it
978                list($hours, $minutes, $seconds) = explode(':', $processingTimeStr);
979                // Convert hours, minutes, and seconds to total seconds
980                $previousProcessingTimeInSeconds = (int)$hours * 3600 + (int)$minutes * 60 + (int)$seconds;
981            }
982            $interval = $showUpDateTime->diff($timeoutDateTime);
983            $totalSeconds = ($interval->days * 24 * 60 * 60) + ($interval->h * 60 * 60) + ($interval->i * 60) + $interval->s;
984
985            $totalSeconds += $previousProcessingTimeInSeconds;
986
987            $hours = intdiv($totalSeconds, 3600);
988            $minutes = intdiv($totalSeconds % 3600, 60);
989            $seconds = $totalSeconds % 60;
990
991            $data['processingTime'] = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
992        }
993
994        $this->addValues($data);
995    }
996
997    protected function addValuesQueueData($process)
998    {
999        $data = array();
1000        $appointmentTime = $process->getFirstAppointment()->toDateTime()->format('H:i:s');
1001
1002        if (isset($process->queue['callCount']) && $process->queue['callCount']) {
1003            $data['AnzahlAufrufe'] = $process->queue['callCount'];
1004        }
1005        if (isset($process->queue['callTime']) && $process->queue['callTime']) {
1006            $data['aufrufzeit'] = (new \DateTimeImmutable())
1007                ->setTimestamp($process->queue['callTime'])->format('H:i:s');
1008        }
1009        if (isset($process->queue['lastCallTime']) && $process->queue['lastCallTime']) {
1010            $data['Timestamp'] = (new \DateTimeImmutable())
1011                ->setTimestamp($process->queue['lastCallTime'])->format('H:i:s');
1012        }
1013        if (isset($process->queue['arrivalTime']) && $process->queue['arrivalTime']) {
1014            $data['wsm_aufnahmezeit'] = (new \DateTimeImmutable())
1015                ->setTimestamp($process->queue['arrivalTime'])->format('H:i:s');
1016        }
1017        if (isset($data['wsm_aufnahmezeit']) && $data['wsm_aufnahmezeit'] == $appointmentTime) {
1018            // Do not save arrivalTime if it is an appointment
1019            $data['wsm_aufnahmezeit'] = 0;
1020        }
1021        $this->addValues($data);
1022    }
1023
1024    protected function addValuesWaitingTimeData($process, $previousStatus = null)
1025    {
1026        $data = array();
1027
1028        if (
1029            (
1030                // Szenario 1: Vorheriger Status ist queued, missed oder confirmed und aktueller Status ist called
1031                in_array($previousStatus, ['queued', 'missed', 'confirmed'])
1032                && $process['status'] == 'called'
1033                && ($process->queue['callCount'] <= 0 || !empty($process['wasMissed']))
1034            )
1035            ||
1036            (
1037                // Szenario 2: Vorheriger Status ist missed, aktueller Status ist queued,
1038                // es gibt den Hinweis wasMissed und die Queue sowie waitingTime sind gesetzt
1039                $previousStatus == 'missed'
1040                && $process['status'] == 'queued'
1041                && !empty($process['wasMissed'])
1042                && isset($process->queue)
1043                && isset($process->queue->waitingTime)
1044            )
1045        ) {
1046            $wartezeitInSeconds = $process->getWaitedSeconds();
1047            $wartezeitInSeconds = $wartezeitInSeconds > 0 ? $wartezeitInSeconds : 0;
1048
1049            // Convert total seconds into HH:MM:SS format
1050            $hours = intdiv($wartezeitInSeconds, 3600);
1051            $minutes = intdiv($wartezeitInSeconds % 3600, 60);
1052            $seconds = $wartezeitInSeconds % 60;
1053
1054            $data['wartezeit'] = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
1055        }
1056
1057        $this->addValues($data);
1058    }
1059
1060
1061    protected function addValuesWayTimeData($process)
1062    {
1063        $data = array();
1064        if ($process['status'] == 'processing') {
1065            $wegezeit = $process->getWayMinutes();
1066            $data['wegezeit'] = $wegezeit > 0 ? $wegezeit : 0;
1067        }
1068        $this->addValues($data);
1069    }
1070
1071    protected function addValuesWasMissed($process)
1072    {
1073        $data = [
1074            'wasMissed' => $process->wasMissed ? 1 : 0,
1075        ];
1076
1077        $this->addValues($data);
1078        return $this;
1079    }
1080
1081    public function postProcess($data)
1082    {
1083        $data[$this->getPrefixed("appointments__0__date")] =
1084            strtotime($data[$this->getPrefixed("appointments__0__date")]);
1085        if ('00:00:00' != $data[$this->getPrefixed("queue__callTime")]) {
1086            $time = explode(':', $data[$this->getPrefixed("queue__callTime")]);
1087            $data[$this->getPrefixed("queue__callTime")] = (new \DateTimeImmutable())
1088                ->setTimestamp($data[$this->getPrefixed("appointments__0__date")])
1089                ->setTime($time[0], $time[1], $time[2])
1090                ->getTimestamp();
1091        } else {
1092            $data[$this->getPrefixed("queue__callTime")] = 0;
1093        }
1094        if ('00:00:00' != $data[$this->getPrefixed("queue__lastCallTime")]) {
1095            $time = explode(':', $data[$this->getPrefixed("queue__lastCallTime")]);
1096            $data[$this->getPrefixed("queue__lastCallTime")] = (new \DateTimeImmutable())
1097                ->setTimestamp($data[$this->getPrefixed("appointments__0__date")])
1098                ->setTime($time[0], $time[1], $time[2])
1099                ->getTimestamp();
1100        } else {
1101            $data[$this->getPrefixed("queue__lastCallTime")] = 0;
1102        }
1103        $data[$this->getPrefixed("queue__arrivalTime")] =
1104            strtotime($data[$this->getPrefixed("queue__arrivalTime")]);
1105        if (
1106            isset($data[$this->getPrefixed('scope__provider__data')])
1107            && $data[$this->getPrefixed('scope__provider__data')]
1108        ) {
1109            $data[$this->getPrefixed('scope__provider__data')] =
1110                json_decode($data[$this->getPrefixed('scope__provider__data')], true);
1111        }
1112        if (isset($data[$this->getPrefixed('__clientsCount')])) {
1113            $clientsCount = $data[$this->getPrefixed('__clientsCount')];
1114            unset($data[$this->getPrefixed('__clientsCount')]);
1115            while (--$clientsCount > 0) {
1116                $data[$this->getPrefixed('clients__' . $clientsCount . '__familyName')] = 'Unbekannt';
1117            }
1118        }
1119        $data[$this->getPrefixed("lastChange")] =
1120            (new \DateTimeImmutable($data[$this->getPrefixed("lastChange")] .
1121                \BO\Zmsdb\Connection\Select::$connectionTimezone))->getTimestamp();
1122        return $data;
1123    }
1124
1125    public function removeDuplicates()
1126    {
1127        $this->query->groupBy('process.BuergerID');
1128        return $this;
1129    }
1130
1131    protected function addRequiredJoins()
1132    {
1133        $this->leftJoin(
1134            new Alias(Useraccount::TABLE, 'processuser'),
1135            'process.NutzerID',
1136            '=',
1137            'processuser.NutzerID'
1138        );
1139
1140        $this->leftJoin(
1141            new Alias(Scope::TABLE, 'processscope'),
1142            'process.StandortID',
1143            '=',
1144            'processscope.StandortID'
1145        );
1146    }
1147}