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