PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / admin / models / makerecurrence.php
vikappointments / admin / models Last commit date
apiban.php 4 years ago apilog.php 2 years ago apiplugin.php 4 years ago apiuser.php 2 years ago apiuseroptions.php 2 years ago backup.php 4 months ago caldays.php 1 month ago calendar.php 1 month ago city.php 4 years ago closure.php 1 month ago configapp.php 4 years ago configcldays.php 4 years ago configcron.php 4 years ago configemp.php 4 years ago configsmsapi.php 4 years ago configuration.php 4 months ago conversion.php 4 years ago country.php 2 years ago coupon.php 2 years ago couponemployee.php 4 years ago coupongroup.php 2 years ago couponservice.php 4 years ago cronjob.php 2 years ago cronjoblog.php 4 years ago customer.php 4 months ago customf.php 2 years ago customfservice.php 4 years ago customizer.php 4 years ago empgroup.php 2 years ago employee.php 2 years ago empsettings.php 4 years ago file.php 4 years ago findreservation.php 1 month ago group.php 2 years ago import.php 4 years ago index.html 4 years ago invoice.php 1 month ago langcustomf.php 4 years ago langempgroup.php 4 years ago langemployee.php 4 years ago langgroup.php 4 years ago langmedia.php 4 years ago langoption.php 2 years ago langoptiongroup.php 4 years ago langoptionvar.php 4 years ago langpackage.php 4 years ago langpackgroup.php 4 years ago langpayment.php 4 years ago langservice.php 4 years ago langstatuscode.php 4 years ago langsubscr.php 4 years ago langtax.php 2 years ago langtaxrule.php 4 years ago location.php 2 years ago mailtext.php 2 years ago makerecurrence.php 1 month ago media.php 2 years ago multiorder.php 1 month ago option.php 1 year ago optiongroup.php 2 years ago optionvar.php 1 year ago orderstatus.php 2 years ago package.php 2 years ago packageservice.php 4 years ago packgroup.php 2 years ago packorder.php 2 years ago packorderitem.php 1 month ago payment.php 2 years ago rate.php 2 years ago reportsemp.php 4 months ago reportsser.php 4 months ago reservation.php 1 month ago resoptassoc.php 2 years ago restriction.php 2 years ago review.php 3 years ago serempassoc.php 1 year ago seroptassoc.php 4 years ago serrateassoc.php 4 years ago serrestrassoc.php 4 years ago service.php 2 years ago state.php 2 years ago statswidget.php 2 years ago statuscode.php 2 years ago subscription.php 1 month ago subscrorder.php 1 month ago tag.php 4 years ago tax.php 2 years ago taxrule.php 2 years ago updateprogram.php 4 years ago usernote.php 2 years ago waitinglist.php 1 month ago webhook.php 1 year ago worktime.php 1 month ago
makerecurrence.php
384 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.mvc.model');
15
16 /**
17 * VikAppointments make recurrence model.
18 *
19 * @since 1.7
20 */
21 class VikAppointmentsModelMakerecurrence extends JModelVAP
22 {
23 /**
24 * Returns an array containing all the (probable) appointments
25 * that match the specified recurrence instructions.
26 *
27 * @param string $datetime A date time string in military format.
28 * @param array $recurrence The recurrence instructions.
29 *
30 * @return array The resulting recurrence array.
31 */
32 public function getRecurrence($datetime, $recurrence)
33 {
34 if ($datetime instanceof JDate)
35 {
36 $start = $datetime;
37 }
38 else
39 {
40 // create initial date
41 $start = JFactory::getDate($datetime);
42 }
43
44 // create ending date
45 $end = clone $start;
46
47 if ($recurrence['for'] == 1)
48 {
49 // +[N] days
50 $end->modify('+' . abs($recurrence['amount']) . ' days');
51 }
52 else if ($recurrence['for'] == 2)
53 {
54 // +[N] weeks
55 $end->modify('+' . abs($recurrence['amount']) . ' weeks');
56 }
57 else if ($recurrence['for'] == 3)
58 {
59 // +[N] months
60 $end->modify('+' . abs($recurrence['amount']) . ' months');
61 }
62 else if ($recurrence['for'] == 4)
63 {
64 // +[N] months
65 $end->modify('+' . abs($recurrence['amount']) . ' years');
66 }
67
68 // fetch modifier role
69 if ($recurrence['by'] == 1)
70 {
71 $modifier = '+1 day';
72 }
73 else if ($recurrence['by'] == 2)
74 {
75 $modifier = '+1 week';
76 }
77 else if ($recurrence['by'] == 3)
78 {
79 $modifier = '+1 month';
80 }
81 else if ($recurrence['by'] == 4)
82 {
83 $modifier = '+2 weeks';
84 }
85 else if ($recurrence['by'] == 5)
86 {
87 $modifier = '+2 months';
88 }
89 else if ($recurrence['by'] == 6)
90 {
91 $modifier = '+3 months';
92 }
93 else if ($recurrence['by'] == 7)
94 {
95 $modifier = '+6 months';
96 }
97 else if ($recurrence['by'] == 8)
98 {
99 $modifier = '+1 year';
100 }
101
102 $result = array();
103
104 // iterate until the start date exceeds the end date
105 while ($start < $end)
106 {
107 // update date
108 $start->modify($modifier);
109
110 // register new date
111 $result[] = $start->format('Y-m-d H:i:s');
112 }
113
114 return $result;
115 }
116
117 /**
118 * Checks the availability for the specified appointment, but with the new
119 * date for recurrence.
120 *
121 * @param mixed $appointment The appointment details.
122 * @param string $date A date string in military format.
123 * @param array $force An associative array used to overwrite the
124 * default details of the appointment.
125 *
126 * @return boolean True if available, false otherwise.
127 */
128 public function checkAvailability($appointment, $date, $force = array())
129 {
130 // get reservation model
131 $reservation = JModelVAP::getInstance('reservation');
132
133 // prepare search data
134 if (is_object($appointment))
135 {
136 $data = array(
137 'id_service' => $appointment->service->id,
138 'id_employee' => $appointment->employee->id,
139 'duration' => $appointment->duration,
140 'sleep' => $appointment->sleep,
141 'people' => $appointment->people,
142 'checkin_ts' => $date,
143 );
144 }
145 else
146 {
147 // use array data as provided
148 $data = (array) $appointment;
149 }
150
151 // inject force data to override the appointment details
152 $data = array_merge($data, $force);
153
154 // check availability
155 if (!$reservation->isAvailable($data))
156 {
157 // propagate error message
158 $this->setError($reservation->getError());
159
160 return false;
161 }
162
163 // seems available
164 return true;
165 }
166
167 /**
168 * Checks the availability for the specified appointment, but with the new date
169 * for recurrence. Any available employee will be accepted here.
170 *
171 * @param mixed $appointment The appointment details.
172 * @param string $date A date string in military format.
173 * @param boolean $exclude True to exclude the validation for the employee
174 * already assigned to the appointment. Useful in
175 * case a first validation has been already made.
176 *
177 * @return array An associative array containing all the available employees.
178 */
179 public function checkRandomAvailability($appointment, $date, $exclude = true)
180 {
181 if (is_object($appointment))
182 {
183 // extract service and employee IDs from object
184 $id_service = $appointment->service->id;
185 $id_employee = $appointment->employee->id;
186 }
187 else
188 {
189 // extract service and employee IDs from array
190 $id_service = $appointment['id_service'];
191 $id_employee = $appointment['id_employee'];
192 }
193
194 // get service model
195 $service = JModelVAP::getInstance('service');
196 // get assigned employees
197 $employees = $service->getEmployees($id_service);
198
199 $results = array();
200
201 foreach ($employees as $emp)
202 {
203 if ($exclude && $emp->id == $id_employee)
204 {
205 // exclude the validation for the employee currently
206 // assigned to the appointment
207 continue;
208 }
209
210 // check availability for this new employee
211 if ($this->checkAvailability($appointment, $date, array('id_employee' => $emp->id)))
212 {
213 // register employee as available
214 $results[$emp->id] = $emp->nickname;
215 }
216 }
217
218 return $results;
219 }
220
221 /**
222 * Checks the availability for the specified appointment, but with the new date
223 * for recurrence. The system will search for any other available time slot for
224 * the current date and any other nearby date.
225 *
226 * @param mixed $appointment The appointment details.
227 * @param string $date A date string in military format.
228 * @param integer $next Indicates the maximum number of days after the
229 * default check-in for which we should go ahead.
230 * @param integer $prev Indicates the maximum number of days before the
231 * default check-in for which we should go behind.
232 *
233 * @return array An associative array containing all the available times.
234 */
235 public function checkNearbyAvailability($appointment, $date, $next = 1, $prev = 1)
236 {
237 // get reservation model
238 $reservation = JModelVAP::getInstance('reservation');
239
240 // create initial range date
241 $start = JFactory::getDate($date);
242 $start->modify('-' . abs($prev) . ' days');
243
244 // create ending range date
245 $end = JFactory::getDate($date);
246 $end->modify('+' . abs($next) . ' days');
247
248 $config = VAPFactory::getConfig();
249
250 $results = array();
251
252 // repeat until the initial date exceeds the ending date
253 while ($start <= $end)
254 {
255 // prepare search data
256 if (is_object($appointment))
257 {
258 $data = array(
259 'id_service' => $appointment->service->id,
260 'id_employee' => $appointment->employee->id,
261 'duration' => $appointment->duration,
262 'sleep' => $appointment->sleep,
263 'people' => $appointment->people,
264 'checkin_ts' => $start->format('Y-m-d H:i:s'),
265 );
266 }
267 else
268 {
269 // use array data as provided
270 $data = (array) $appointment;
271 }
272
273 // get available timeline for the current date
274 $timeline = $reservation->getAvailableTimes($data);
275
276 if ($timeline)
277 {
278 foreach ($timeline as $level)
279 {
280 foreach ($level as $time)
281 {
282 if ($time->isAvailable())
283 {
284 // military date as key
285 $k = $time->checkin('Y-m-d H:i:s', 'UTC');
286 // formatted date as value
287 $v = $time->checkin($config->get('dateformat') . ' ' . $config->get('timeformat'));
288
289 // register time within the list
290 $results[$k] = $v;
291 }
292 }
293 }
294 }
295
296 // go to next day
297 $start->modify('+1 day');
298 }
299
300 return $results;
301 }
302
303 /**
304 * Creates a recurrence for the given appointment according to the specified
305 * recurrence rules and specified hints.
306 *
307 * @param string $datetime A date time string in military format.
308 * @param array $recurrence The recurrence instructions.
309 * @param array $hints An associative array of hints that aim to fix
310 * the availability of those appointments that could
311 * not be booked with the default calculated check-in.
312 *
313 * @return integer The number of created appointments.
314 */
315 public function createRecurrence($appointment, array $recurrence, array $hints = array())
316 {
317 // create date by using the local timezone of the employee, because the DST
318 // might change over time
319 $tz = new DateTimeZone(JModelVAP::getInstance('employee')->getTimezone($appointment->employee->id));
320 $empDate = JFactory::getDate($appointment->checkin->utc);
321 $empDate->setTimezone($tz);
322
323 // compose dates recurrence
324 $arr = $this->getRecurrence($empDate, $recurrence);
325
326 if (!$arr)
327 {
328 // invalid recurrence
329 $this->setError(JText::translate('VAPMAKERECNOROWS'));
330
331 return 0;
332 }
333
334 // get reservation model
335 $reservation = JModelVAP::getInstance('reservation');
336
337 $count = 0;
338
339 // iterate all dates found
340 foreach ($arr as $date)
341 {
342 $src = array();
343
344 // use default date
345 $src['checkin_ts'] = $date;
346
347 // check if we have a hint for this date
348 if (isset($hints[$date]))
349 {
350 if (is_numeric($hints[$date]))
351 {
352 // numeric hint, a different employee was specified
353 $src['id_employee'] = $hints[$date];
354 }
355 else
356 {
357 // different check-in was specified
358 $src['checkin_ts'] = $hints[$date];
359 }
360 }
361
362 // check availability of the new appointment
363 $avail = $this->checkAvailability($appointment, $date, $src);
364
365 if (!$avail)
366 {
367 // still not available, skip date
368 continue;
369 }
370
371 // register a status to understand that the appointments has been created with recurrence
372 $src['status_comment'] = JText::sprintf('VAP_STATUS_CREATED_RECURRENCE', $appointment->id);
373
374 // duplicate appointment with new data
375 if ($reservation->duplicate(array($appointment->id), $src))
376 {
377 $count++;
378 }
379 }
380
381 return $count;
382 }
383 }
384