Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
ProcessStatusFree
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 buildDaysCondition
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace BO\Zmsbackend\Process\Repository;
4
5class ProcessStatusFree extends \BO\Zmsbackend\Query\Base
6{
7    /**
8     * Shared free-process query used by zmsadmin / classic free-slot APIs (same semantics as next).
9     *
10     * see also \BO\Zmsbackend\Day\Repository\Day::QUERY_DAYLIST_JOIN
11     */
12    const string QUERY_SELECT_PROCESSLIST_DAYS = '
13        SELECT
14            -- tmp_avail.*,
15            "free" AS status,
16            CONCAT(year, "-", month, "-", day, " ", time) AS appointments__0__date,
17            slotsRequired AS appointments__0__slotCount,
18            scopeID AS scope__id
19        FROM
20            (SELECT
21               COUNT(slotID) as ancestorCount,
22               IF(MIN(available - confirmed) > 0, MIN(available - confirmed), 0) as free,
23               tmp_ancestor.*
24            FROM (SELECT
25                IFNULL(COUNT(p.slotID), 0) confirmed,
26                IF(:slotType = "intern", s.intern,
27                    IF(:slotType = "public", s.`public`, 0)
28                    ) available,
29                IF(a.multiple_slots_allowed, c.slotsRequired, :forceRequiredSlots) slotsRequired,
30                s.*,
31                cc.id
32            FROM
33                calendarscope c
34                INNER JOIN slot s
35                    ON c.scopeID = s.scopeID
36                        %s
37                        AND s.status = "free"
38                LEFT JOIN oeffnungszeit a ON s.availabilityID = a.OeffnungszeitID
39                LEFT JOIN slot_hiera h ON h.ancestorID = s.slotID
40                    AND h.ancestorLevel <= IF(a.multiple_slots_allowed, c.slotsRequired, :forceRequiredSlots)
41                INNER JOIN slot s2 on h.slotID = s2.slotID and s2.status = "free"
42                LEFT JOIN slot_process p ON h.slotID = p.slotID
43                LEFT JOIN closures cc ON (s.scopeID = cc.StandortID AND s.year = cc.year AND s.month = cc.month and s.day = cc.day)
44            GROUP BY s.slotID, h.slotID
45            HAVING cc.id IS NULL
46            ) AS tmp_ancestor
47            GROUP BY slotID
48            HAVING ancestorCount >= slotsRequired
49            ) AS tmp_avail 
50            INNER JOIN slot_sequence sq ON sq.slotsequence <= tmp_avail.free
51    ';
52
53    /**
54     * Citizen calendar-availability free-process query only.
55     *
56     * Occupancy is pre-aggregated from slot_process (same approach as Day::QUERY_DAYLIST_JOIN_AVAILABILITY).
57     *
58     * see also \BO\Zmsbackend\Day\Repository\Day::QUERY_DAYLIST_JOIN_AVAILABILITY
59     */
60    const string QUERY_SELECT_PROCESSLIST_DAYS_AVAILABILITY = '
61        SELECT
62            -- tmp_avail.*,
63            "free" AS status,
64            CONCAT(year, "-", month, "-", day, " ", time) AS appointments__0__date,
65            slotsRequired AS appointments__0__slotCount,
66            scopeID AS scope__id
67        FROM
68            (SELECT
69               COUNT(slotID) as ancestorCount,
70               IF(MIN(available - confirmed) > 0, MIN(available - confirmed), 0) as free,
71               tmp_ancestor.*
72            FROM (SELECT
73                IFNULL(occ.confirmed, 0) confirmed,
74                IF(:slotType = "intern", s.intern,
75                    IF(:slotType = "public", s.`public`, 0)
76                    ) available,
77                IF(a.multiple_slots_allowed, c.slotsRequired, :forceRequiredSlots) slotsRequired,
78                s.*,
79                cc.id
80            FROM
81                calendarscope c
82                INNER JOIN slot s
83                    ON c.scopeID = s.scopeID
84                        %s
85                        AND s.status = "free"
86                LEFT JOIN oeffnungszeit a ON s.availabilityID = a.OeffnungszeitID
87                LEFT JOIN slot_hiera h ON h.ancestorID = s.slotID
88                    AND h.ancestorLevel <= IF(a.multiple_slots_allowed, c.slotsRequired, :forceRequiredSlots)
89                INNER JOIN slot s2 on h.slotID = s2.slotID and s2.status = "free"
90                LEFT JOIN (
91                    SELECT p.slotID, COUNT(*) AS confirmed
92                    FROM slot_process p
93                    INNER JOIN slot s_occ
94                        ON s_occ.slotID = p.slotID
95                    INNER JOIN calendarscope c_occ
96                        ON c_occ.scopeID = s_occ.scopeID
97                        AND c_occ.year = s_occ.year
98                        AND c_occ.month = s_occ.month
99                    GROUP BY p.slotID
100                ) occ ON occ.slotID = h.slotID
101                LEFT JOIN closures cc ON (s.scopeID = cc.StandortID AND s.year = cc.year AND s.month = cc.month and s.day = cc.day)
102            WHERE cc.id IS NULL
103            ) AS tmp_ancestor
104            GROUP BY slotID
105            HAVING ancestorCount >= slotsRequired
106            ) AS tmp_avail 
107            INNER JOIN slot_sequence sq ON sq.slotsequence <= tmp_avail.free
108    ';
109
110    const string GROUPBY_SELECT_PROCESSLIST_DAY = 'GROUP BY scope__id, appointments__0__date';
111
112    public static function buildDaysCondition(array $days): string
113    {
114        $sql = 'AND (';
115        $sqlPats = [];
116
117        foreach ($days as $day) {
118            $sqlPats[] = '(c.year = ' . $day->format('Y') . '
119                        AND c.month = ' . $day->format('m') . '
120                        AND s.day = ' . $day->format('d') . '
121                        AND c.year = s.year
122                        AND c.month = s.month)';
123        }
124
125        return $sql . implode(' OR ', $sqlPats) . ')';
126    }
127}