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