PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.20
VikAppointments Services Booking Calendar v1.2.20
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / availability / timeline / parser / groupext.php
vikappointments / site / helpers / libraries / availability / timeline / parser Last commit date
allemployees.php 1 month ago employee.php 1 month ago group.php 1 month ago groupext.php 1 month ago index.html 1 month ago service.php 1 month ago
groupext.php
237 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 VAPLoader::import('libraries.availability.timeline.parser.group');
15
16 /**
17 * Extended group timeline parser.
18 *
19 * @since 1.7.8
20 */
21 class VAPAvailabilityTimelineParserGroupext extends VAPAvailabilityTimelineParserGroup
22 {
23 /**
24 * Helper method used to elaborate the resulting timeline.
25 *
26 * @param string $date The UTC date in military format.
27 * @param integer $people The number of participants.
28 * @param array $worktime A list of working times.
29 * @param array $bookings A list of booked appointments.
30 * @param object $service The service details.
31 * @param mixed &$seats An array containing the remaining seats for each time.
32 *
33 * @return array The resulting timeline.
34 */
35 protected function elaborateTimeLine($date, $people, $worktime, $bookings, $service, &$seats = null)
36 {
37 $arr = [];
38 $seats = $seats ?? [];
39
40 // default one specified by the configuration
41 $min_int = VAPFactory::getConfig()->getUint('minuteintervals');
42
43 for ($i = 0; $i < count($worktime); $i++)
44 {
45 for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
46 {
47 $arr[$i][$j] = 1;
48 }
49 }
50
51 // fetch default system timezone
52 $tz = JFactory::getApplication()->get('offset', 'UTC');
53
54 // re-format check-in date for correct comparison
55 $ymd = JFactory::getDate($date)->format('Y-m-d');
56
57 $cont_people = 0;
58
59 foreach ($bookings as $k => $b)
60 {
61 if (!$b->timezone)
62 {
63 // use default system timezone
64 $b->timezone = $tz;
65 }
66
67 // increase people count
68 $cont_people += $b->people_count;
69
70 if ($k == count($bookings) - 1 || $bookings[$k + 1]->checkin_ts != $b->checkin_ts || $bookings[$k + 1]->timezone != $b->timezone)
71 {
72 // create check-in and adjust it to the employee timezone
73 $checkin = JFactory::getDate($b->checkin_ts);
74 $checkin->setTimezone(new DateTimeZone($b->timezone));
75
76 // get check-in time adjusted to local timezone
77 $from = $checkin->format('H:i', $local = true);
78 // convert time string in minutes
79 $from = JHtml::fetch('vikappointments.time2min', $from);
80
81 /**
82 * Check the day factor of a reservation to check if it is referring
83 * to the current day or if it close to the bounds of this working time.
84 * Used to support midnight reservations.
85 *
86 * @since 1.6
87 */
88
89 if ($checkin->format('Y-m-d', $local = true) > $ymd)
90 {
91 // we are evaluating a reservation for the next day, so we need to increase the
92 // initial time by 1440 minutes (24 hours * 60).
93 $from += 1440;
94 }
95 else if ($checkin->format('Y-m-d', $local = true) < $ymd)
96 {
97 // we are evaluating a reservation for the previous day, so we need to decrease the
98 // initial time by 1440 minutes (24 hours * 60).
99 $from -= 1440;
100 }
101
102 // calculate end time by adding total duration to start time
103 $to = $from + $b->duration + $b->sleep;
104
105 for ($i = $from; $i < $to; $i += $min_int)
106 {
107 $found = false;
108
109 for ($j = 0; $j < count($arr) && !$found; $j++)
110 {
111 // calculate the number of occupied seats
112 $real_occupancy = $service->max_capacity - ($seats[$i] ?? $service->max_capacity) + $cont_people;
113
114 /**
115 * Try to block appointments that come from a different service or
116 * if the number of people exceeds the total capacity.
117 *
118 * @since 1.6 check if the services are different only if $arr[$j][$i] is set
119 */
120 if (!empty($arr[$j][$i]) && ($real_occupancy + $people > $service->max_capacity || $b->id_service != $service->id || $b->closure))
121 {
122 $found = true;
123 $arr[$j][$i] = 0;
124 }
125
126 if ($b->id_service == $service->id && !$b->closure)
127 {
128 if (!isset($seats[$i]))
129 {
130 $seats[$i] = $service->max_capacity;
131 }
132
133 // same service, we can display the remaining seats
134 $seats[$i] = max(0, $seats[$i] - $cont_people);
135 }
136 else
137 {
138 // booked for a different service, unset the remaining seats
139 $seats[$i] = 0;
140 }
141 }
142
143 /**
144 * We may have different services that display shifted
145 * timelines. This would cause an issue as previous check
146 * ignores the times that don't match the evaluated slots.
147 *
148 * We need to unset here all the times that intersect with
149 * an existing reservation, which might have been created for
150 * a different service.
151 *
152 * @since 1.6.2
153 */
154 if (!$found)
155 {
156 // find all slots that intersect this one
157 for ($j = 0; $j < count($arr); $j++)
158 {
159 foreach ($arr[$j] as $arr_hm => &$v)
160 {
161 if (($from < $arr_hm && $arr_hm < $to)
162 || ($arr_hm < $from && $from < $arr_hm + $service->duration + $service->sleep))
163 {
164 /**
165 * @todo evaluate to enhance this statement, because in case
166 * an appointment uses an increased duration, the exceeding
167 * slot are entirely turned off
168 */
169 // $v = 0;
170 }
171 }
172 }
173 }
174 }
175
176 $cont_people = 0;
177 }
178 }
179
180 $n_step = $service->duration + $service->sleep;
181
182 // array deep : elaborate each timeline
183 for ($level = 0; $level < count($arr); $level++)
184 {
185 // get all the times in the current timeline
186 $keys = array_keys($arr[$level] ?? []);
187 // insert the end working time to evaluate properly the last available time
188 $keys[] = $worktime[$level]->endts;
189
190 for ($i = 0; $i < count($keys) - 1; $i++)
191 {
192 $last_index = -1;
193
194 for ($j = $i + 1; $j < count($keys) && $last_index == -1; $j++)
195 {
196 /**
197 * If index is last or if current time is not available.
198 *
199 * @since 1.6 Use empty($arr[$level][$keys[$j]]) to avoid "Undefined Index" notices.
200 * These notices may be raised when the reservations were stored for certain
201 * times that don't exist anymore.
202 */
203 // if ($keys[$j] == count($keys) -1 || $arr[$level][$keys[$j]] == 0)
204 if ($keys[$j] == count($keys) -1 || empty($arr[$level][$keys[$j]]))
205 {
206 // store last index found and stop for statement
207 $last_index = $j;
208 }
209 }
210
211 // if subtraction of last index found with current index is not enough
212 if ($keys[$last_index] - $keys[$i] < $n_step)
213 {
214 // if current time is still available
215 if ($arr[$level][$keys[$i]] == 1)
216 {
217 // mark current time as no more available
218 $arr[$level][$keys[$i]] = 2;
219 }
220 }
221 }
222 }
223
224 $timeline = array();
225
226 foreach ($arr as $a)
227 {
228 foreach ($a as $hour => $val)
229 {
230 $timeline[$hour] = $val;
231 }
232 }
233
234 return $timeline;
235 }
236 }
237