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