PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / admin / models / caldays.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 5 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 5 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 5 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 5 months ago reportsser.php 5 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
caldays.php
332 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 VAPLoader::import('libraries.calendar.wrapper');
16
17 /**
18 * VikAppointments weekly calendar model.
19 *
20 * @since 1.7
21 */
22 class VikAppointmentsModelCaldays extends JModelVAP
23 {
24 /**
25 * A lookup of services per employee.
26 *
27 * @var array
28 */
29 protected $_services = array();
30
31 /**
32 * Returns the structure of the weekly calendar to display.
33 *
34 * @param array $options An array of options.
35 *
36 * @return object The resulting calendar.
37 */
38 public function getCalendar(array $options = array())
39 {
40 if (!isset($options['layout']))
41 {
42 // use default layout if not specified
43 $options['layout'] = 'default';
44 }
45
46 if (!isset($options['date']) || VAPDateHelper::isNull($options['date']))
47 {
48 // get current day
49 $options['begin'] = JFactory::getDate('today 00:00:00')->toSql();
50 }
51 else
52 {
53 // get selected day (always treat as UTC to obtain the correct date at midnight)
54 $options['begin'] = VAPDateHelper::date2sql($options['date'], new DateTimeZone('UTC'));
55 }
56
57 $dt = JFactory::getDate($options['begin']);
58
59 if ($options['layout'] == 'day')
60 {
61 // get all reservations for the specified date
62 $dt->modify('23:59:59');
63 }
64 else
65 {
66 // get all reservations between the specified day and the next 7 days
67 $dt->modify('+7 days');
68 }
69
70 // fetch ending dates range
71 $options['end'] = $dt->toSql();
72
73 $calendar = new stdClass;
74 $calendar->start = $options['begin'];
75 $calendar->end = $options['end'];
76
77 // fetch appointments
78 $rows = $this->getAppointments($options);
79
80 if (!empty($options['layout']) && $options['layout'] == 'day')
81 {
82 // create a calendar wrapper for each employee
83 $calendar->employees = $this->groupReservationsByEmployee($rows);
84 }
85
86 // create a global calendar wrapper
87 $calendar->calendar = $this->groupReservations($rows);
88
89 return $calendar;
90 }
91
92 /**
93 * Returns a list of supported services.
94 *
95 * @param array $options An array of options.
96 *
97 * @return array An array of services.
98 */
99 public function getServices(array $options = array())
100 {
101 // fetch employee ID
102 $id_employee = !empty($options['employee']) ? (int) $options['employee'] : 0;
103
104 if (!isset($this->_services[$id_employee]))
105 {
106 // get services
107 $this->_services[$id_employee] = array();
108
109 $dbo = JFactory::getDbo();
110
111 $q = $dbo->getQuery(true)
112 ->select($dbo->qn(array('s.id', 's.name', 's.color', 's.id_group')))
113 ->from($dbo->qn('#__vikappointments_service', 's'))
114 ->leftjoin($dbo->qn('#__vikappointments_group', 'g') . ' ON ' . $dbo->qn('s.id_group') . ' = ' . $dbo->qn('g.id'))
115 ->where(1)
116 ->order($dbo->qn('g.ordering') . ' ASC')
117 ->order($dbo->qn('s.ordering') . ' ASC');
118
119 // if the employee is set, obtain only the related services
120
121 if ($id_employee)
122 {
123 $q->leftjoin($dbo->qn('#__vikappointments_ser_emp_assoc', 'a') . ' ON ' . $dbo->qn('s.id') . ' = ' . $dbo->qn('a.id_service'));
124 $q->where($dbo->qn('a.id_employee') . ' = ' . $id_employee);
125 }
126
127 $dbo->setQuery($q);
128 $list = $dbo->loadObjectList();
129
130 // check whether the first element of the list has no group
131 if ($list && $list[0]->id_group <= 0)
132 {
133 // check whether the last element of the list has a group
134 $last = end($list);
135
136 if ($last->id_group > 0)
137 {
138 // always move services without group at the end of the list
139 while ($list[0]->id_group <= 0)
140 {
141 $list[] = array_shift($list);
142 }
143 }
144 }
145
146 $this->_services[$id_employee] = $list;
147 }
148
149 // return cached services
150 return $this->_services[$id_employee];
151 }
152
153 /**
154 * Returns a calendar wrapper or an array of wrappers, depending
155 * on the provided options.
156 *
157 * @param array $options An array of options.
158 *
159 * @return mixed A calendar wrapper in case of weekly layout,
160 * an array of wrappers in case of daily layout.
161 */
162 public function getAppointments(array $options = array())
163 {
164 $dbo = JFactory::getDbo();
165
166 $q = $dbo->getQuery(true);
167
168 $q->select($dbo->qn(array(
169 'r.id',
170 'r.id_service',
171 'r.id_employee',
172 'r.checkin_ts',
173 'r.duration',
174 'r.sleep',
175 'r.people',
176 'r.total_cost',
177 'r.purchaser_nominative',
178 )));
179 $q->select($dbo->qn('e.nickname', 'employee_name'));
180 $q->select($dbo->qn('s.name', 'service_name'));
181 $q->select($dbo->qn('s.color', 'service_color'));
182
183 $q->from($dbo->qn('#__vikappointments_reservation', 'r'));
184 $q->leftjoin($dbo->qn('#__vikappointments_employee', 'e') . ' ON ' . $dbo->qn('e.id') . ' = ' . $dbo->qn('r.id_employee'));
185 $q->leftjoin($dbo->qn('#__vikappointments_service', 's') . ' ON ' . $dbo->qn('s.id') . ' = ' . $dbo->qn('r.id_service'));
186
187 // get all status codes that locks the appointments
188 $statuses = JHtml::fetch('vaphtml.status.find', 'code', array('appointments' => 1, 'reserved' => 1));
189
190 if ($statuses)
191 {
192 // take only the reserved appointments
193 $q->where($dbo->qn('r.status') . ' IN (' . implode(',', array_map(array($dbo, 'q'), $statuses)) . ')');
194 }
195
196 /**
197 * @todo consider to display the closures
198 */
199
200 // exclude closures
201 $q->where($dbo->qn('r.closure') . ' = 0');
202
203 // exclude parent orders
204 $q->where($dbo->qn('r.id_parent') . ' > 0');
205
206 if (!empty($options['employee']))
207 {
208 // filter by selected employee
209 $q->where($dbo->qn('r.id_employee') . ' = ' . (int) $options['employee']);
210 }
211
212 // get all supported services
213 $services = $this->getServices($options);
214
215 $options['services'] = !empty($options['services']) ? (array) $options['services'] : array();
216
217 // check whether we have at least a service and not all them are selected
218 if (($count = count($options['services'])) && $count != count($services))
219 {
220 // filter by selected services
221 $q->where($dbo->qn('r.id_service') . ' IN (' . implode(', ', $options['services']) . ')');
222 }
223
224 // check whether we should filter by employee
225 if (!empty($options['employees']))
226 {
227 // filter by selected services
228 $q->where($dbo->qn('r.id_employee') . ' IN (' . implode(', ', array_map('intval', (array) $options['employees'])) . ')');
229 }
230
231 // adjust dates range to UTC
232 $tz = JFactory::getUser()->getTimezone();
233 $begin = JFactory::getDate($options['begin'], $tz);
234 $end = JFactory::getDate($options['end'], $tz);
235
236 /**
237 * It is needed to intersect the delimiters with checkin and checkout in order
238 * to retrieve also the appointments that start on a day and ends on the next one.
239 */
240 $q->andWhere(array(
241 $dbo->qn('r.checkin_ts') . ' BETWEEN ' . $dbo->q($begin->toSql()) . ' AND ' . $dbo->q($end->toSql()),
242 sprintf(
243 'DATE_ADD(%s, INTERVAL (%s + %s) MINUTE) BETWEEN %s AND %s',
244 $dbo->qn('r.checkin_ts'),
245 $dbo->qn('r.duration'),
246 $dbo->qn('r.sleep'),
247 $dbo->q($begin->toSql()),
248 $dbo->q($end->toSql())
249 ),
250 ));
251
252 $q->order($dbo->qn('r.checkin_ts') . ' ASC');
253
254 $dbo->setQuery($q);
255 return $dbo->loadObjectList();
256 }
257
258 /**
259 * Groups the reservations depending on their checkin.
260 *
261 * @param array $rows The reservations to group.
262 *
263 * @return array The grouped reservations.
264 */
265 protected function groupReservations(array $rows)
266 {
267 $wrapper = new CalendarWrapper();
268
269 foreach ($rows as $row)
270 {
271 $row->checkout_ts = VikAppointments::getCheckout($row->checkin_ts, $row->duration);
272
273 $app = $wrapper->getIntersection($row->checkin_ts, $row->checkout_ts);
274
275 if ($app !== false)
276 {
277 $app->extendBounds($row->checkin_ts, $row->checkout_ts, $row);
278 }
279 else
280 {
281 $wrapper->push(new CalendarRect($row->checkin_ts, $row->checkout_ts, $row));
282 }
283 }
284
285 return $wrapper;
286 }
287
288 /**
289 * Groups the reservations by employee depending on their checkin.
290 *
291 * @param array $rows The reservations to group.
292 *
293 * @return array The grouped reservations.
294 *
295 * @uses groupReservations()
296 */
297 protected function groupReservationsByEmployee(array $rows)
298 {
299 $employees = array();
300
301 // create employees list
302 foreach (JHtml::fetch('vaphtml.admin.employees') as $emp)
303 {
304 $tmp = new stdClass;
305 $tmp->id = $emp->value;
306 $tmp->nickname = $emp->text;
307 $tmp->calendar = array();
308
309 $employees[$emp->value] = $tmp;
310 }
311
312 // group reservations by employee
313 foreach ($rows as $row)
314 {
315 $id_emp = $row->id_employee;
316
317 if (isset($employees[$id_emp]))
318 {
319 $employees[$id_emp]->calendar[] = $row;
320 }
321 }
322
323 foreach ($employees as $id_emp => $data)
324 {
325 // replace employee reservations with calendar wrapper
326 $employees[$id_emp]->calendar = $this->groupReservations($data->calendar);
327 }
328
329 return $employees;
330 }
331 }
332