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
35
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
7
 updateProcessStatus
n/a
0 / 0
n/a
0 / 0
2
 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\Zmsdb\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    protected $count = [];
44
45    public function __construct($timeIntervalDays, \DateTimeInterface $now, $verbose = false)
46    {
47        $deleteInSeconds = (24 * 60 * 60) * $timeIntervalDays;
48        $time = new \DateTimeImmutable();
49        $this->time = $time->setTimestamp($now->getTimestamp() - $deleteInSeconds);
50        if ($verbose) {
51            $this->log("INFO: Deleting appointments older than " . $this->time->format('c'));
52            $this->verbose = true;
53        }
54    }
55
56    protected function log($message, string $level = 'info')
57    {
58        $this->writeVerboseCronLog($message, $level);
59    }
60
61    public function getCount()
62    {
63        return $this->count;
64    }
65
66    public function setLimit($limit)
67    {
68        $this->limit = $limit;
69    }
70
71    public function setLoopCount($loopCount)
72    {
73        $this->loopCount = $loopCount;
74    }
75
76    public function startProcessing($commit, $pending = false)
77    {
78        if ($pending) {
79            $this->statuslist[] = "pending";
80        }
81        $this->count = array_fill_keys($this->statuslist, 0);
82        $this->deleteBlockedProcesses($commit);
83        $this->deleteExpiredProcesses($commit);
84        $this->log("\nSUMMARY: Deleted processes: " . var_export($this->count, true));
85    }
86
87    protected function deleteExpiredProcesses($commit)
88    {
89        foreach ($this->statuslist as $status) {
90            $this->log("\nDelete expired processes with status $status:");
91            $count = $this->deleteByCallback($commit, function ($limit, $offset) use ($status) {
92                $query = new \BO\Zmsdb\Process();
93                $processList = $query->readExpiredProcessListByStatus($this->time, $status, $limit, $offset);
94                return $processList;
95            });
96            $this->count[$status] += $count;
97        }
98    }
99
100    protected function deleteBlockedProcesses($commit)
101    {
102        $this->log("\nDelete blocked processes in the future:");
103        $count = $this->deleteByCallback($commit, function ($limit, $offset) {
104            $query = new \BO\Zmsdb\Process();
105            $processList = $query->readProcessListByScopeAndStatus(0, 'blocked', 0, $limit, $offset);
106            return $processList;
107        });
108        $this->count["blocked"] += $count;
109    }
110
111    protected function deleteByCallback($commit, \Closure $callback)
112    {
113        $processCount = 0;
114        $startposition = 0;
115        while ($processCount < $this->limit) {
116            $processList = $callback($this->loopCount, $startposition);
117            if (0 == $processList->count()) {
118                break;
119            }
120            foreach ($processList as $process) {
121                if (!$this->removeProcess($process, $commit, $processCount)) {
122                    $startposition++;
123                }
124                $processCount++;
125            }
126        }
127        return $processCount;
128    }
129
130    protected function removeProcess(\BO\Zmsentities\Process $process, $commit, $processCount)
131    {
132        $verbose = $this->verbose;
133        if (in_array($process->status, $this->statuslist)) {
134            if (in_array($process->status, $this->archivelist)) {
135                $this->log("INFO: $processCount. Archive $process");
136                $process = $this->updateProcessStatus($process);
137                if ($commit && $this->shouldArchiveProcess($process)) {
138                    $this->archiveProcess($process);
139                }
140            }
141            $this->log("INFO: $processCount. Delete $process");
142            if ($commit) {
143                $this->deleteProcess($process);
144                return 1;
145            }
146        } elseif ($verbose) {
147            $this->log("INFO: Keep process $process");
148        }
149        return 0;
150    }
151
152    protected function updateProcessStatus(\BO\Zmsentities\Process $process)
153    {
154        if (in_array($process->status, ["confirmed", "queued", "called"])) {
155            $process->status = 'missed';
156        }
157        return $process;
158    }
159
160    protected function shouldArchiveProcess(\BO\Zmsentities\Process $process): bool
161    {
162        if ($process->isDereferenced()) {
163            $this->log("WARN: Skip archive for dereferenced process {$process->id}, delete only", 'warning');
164            return false;
165        }
166        if (!$process->getScopeId()) {
167            $this->log("WARN: Skip archive for process {$process->id} without scope, delete only", 'warning');
168            return false;
169        }
170        return true;
171    }
172
173    protected function archiveProcess(\BO\Zmsentities\Process $process)
174    {
175        $verbose = $this->verbose;
176        $now = new \DateTimeImmutable();
177        $archiver = new \BO\Zmsdb\ProcessStatusArchived();
178        $archived = $archiver->writeEntityFinished($process, $now);
179        if ($archived && $verbose) {
180            $this->log("INFO: Archived with Status=$process->status and Id=" . $archived->archiveId);
181        }
182    }
183
184    protected function deleteProcess(\BO\Zmsentities\Process $process)
185    {
186        $verbose = $this->verbose;
187        $query = new \BO\Zmsdb\Process();
188        if ($query->writeDeletedEntity($process->id)) {
189            if ($verbose) {
190                $this->log("INFO: Process $process->id successfully removed");
191            }
192        } else {
193            if ($verbose) {
194                $this->log("WARN: Could not remove process '$process->id'!");
195            }
196        }
197    }
198}