PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
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 4 days ago employee.php 4 days ago group.php 4 days ago groupext.php 4 days ago index.html 4 days ago service.php 4 days ago
groupext.php
239 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 $arr[$i] = [];
46
47 for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
48 {
49 $arr[$i][$j] = 1;
50 }
51 }
52
53 // fetch default system timezone
54 $tz = JFactory::getApplication()->get('offset', 'UTC');
55
56 // re-format check-in date for correct comparison
57 $ymd = JFactory::getDate($date)->format('Y-m-d');
58
59 $cont_people = 0;
60
61 foreach ($bookings as $k => $b)
62 {
63 if (!$b->timezone)
64 {
65 // use default system timezone
66 $b->timezone = $tz;
67 }
68
69 // increase people count
70 $cont_people += $b->people_count;
71
72 if ($k == count($bookings) - 1 || $bookings[$k + 1]->checkin_ts != $b->checkin_ts || $bookings[$k + 1]->timezone != $b->timezone)
73 {
74 // create check-in and adjust it to the employee timezone
75 $checkin = JFactory::getDate($b->checkin_ts);
76 $checkin->setTimezone(new DateTimeZone($b->timezone));
77
78 // get check-in time adjusted to local timezone
79 $from = $checkin->format('H:i', $local = true);
80 // convert time string in minutes
81 $from = JHtml::fetch('vikappointments.time2min', $from);
82
83 /**
84 * Check the day factor of a reservation to check if it is referring
85 * to the current day or if it close to the bounds of this working time.
86 * Used to support midnight reservations.
87 *
88 * @since 1.6
89 */
90
91 if ($checkin->format('Y-m-d', $local = true) > $ymd)
92 {
93 // we are evaluating a reservation for the next day, so we need to increase the
94 // initial time by 1440 minutes (24 hours * 60).
95 $from += 1440;
96 }
97 else if ($checkin->format('Y-m-d', $local = true) < $ymd)
98 {
99 // we are evaluating a reservation for the previous day, so we need to decrease the
100 // initial time by 1440 minutes (24 hours * 60).
101 $from -= 1440;
102 }
103
104 // calculate end time by adding total duration to start time
105 $to = $from + $b->duration + $b->sleep;
106
107 for ($i = $from; $i < $to; $i += $min_int)
108 {
109 $found = false;
110
111 for ($j = 0; $j < count($arr) && !$found; $j++)
112 {
113 // calculate the number of occupied seats
114 $real_occupancy = $service->max_capacity - ($seats[$i] ?? $service->max_capacity) + $cont_people;
115
116 /**
117 * Try to block appointments that come from a different service or
118 * if the number of people exceeds the total capacity.
119 *
120 * @since 1.6 check if the services are different only if $arr[$j][$i] is set
121 */
122 if (!empty($arr[$j][$i]) && ($real_occupancy + $people > $service->max_capacity || $b->id_service != $service->id || $b->closure))
123 {
124 $found = true;
125 $arr[$j][$i] = 0;
126 }
127
128 if ($b->id_service == $service->id && !$b->closure)
129 {
130 if (!isset($seats[$i]))
131 {
132 $seats[$i] = $service->max_capacity;
133 }
134
135 // same service, we can display the remaining seats
136 $seats[$i] = max(0, $seats[$i] - $cont_people);
137 }
138 else
139 {
140 // booked for a different service, unset the remaining seats
141 $seats[$i] = 0;
142 }
143 }
144
145 /**
146 * We may have different services that display shifted
147 * timelines. This would cause an issue as previous check
148 * ignores the times that don't match the evaluated slots.
149 *
150 * We need to unset here all the times that intersect with
151 * an existing reservation, which might have been created for
152 * a different service.
153 *
154 * @since 1.6.2
155 */
156 if (!$found)
157 {
158 // find all slots that intersect this one
159 for ($j = 0; $j < count($arr); $j++)
160 {
161 foreach ($arr[$j] as $arr_hm => &$v)
162 {
163 if (($from < $arr_hm && $arr_hm < $to)
164 || ($arr_hm < $from && $from < $arr_hm + $service->duration + $service->sleep))
165 {
166 /**
167 * @todo evaluate to enhance this statement, because in case
168 * an appointment uses an increased duration, the exceeding
169 * slot are entirely turned off
170 */
171 // $v = 0;
172 }
173 }
174 }
175 }
176 }
177
178 $cont_people = 0;
179 }
180 }
181
182 $n_step = $service->duration + $service->sleep;
183
184 // array deep : elaborate each timeline
185 for ($level = 0; $level < count($arr); $level++)
186 {
187 // get all the times in the current timeline
188 $keys = array_keys($arr[$level] ?? []);
189 // insert the end working time to evaluate properly the last available time
190 $keys[] = $worktime[$level]->endts;
191
192 for ($i = 0; $i < count($keys) - 1; $i++)
193 {
194 $last_index = -1;
195
196 for ($j = $i + 1; $j < count($keys) && $last_index == -1; $j++)
197 {
198 /**
199 * If index is last or if current time is not available.
200 *
201 * @since 1.6 Use empty($arr[$level][$keys[$j]]) to avoid "Undefined Index" notices.
202 * These notices may be raised when the reservations were stored for certain
203 * times that don't exist anymore.
204 */
205 // if ($keys[$j] == count($keys) -1 || $arr[$level][$keys[$j]] == 0)
206 if ($keys[$j] == count($keys) -1 || empty($arr[$level][$keys[$j]]))
207 {
208 // store last index found and stop for statement
209 $last_index = $j;
210 }
211 }
212
213 // if subtraction of last index found with current index is not enough
214 if ($keys[$last_index] - $keys[$i] < $n_step)
215 {
216 // if current time is still available
217 if ($arr[$level][$keys[$i]] == 1)
218 {
219 // mark current time as no more available
220 $arr[$level][$keys[$i]] = 2;
221 }
222 }
223 }
224 }
225
226 $timeline = array();
227
228 foreach ($arr as $a)
229 {
230 foreach ($a as $hour => $val)
231 {
232 $timeline[$hour] = $val;
233 }
234 }
235
236 return $timeline;
237 }
238 }
239