Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.87% covered (success)
97.87%
46 / 47
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CalendarStatusSupervisorTask
97.83% covered (success)
97.83%
45 / 46
75.00% covered (warning)
75.00%
3 / 4
5
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
2.00
 transitionToInProgress
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 transitionToOverdue
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Calendar\Task;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Cron\CronTaskInterface;
12use App\Core\Database\FallbackPdoResolver;
13use DateTimeImmutable;
14use DateTimeInterface;
15use PDO;
16
17/**
18 * Calendar Event Status Lifecycle Supervisor Background Task.
19 *
20 * Implements CronTaskInterface to automatically transition calendar event statuses:
21 * - 'planned' -> 'in_progress' when start_date <= reference_time < end_date
22 * - 'planned', 'in_progress' -> 'overdue' when end_date <= reference_time
23 * Terminal statuses ('completed', 'cancelled', 'postponed') are preserved.
24 *
25 * @package App\Modules\Calendar\Task
26 */
27final readonly class CalendarStatusSupervisorTask implements CronTaskInterface
28{
29    /**
30     * CalendarStatusSupervisorTask constructor.
31     *
32     * @param PDO|null $pdo Database connection or null for fallback resolution.
33     * @param string $tablePrefix Database table prefix.
34     * @param DateTimeInterface|null $referenceTime Optional reference time for testing.
35     */
36    public function __construct(
37        private ?PDO $pdo = null,
38        private string $tablePrefix = 'a_',
39        private ?DateTimeInterface $referenceTime = null,
40    ) {
41    }
42
43    /**
44     * {@inheritdoc}
45     */
46    public function run(): string
47    {
48        $pdo = $this->pdo ?? FallbackPdoResolver::resolveDefaultConnection();
49        if (!$pdo instanceof PDO) {
50            return 'Calendar status supervision skipped: No database connection available.';
51        }
52
53        $tableName = $this->tablePrefix . 'mod_calendar_records';
54        $nowStr = ($this->referenceTime ?? new DateTimeImmutable())->format('Y-m-d H:i:s');
55
56        $inProgressCount = $this->transitionToInProgress($pdo, $tableName, $nowStr);
57        $overdueCount = $this->transitionToOverdue($pdo, $tableName, $nowStr);
58
59        return sprintf(
60            'Calendar status supervision completed: %d transitioned to in_progress, %d to overdue.',
61            $inProgressCount,
62            $overdueCount
63        );
64    }
65
66    /**
67     * Transitions active events to 'in_progress' if start_date has arrived but end_date has not.
68     *
69     * @param PDO $pdo Database connection.
70     * @param string $tableName Calendar records table name.
71     * @param string $nowStr Formatted reference datetime string.
72     * @return int Number of updated records.
73     */
74    private function transitionToInProgress(PDO $pdo, string $tableName, string $nowStr): int
75    {
76        $sql = sprintf(
77            'UPDATE `%s` '
78            . 'SET `status` = :in_progress '
79            . 'WHERE `status` = :planned '
80            . 'AND `start_date` <= :now_start '
81            . 'AND `end_date` > :now_end '
82            . 'AND `special_access` = 1',
83            $tableName
84        );
85
86        $stmt = $pdo->prepare($sql);
87        $stmt->execute([
88            ':in_progress' => 'in_progress',
89            ':planned' => 'planned',
90            ':now_start' => $nowStr,
91            ':now_end' => $nowStr,
92        ]);
93
94        return $stmt->rowCount();
95    }
96
97    /**
98     * Transitions past uncompleted events to 'overdue' if end_date has passed.
99     *
100     * @param PDO $pdo Database connection.
101     * @param string $tableName Calendar records table name.
102     * @param string $nowStr Formatted reference datetime string.
103     * @return int Number of updated records.
104     */
105    private function transitionToOverdue(PDO $pdo, string $tableName, string $nowStr): int
106    {
107        $sql = sprintf(
108            'UPDATE `%s` '
109            . 'SET `status` = :overdue '
110            . 'WHERE `status` IN (:planned, :in_progress) '
111            . 'AND `end_date` <= :now '
112            . 'AND `special_access` = 1',
113            $tableName
114        );
115
116        $stmt = $pdo->prepare($sql);
117        $stmt->execute([
118            ':overdue' => 'overdue',
119            ':planned' => 'planned',
120            ':in_progress' => 'in_progress',
121            ':now' => $nowStr,
122        ]);
123
124        return $stmt->rowCount();
125    }
126}