Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AppointmentDeleteByCron
n/a
0 / 0
n/a
0 / 0
39
n/a
0 / 0
 __construct
n/a
0 / 0
n/a
0 / 0
2
 log
n/a
0 / 0
n/a
0 / 0
1
 getCount
n/a
0 / 0
n/a
0 / 0
1
 setLimit
n/a
0 / 0
n/a
0 / 0
1
 setLoopCount
n/a
0 / 0
n/a
0 / 0
1
 startProcessing
n/a
0 / 0
n/a
0 / 0
1
 deleteExpiredProcesses
n/a
0 / 0
n/a
0 / 0
2
 deleteBlockedProcesses
n/a
0 / 0
n/a
0 / 0
1
 deleteByCallback
n/a
0 / 0
n/a
0 / 0
5
 removeProcess
n/a
0 / 0
n/a
0 / 0
8
 updateProcessStatus
n/a
0 / 0
n/a
0 / 0
2
 determineHistoryStatus
n/a
0 / 0
n/a
0 / 0
4
 shouldArchiveProcess
n/a
0 / 0
n/a
0 / 0
3
 archiveProcess
n/a
0 / 0
n/a
0 / 0
3
 deleteProcess
n/a
0 / 0
n/a
0 / 0
4
1<?php
2
3namespace BO\Zmsbackend\Helper;
4
5/**
6 * @codeCoverageIgnore
7 */
8class AppointmentDeleteByCron
9{
10    use VerboseCronLogTrait;
11
12    protected $verbose = false;
13
14    protected $limit = 10000;
15
16    protected $loopCount = 500;
17
18    protected $time;
19
20    protected $statuslist = [
21        "blocked",
22        "reserved",
23        "deleted",
24        "confirmed",
25        "preconfirmed",
26        "queued",
27        "called",
28        "missed",
29        'parked',
30        "processing",
31        "pending",
32    ];
33
34    protected $archivelist = [
35        "confirmed",
36        "queued",
37        "called",
38        "missed",
39        'parked',
40        "processing",
41        "pending"
42    ];
43
44    /** @var array<string, int> */
45    protected array $count = [];
46
47    protected \DateTimeInterface $now;
48
49    public function __construct(int $timeIntervalDays, \DateTimeInterface $now, bool $verbose = false)
50    {
51        $this->now = $now;
52        $this->time = \DateTimeImmutable::createFromInterface($now)
53            ->setTime(0, 0, 0)
54            ->modify(sprintf('-%d days', $timeIntervalDays));
55        if ($verbose) {
56            $this->log("INFO: Deleting appointments with date before " . $this->time->format('Y-m-d'));
57            $this->verbose = true;
58        }
59    }
60
61    protected function log(string $message, string $level = 'info'): void
62    {
63        $this->writeVerboseCronLog($message, $level);
64    }
65
66    public function getCount()
67    {
68        return $this->count;
69    }
70
71    public function setLimit(int $limit): void
72    {
73        $this->limit = $limit;
74    }
75
76    public function setLoopCount(int $loopCount): void
77    {
78        $this->loopCount = $loopCount;
79    }
80
81    public function startProcessing(bool $commit): void
82    {
83        $this->count = array_fill_keys($this->statuslist, 0);
84        $this->deleteBlockedProcesses($commit);
85        $this->deleteExpiredProcesses($commit);
86        $this->log("\nSUMMARY: Deleted processes: " . var_export($this->count, true));
87    }
88
89    protected function deleteExpiredProcesses(bool $commit): void
90    {
91        foreach ($this->statuslist as $status) {
92            $this->log("\nDelete expired processes with status $status:");
93            $count = $this->deleteByCallback($commit, function ($limit, $offset) use ($status) {
94                $query = new \BO\Zmsbackend\Process\Service\Process();
95                $processList = $query->readExpiredProcessListByStatus($this->time, $status, $limit, $offset);
96                return $processList;
97            });
98            $this->count[$status] += $count;
99        }
100    }
101
102    protected function deleteBlockedProcesses(bool $commit): void
103    {
104        $this->log("\nDelete blocked processes in the future:");
105        $count = $this->deleteByCallback($commit, function ($limit, $offset) {
106            $query = new \BO\Zmsbackend\Process\Service\Process();
107            $processList = $query->readProcessListByScopeAndStatus(0, 'blocked', 0, $limit, $offset);
108            return $processList;
109        });
110        $this->count["blocked"] += $count;
111    }
112
113    protected function deleteByCallback(bool $commit, \Closure $callback): int
114    {
115        $processCount = 0;
116        $startposition = 0;
117        while ($processCount < $this->limit) {
118            $processList = $callback($this->loopCount, $startposition);
119            if (0 == $processList->count()) {
120                break;
121            }
122            foreach ($processList as $process) {
123                if (!$this->removeProcess($process, $commit, $processCount)) {
124                    $startposition++;
125                }
126                $processCount++;
127            }
128        }
129        return $processCount;
130    }
131
132    protected function removeProcess(\BO\Zmsentities\Process $process, bool $commit, int $processCount): int
133    {
134        $verbose = $this->verbose;
135        if (in_array($process->status, $this->statuslist)) {
136            if (in_array($process->status, $this->archivelist)) {
137                $this->log("INFO: $processCount. Archive $process");
138                $process = $this->updateProcessStatus($process);
139
140                if ($commit && $this->shouldArchiveProcess($process)) {
141                    $historyStatus = $this->determineHistoryStatus($process);
142
143                    if ($historyStatus !== null) {
144                        $historyService =
145                            new \BO\Zmsbackend\ProcessSearchHistory\Service\ProcessSearchHistory();
146
147                        $historyService->writeHistoryEntry(
148                            $process,
149                            $historyStatus,
150                            $this->now
151                        );
152                    }
153
154                    $this->archiveProcess($process);
155                }
156            }
157            $this->log("INFO: $processCount. Delete $process");
158            if ($commit) {
159                $this->deleteProcess($process);
160                return 1;
161            }
162        } elseif ($verbose) {
163            $this->log("INFO: Keep process $process");
164        }
165        return 0;
166    }
167
168    protected function updateProcessStatus(\BO\Zmsentities\Process $process): \BO\Zmsentities\Process
169    {
170        if (in_array($process->getStatus(), ["confirmed", "queued", "called"])) {
171            $process->setStatus('missed');
172        }
173        return $process;
174    }
175
176    protected function determineHistoryStatus(\BO\Zmsentities\Process $process): ?string
177    {
178        return match ($process->getStatus()) {
179            'missed' =>
180                \BO\Zmsbackend\ProcessSearchHistory\Service\ProcessSearchHistory::STATUS_MISSED,
181
182            'processing',
183            'parked',
184            'pending' =>
185                \BO\Zmsbackend\ProcessSearchHistory\Service\ProcessSearchHistory::STATUS_COMPLETED,
186
187            default => null,
188        };
189    }
190
191    protected function shouldArchiveProcess(\BO\Zmsentities\Process $process): bool
192    {
193        if ($process->isDereferenced()) {
194            $this->log("WARN: Skip archive for dereferenced process {$process->id}, delete only", 'warning');
195            return false;
196        }
197        if (!$process->getScopeId()) {
198            $this->log("WARN: Skip archive for process {$process->id} without scope, delete only", 'warning');
199            return false;
200        }
201        return true;
202    }
203
204    protected function archiveProcess(\BO\Zmsentities\Process $process): void
205    {
206        $verbose = $this->verbose;
207        $archiver = new \BO\Zmsbackend\Process\Service\ProcessStatusArchived();
208        $archived = $archiver->writeEntityFinished($process, $this->now);
209        if ($archived && $verbose) {
210            $this->log("INFO: Archived with Status=$process->status and Id=" . $archived->archiveId);
211        }
212    }
213
214    protected function deleteProcess(\BO\Zmsentities\Process $process): void
215    {
216        $verbose = $this->verbose;
217        $query = new \BO\Zmsbackend\Process\Service\Process();
218        if ($query->writeDeletedEntity($process->id)) {
219            if ($verbose) {
220                $this->log("INFO: Process $process->id successfully removed");
221            }
222        } else {
223            if ($verbose) {
224                $this->log("WARN: Could not remove process '$process->id'!");
225            }
226        }
227    }
228}