appointment.php
3 days ago
empsubscr.php
3 days ago
index.html
3 days ago
package.php
3 days ago
subscr.php
3 days ago
appointment.php
1189 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.order.wrapper'); |
| 15 | |
| 16 | /** |
| 17 | * Appointments order class wrapper. |
| 18 | * |
| 19 | * @since 1.7 |
| 20 | */ |
| 21 | class VAPOrderAppointment extends VAPOrderWrapper |
| 22 | { |
| 23 | /** |
| 24 | * Class constructor. |
| 25 | * |
| 26 | * @param integer $id The order ID. |
| 27 | * @param mixed $langtag The language tag. If null, the default one will be used. |
| 28 | * @param array $options An array of options to be passed to the order instance. |
| 29 | */ |
| 30 | public function __construct($id, $langtag = null, array $options = array()) |
| 31 | { |
| 32 | // always force translation |
| 33 | $options['translate'] = true; |
| 34 | |
| 35 | // get current language tag |
| 36 | $default_lang = JFactory::getLanguage()->getTag(); |
| 37 | |
| 38 | // construct with parent |
| 39 | parent::__construct($id, $langtag, $options); |
| 40 | |
| 41 | // restore previous language according to the current cllient |
| 42 | VikAppointments::loadLanguage($default_lang); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * Helper method used to access all the user notes that belong to the |
| 47 | * specified appointment/order. |
| 48 | * |
| 49 | * @return array |
| 50 | */ |
| 51 | public function getUserNotes($id = null) |
| 52 | { |
| 53 | $notes = array(); |
| 54 | |
| 55 | foreach ($this->notes as $note) |
| 56 | { |
| 57 | // take only public notes |
| 58 | if ($note->status && (!$id || $id == $note->id_parent)) |
| 59 | { |
| 60 | $notes[] = $note; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | return $notes; |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Returns an array containing the requested information of the attendees. |
| 69 | * |
| 70 | * @param string $column The information to access (name, mail, phone). |
| 71 | * |
| 72 | * @return array An array of values. |
| 73 | */ |
| 74 | public function getAttendeesInfo($column = 'mail') |
| 75 | { |
| 76 | // fetch property name |
| 77 | switch ($column) |
| 78 | { |
| 79 | case 'name': |
| 80 | case 'nominative': |
| 81 | $column = 'purchaser_nominative'; |
| 82 | break; |
| 83 | |
| 84 | case 'mail': |
| 85 | case 'email': |
| 86 | $column = 'purchaser_mail'; |
| 87 | break; |
| 88 | |
| 89 | case 'tel': |
| 90 | case 'phone': |
| 91 | $column = 'purchaser_phone'; |
| 92 | break; |
| 93 | } |
| 94 | |
| 95 | $list = array(); |
| 96 | |
| 97 | // include author info, if existing |
| 98 | $list[] = $this->get($column, null); |
| 99 | |
| 100 | // iterate attendees |
| 101 | foreach ($this->attendees as $attendee) |
| 102 | { |
| 103 | // make sure the requested property exists |
| 104 | if (isset($attendee[$column])) |
| 105 | { |
| 106 | $list[] = $attendee[$column]; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // get rid of empty and duplicate values |
| 111 | return array_values(array_unique(array_filter($list))); |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * @override |
| 116 | * Returns the appointments order object. |
| 117 | * |
| 118 | * @param integer $id The order ID. |
| 119 | * @param mixed $langtag The language tag. If null, the default one will be used. |
| 120 | * @param array $options An array of options to be passed to the order instance. |
| 121 | * |
| 122 | * @return mixed The array/object to load. |
| 123 | * |
| 124 | * @throws Exception |
| 125 | */ |
| 126 | protected function load($id, $langtag = null, array $options = array()) |
| 127 | { |
| 128 | $dbo = JFactory::getDbo(); |
| 129 | $config = VAPFactory::getConfig(); |
| 130 | $dispatcher = VAPFactory::getEventDispatcher(); |
| 131 | |
| 132 | // create query |
| 133 | $q = $dbo->getQuery(true); |
| 134 | |
| 135 | // select all reservation columns |
| 136 | $q->select('r.*'); |
| 137 | $q->from($dbo->qn('#__vikappointments_reservation', 'r')); |
| 138 | |
| 139 | // select employee details |
| 140 | $q->select($dbo->qn('e.nickname', 'employee_name')); |
| 141 | $q->select($dbo->qn('e.email', 'employee_email')); |
| 142 | $q->select($dbo->qn('e.phone', 'employee_phone')); |
| 143 | $q->select($dbo->qn('e.notify', 'employee_notify')); |
| 144 | $q->select($dbo->qn('e.showemail', 'employee_replyto')); |
| 145 | $q->select($dbo->qn('e.image', 'employee_image')); |
| 146 | $q->select($dbo->qn('e.timezone')); |
| 147 | $q->leftjoin($dbo->qn('#__vikappointments_employee', 'e') . ' ON ' . $dbo->qn('r.id_employee') . ' = ' . $dbo->qn('e.id')); |
| 148 | |
| 149 | // select employee group details |
| 150 | $q->select($dbo->qn('ge.id', 'employee_group_id')); |
| 151 | $q->select($dbo->qn('ge.name', 'employee_group_name')); |
| 152 | $q->leftjoin($dbo->qn('#__vikappointments_employee_group', 'ge') . ' ON ' . $dbo->qn('e.id_group') . ' = ' . $dbo->qn('ge.id')); |
| 153 | |
| 154 | // select service details |
| 155 | $q->select($dbo->qn('s.name', 'service_name')); |
| 156 | $q->select($dbo->qn('s.description', 'service_description')); |
| 157 | $q->select($dbo->qn('s.image', 'service_image')); |
| 158 | $q->select($dbo->qn('s.max_capacity', 'service_max_capacity')); |
| 159 | $q->select($dbo->qn('s.choose_emp', 'service_choose_emp')); |
| 160 | $q->select($dbo->qn('s.attachments', 'service_attachments')); |
| 161 | $q->leftjoin($dbo->qn('#__vikappointments_service', 's') . ' ON ' . $dbo->qn('r.id_service') . ' = ' . $dbo->qn('s.id')); |
| 162 | |
| 163 | // select service group details |
| 164 | $q->select($dbo->qn('gs.id', 'service_group_id')); |
| 165 | $q->select($dbo->qn('gs.name', 'service_group_name')); |
| 166 | $q->leftjoin($dbo->qn('#__vikappointments_group', 'gs') . ' ON ' . $dbo->qn('s.id_group') . ' = ' . $dbo->qn('gs.id')); |
| 167 | |
| 168 | // select payment details |
| 169 | $q->select($dbo->qn('gp.name', 'payment_name')); |
| 170 | $q->select($dbo->qn('gp.file', 'payment_file')); |
| 171 | $q->select($dbo->qn('gp.note', 'payment_note')); |
| 172 | $q->select($dbo->qn('gp.prenote', 'payment_prenote')); |
| 173 | $q->select($dbo->qn('gp.icontype', 'payment_icontype')); |
| 174 | $q->select($dbo->qn('gp.icon', 'payment_icon')); |
| 175 | $q->leftjoin($dbo->qn('#__vikappointments_gpayments', 'gp') . ' ON ' . $dbo->qn('r.id_payment') . ' = ' . $dbo->qn('gp.id')); |
| 176 | |
| 177 | // select purchased options |
| 178 | $q->select($dbo->qn('oi.id', 'option_id')); |
| 179 | $q->select($dbo->qn('oi.id_option', 'option_id_option')); |
| 180 | $q->select($dbo->qn('oi.id_variation', 'option_id_variation')); |
| 181 | $q->select($dbo->qn('oi.quantity', 'option_quantity')); |
| 182 | $q->select($dbo->qn('oi.inc_price', 'option_price')); |
| 183 | $q->select($dbo->qn('oi.net', 'option_net')); |
| 184 | $q->select($dbo->qn('oi.tax', 'option_tax')); |
| 185 | $q->select($dbo->qn('oi.gross', 'option_gross')); |
| 186 | $q->select($dbo->qn('oi.discount', 'option_discount')); |
| 187 | $q->select($dbo->qn('oi.tax_breakdown', 'option_tax_breakdown')); |
| 188 | $q->select($dbo->qn('o.name', 'option_name')); |
| 189 | $q->select($dbo->qn('o.description', 'option_description')); |
| 190 | $q->select($dbo->qn('o.image', 'option_image')); |
| 191 | $q->select($dbo->qn('o.single', 'option_single')); |
| 192 | $q->select($dbo->qn('o.duration', 'option_duration')); |
| 193 | $q->select($dbo->qn('ov.inc_duration', 'option_var_duration')); |
| 194 | $q->leftjoin($dbo->qn('#__vikappointments_res_opt_assoc', 'oi') . ' ON ' . $dbo->qn('oi.id_reservation') . ' = ' . $dbo->qn('r.id')); |
| 195 | $q->leftjoin($dbo->qn('#__vikappointments_option', 'o') . ' ON ' . $dbo->qn('oi.id_option') . ' = ' . $dbo->qn('o.id')); |
| 196 | $q->leftjoin($dbo->qn('#__vikappointments_option_value', 'ov') . ' ON ' . $dbo->qn('oi.id_variation') . ' = ' . $dbo->qn('ov.id')); |
| 197 | |
| 198 | // exclude closures |
| 199 | $q->where($dbo->qn('r.closure') . ' = 0'); |
| 200 | |
| 201 | // filter by order key, if specified |
| 202 | if (isset($options['sid'])) |
| 203 | { |
| 204 | $q->where($dbo->qn('r.sid') . ' = ' . $dbo->q($options['sid'])); |
| 205 | } |
| 206 | |
| 207 | // load appointments matching the specified ID/parent |
| 208 | $q->andWhere(array( |
| 209 | $dbo->qn('r.id') . ' = ' . (int) $id, |
| 210 | $dbo->qn('r.id_parent') . ' = ' . (int) $id, |
| 211 | ), 'OR'); |
| 212 | |
| 213 | // filter by confirmation key, if specified |
| 214 | if (isset($options['conf_key'])) |
| 215 | { |
| 216 | $q->where($dbo->qn('r.conf_key') . ' = ' . $dbo->q($options['conf_key'])); |
| 217 | } |
| 218 | |
| 219 | $q->order($dbo->qn('r.id') . ' ASC'); |
| 220 | $q->order($dbo->qn('oi.id') . ' ASC'); |
| 221 | |
| 222 | /** |
| 223 | * External plugins can attach to this hook in order to manipulate |
| 224 | * the query at runtime, in example to alter the default ordering. |
| 225 | * |
| 226 | * @param mixed &$query A query builder instance. |
| 227 | * @param integer $id The ID of the order. |
| 228 | * @param mixed $langtag The language tag. If null, the default one will be used. |
| 229 | * @param array $options An array of options to be passed to the order instance. |
| 230 | * |
| 231 | * @return void |
| 232 | * |
| 233 | * @since 1.7 |
| 234 | */ |
| 235 | $dispatcher->trigger('onLoadAppointmentsOrderDetails', array(&$q, $id, $langtag, $options)); |
| 236 | |
| 237 | $dbo->setQuery($q); |
| 238 | $list = $dbo->loadObjectList(); |
| 239 | |
| 240 | if (!$list) |
| 241 | { |
| 242 | // order not found raise error |
| 243 | throw new Exception(sprintf('Order [%d] not found', $id), 404); |
| 244 | } |
| 245 | |
| 246 | if (!$langtag) |
| 247 | { |
| 248 | // use order lang tag in case it was not specified |
| 249 | $langtag = $list[0]->langtag; |
| 250 | |
| 251 | if (!$langtag) |
| 252 | { |
| 253 | // the order is not assigned to any lang tag, use the current one |
| 254 | $langtag = JFactory::getLanguage()->getTag(); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | |
| 259 | // Load the language adjusted to the specified one, because this method |
| 260 | // uses JText to translate some contents, such as the date formats. |
| 261 | // Otherwise, when sending a notification from the back-end, in case the |
| 262 | // language differs, the dates will be reported in a wrong locale. |
| 263 | VikAppointments::loadLanguage($langtag, JPATH_SITE); |
| 264 | |
| 265 | // get current user timezone |
| 266 | $tz = JFactory::getUser()->getTimezone()->getName(); |
| 267 | |
| 268 | // get system timezone |
| 269 | $system_tz = JFactory::getApplication()->get('offset', 'UTC'); |
| 270 | |
| 271 | if ($list[0]->user_timezone) |
| 272 | { |
| 273 | // use the timezone saved within the appointment record |
| 274 | $customer_tz = $list[0]->user_timezone; |
| 275 | } |
| 276 | else if ($list[0]->createdby > 0) |
| 277 | { |
| 278 | // Get customer timezone. |
| 279 | // Notice that it relies on the used ID of the author. This means that |
| 280 | // the timezone might not be correct in case the reservation has been |
| 281 | // created from the back-end. |
| 282 | $customer_tz = JFactory::getUser($list[0]->createdby)->getTimezone()->getName(); |
| 283 | } |
| 284 | else |
| 285 | { |
| 286 | // use system timezone |
| 287 | $customer_tz = $system_tz; |
| 288 | } |
| 289 | |
| 290 | // get working time model |
| 291 | $wdModel = JModelVAP::getInstance('worktime'); |
| 292 | // get location model |
| 293 | $locModel = JModelVAP::getInstance('location'); |
| 294 | |
| 295 | // create parent order details |
| 296 | $order = $list[0]; |
| 297 | |
| 298 | // register customer timezone |
| 299 | $order->customerTimezone = $customer_tz; |
| 300 | |
| 301 | $order->appointments = array(); |
| 302 | $order->sameEmp = array(); |
| 303 | |
| 304 | foreach ($list as $row) |
| 305 | { |
| 306 | if ($row->id_employee <= 0) |
| 307 | { |
| 308 | // we have a parent ID, skip it |
| 309 | continue; |
| 310 | } |
| 311 | |
| 312 | // check if we have already registered the appointment |
| 313 | if (!isset($order->appointments[$row->id])) |
| 314 | { |
| 315 | // create appointment object |
| 316 | $app = new stdClass; |
| 317 | $app->id = $row->id; |
| 318 | $app->sid = $row->sid; |
| 319 | |
| 320 | // create employee object |
| 321 | $app->employee = new stdClass; |
| 322 | $app->employee->id = $this->detach($row, 'id_employee'); |
| 323 | $app->employee->name = $this->detach($row, 'employee_name'); |
| 324 | $app->employee->email = $this->detach($row, 'employee_email'); |
| 325 | $app->employee->phone = $this->detach($row, 'employee_phone'); |
| 326 | $app->employee->image = $this->detach($row, 'employee_image'); |
| 327 | $app->employee->notify = $this->detach($row, 'employee_notify'); |
| 328 | $app->employee->replyto = (bool) $this->detach($row, 'employee_replyto'); |
| 329 | |
| 330 | $order->sameEmp[] = $app->employee->id; |
| 331 | |
| 332 | // create employee group object |
| 333 | if ($row->employee_group_id) |
| 334 | { |
| 335 | $app->employee->group = new stdClass; |
| 336 | $app->employee->group->id = $this->detach($row, 'employee_group_id'); |
| 337 | $app->employee->group->name = $this->detach($row, 'employee_group_name'); |
| 338 | } |
| 339 | else |
| 340 | { |
| 341 | $app->employee->group = null; |
| 342 | } |
| 343 | |
| 344 | // create service object |
| 345 | $app->service = new stdClass; |
| 346 | $app->service->id = $this->detach($row, 'id_service'); |
| 347 | $app->service->name = $this->detach($row, 'service_name'); |
| 348 | $app->service->description = $this->detach($row, 'service_description'); |
| 349 | $app->service->price = $this->detach($row, 'service_price'); |
| 350 | $app->service->image = $this->detach($row, 'service_image'); |
| 351 | $app->service->maxCapacity = $this->detach($row, 'service_max_capacity'); |
| 352 | $app->service->chooseEmployee = $this->detach($row, 'service_choose_emp'); |
| 353 | $app->service->attachments = $this->detach($row, 'service_attachments'); |
| 354 | // decode attachments |
| 355 | $app->service->attachments = $app->service->attachments ? (array) json_decode($app->service->attachments, true) : array(); |
| 356 | |
| 357 | // create service group object |
| 358 | if ($row->service_group_id) |
| 359 | { |
| 360 | $app->service->group = new stdClass; |
| 361 | $app->service->group->id = $this->detach($row, 'service_group_id'); |
| 362 | $app->service->group->name = $this->detach($row, 'service_group_name'); |
| 363 | } |
| 364 | else |
| 365 | { |
| 366 | $app->service->group = null; |
| 367 | } |
| 368 | |
| 369 | // create check-in object relative to current user timezone |
| 370 | $app->checkin = new stdClass; |
| 371 | $app->checkin->lc = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat')); |
| 372 | $app->checkin->lc2 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC2')); |
| 373 | $app->checkin->lc3 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat')); |
| 374 | $app->checkin->date = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC5')); |
| 375 | $app->checkin->utc = $row->checkin_ts; |
| 376 | // register ISO 8601 date format (Y-m-dTH:i:sZ) |
| 377 | $app->checkin->iso8601 = JFactory::getDate($row->checkin_ts)->toISO8601(); |
| 378 | // register check-in UNIX timestamp (UTC) |
| 379 | $app->checkin->timestamp = JFactory::getDate($row->checkin_ts)->getTimestamp(); |
| 380 | // register check-in timezone |
| 381 | $app->checkin->timezone = $tz; |
| 382 | |
| 383 | $checkout = VikAppointments::getCheckout($row->checkin_ts, $row->duration); |
| 384 | |
| 385 | // create check-out object relative to current user timezone |
| 386 | $app->checkout = new stdClass; |
| 387 | $app->checkout->lc = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat')); |
| 388 | $app->checkout->lc2 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC2')); |
| 389 | $app->checkout->lc3 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat')); |
| 390 | $app->checkout->date = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC5')); |
| 391 | $app->checkout->utc = $checkout; |
| 392 | // register ISO 8601 date format (Y-m-dTH:i:sZ) |
| 393 | $app->checkout->iso8601 = JFactory::getDate($checkout)->toISO8601(); |
| 394 | // register check-out timezone |
| 395 | $app->checkout->timezone = $tz; |
| 396 | |
| 397 | if (!$row->timezone) |
| 398 | { |
| 399 | // use system timezone for employees that do not specify it |
| 400 | $row->timezone = $system_tz; |
| 401 | } |
| 402 | |
| 403 | // create check-in object relative to employee timezone |
| 404 | $app->employee->checkin = new stdClass; |
| 405 | $app->employee->checkin->lc = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat'), $row->timezone); |
| 406 | $app->employee->checkin->lc2 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC2'), $row->timezone); |
| 407 | $app->employee->checkin->lc3 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat'), $row->timezone); |
| 408 | $app->employee->checkin->date = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC5'), $row->timezone); |
| 409 | // register check-in timezone |
| 410 | $app->employee->checkin->timezone = $row->timezone; |
| 411 | |
| 412 | // create check-out object relative to employee timezone |
| 413 | $app->employee->checkout = new stdClass; |
| 414 | $app->employee->checkout->lc = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat'), $row->timezone); |
| 415 | $app->employee->checkout->lc2 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC2'), $row->timezone); |
| 416 | $app->employee->checkout->lc3 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat'), $row->timezone); |
| 417 | $app->employee->checkout->date = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC5'), $row->timezone); |
| 418 | // register check-out timezone |
| 419 | $app->employee->checkout->timezone = $row->timezone; |
| 420 | |
| 421 | // create check-in object relative to customer timezone |
| 422 | $app->customerCheckin = new stdClass; |
| 423 | $app->customerCheckin->lc = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat'), $customer_tz); |
| 424 | $app->customerCheckin->lc2 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC2'), $customer_tz); |
| 425 | $app->customerCheckin->lc3 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat'), $customer_tz); |
| 426 | $app->customerCheckin->date = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC5'), $customer_tz); |
| 427 | // register check-in timezone |
| 428 | $app->customerCheckin->timezone = $customer_tz; |
| 429 | |
| 430 | // create check-out object relative to customer timezone |
| 431 | $app->customerCheckout = new stdClass; |
| 432 | $app->customerCheckout->lc = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat'), $customer_tz); |
| 433 | $app->customerCheckout->lc2 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC2'), $customer_tz); |
| 434 | $app->customerCheckout->lc3 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat'), $customer_tz); |
| 435 | $app->customerCheckout->date = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC5'), $customer_tz); |
| 436 | // register check-out timezone |
| 437 | $app->customerCheckout->timezone = $customer_tz; |
| 438 | |
| 439 | // create check-in object relative to system timezone |
| 440 | $app->systemCheckin = new stdClass; |
| 441 | $app->systemCheckin->lc = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat'), $system_tz); |
| 442 | $app->systemCheckin->lc2 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC2'), $system_tz); |
| 443 | $app->systemCheckin->lc3 = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat'), $system_tz); |
| 444 | $app->systemCheckin->date = JHtml::fetch('date', $row->checkin_ts, JText::translate('DATE_FORMAT_LC5'), $system_tz); |
| 445 | // register check-in timezone |
| 446 | $app->systemCheckin->timezone = $system_tz; |
| 447 | |
| 448 | // create check-out object relative to system timezone |
| 449 | $app->systemCheckout = new stdClass; |
| 450 | $app->systemCheckout->lc = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC1') . ' ' . $config->get('timeformat'), $system_tz); |
| 451 | $app->systemCheckout->lc2 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC2'), $system_tz); |
| 452 | $app->systemCheckout->lc3 = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC3') . ' ' . $config->get('timeformat'), $system_tz); |
| 453 | $app->systemCheckout->date = JHtml::fetch('date', $checkout, JText::translate('DATE_FORMAT_LC5'), $system_tz); |
| 454 | // register check-out timezone |
| 455 | $app->systemCheckout->timezone = $system_tz; |
| 456 | |
| 457 | // inject appointment details |
| 458 | $app->duration = $this->detach($row, 'duration'); |
| 459 | $app->sleep = $this->detach($row, 'sleep'); |
| 460 | $app->people = $this->detach($row, 'people'); |
| 461 | $app->viewEmp = $this->detach($row, 'view_emp'); |
| 462 | $app->status = $row->status; |
| 463 | |
| 464 | $app->statusRole = null; |
| 465 | |
| 466 | // fetch status role |
| 467 | if (JHtml::fetch('vaphtml.status.ispending', 'appointments', $app->status)) |
| 468 | { |
| 469 | $app->statusRole = 'PENDING'; |
| 470 | } |
| 471 | else if (JHtml::fetch('vaphtml.status.isapproved', 'appointments', $app->status)) |
| 472 | { |
| 473 | $app->statusRole = 'APPROVED'; |
| 474 | } |
| 475 | else if (JHtml::fetch('vaphtml.status.isremoved', 'appointments', $app->status)) |
| 476 | { |
| 477 | $app->statusRole = 'EXPIRED'; |
| 478 | } |
| 479 | else if (JHtml::fetch('vaphtml.status.iscancelled', 'appointments', $app->status)) |
| 480 | { |
| 481 | $app->statusRole = 'CANCELLED'; |
| 482 | } |
| 483 | |
| 484 | // create appointment sub-totals |
| 485 | $app->totals = new stdClass; |
| 486 | $app->totals->net = $this->detach($row, 'service_net'); |
| 487 | $app->totals->tax = $this->detach($row, 'service_tax'); |
| 488 | $app->totals->gross = $this->detach($row, 'service_gross'); |
| 489 | $app->totals->grossOpt = $app->totals->gross; |
| 490 | $app->totals->discount = $this->detach($row, 'service_discount'); |
| 491 | $app->totals->breakdown = $row->tax_breakdown ? json_decode($this->detach($row, 'tax_breakdown')) : null; |
| 492 | |
| 493 | // find location of the appointment |
| 494 | $id_location = $wdModel->getLocation($app->checkin->utc, $app->service->id, $app->employee->id); |
| 495 | // get location details |
| 496 | $app->location = $locModel->getInfo($id_location); |
| 497 | |
| 498 | // init options array |
| 499 | $app->options = array(); |
| 500 | |
| 501 | // register appointment |
| 502 | $order->appointments[$row->id] = $app; |
| 503 | } |
| 504 | |
| 505 | // check if we have an option to register |
| 506 | if (!empty($row->option_id)) |
| 507 | { |
| 508 | // create option |
| 509 | $option = new stdClass; |
| 510 | $option->id_assoc = $this->detach($row, 'option_id'); |
| 511 | $option->id = $this->detach($row, 'option_id_option'); |
| 512 | $option->id_variation = $this->detach($row, 'option_id_variation'); |
| 513 | $option->name = $this->detach($row, 'option_name'); |
| 514 | $option->varName = $this->detach($row, 'option_var_name'); |
| 515 | $option->fullName = $option->name . ($option->varName ? ' - ' . $option->varName : ''); |
| 516 | $option->description = $this->detach($row, 'option_description'); |
| 517 | $option->image = $this->detach($row, 'option_image'); |
| 518 | $option->multiple = $this->detach($row, 'option_single'); |
| 519 | $option->quantity = $this->detach($row, 'option_quantity'); |
| 520 | $option->price = $this->detach($row, 'option_price'); |
| 521 | |
| 522 | // create option totals |
| 523 | $option->totals = new stdClass; |
| 524 | $option->totals->net = $this->detach($row, 'option_net'); |
| 525 | $option->totals->tax = $this->detach($row, 'option_tax'); |
| 526 | $option->totals->gross = $this->detach($row, 'option_gross'); |
| 527 | $option->totals->discount = $this->detach($row, 'option_discount'); |
| 528 | $option->totals->breakdown = $row->option_tax_breakdown ? json_decode($this->detach($row, 'option_tax_breakdown')) : null; |
| 529 | |
| 530 | /** |
| 531 | * Include information about the extra duration provided by the option, if configured. |
| 532 | * |
| 533 | * @since 1.7.7 |
| 534 | */ |
| 535 | if ($duration = (int) $this->detach($row, 'option_duration')) |
| 536 | { |
| 537 | $option->duration = new stdClass; |
| 538 | $option->duration->extraUnit = $duration + ($this->detach($row, 'option_var_duration') ?: 0); |
| 539 | $option->duration->extraTotal = $option->duration->extraUnit * $option->quantity; |
| 540 | } |
| 541 | else |
| 542 | { |
| 543 | $option->duration = null; |
| 544 | } |
| 545 | |
| 546 | // register option within appointment record |
| 547 | $order->appointments[$row->id]->options[] = $option; |
| 548 | |
| 549 | // increase appointment gross by the option cost |
| 550 | $order->appointments[$row->id]->totals->grossOpt += $option->totals->gross; |
| 551 | } |
| 552 | } |
| 553 | |
| 554 | // reset array keys |
| 555 | $order->appointments = array_values($order->appointments); |
| 556 | |
| 557 | // fetch coupon |
| 558 | if ($order->coupon_str) |
| 559 | { |
| 560 | list($code, $type, $amount) = explode(';;', $this->detach($order, 'coupon_str')); |
| 561 | |
| 562 | $order->coupon = new stdClass; |
| 563 | $order->coupon->code = $code; |
| 564 | $order->coupon->amount = $amount; |
| 565 | $order->coupon->type = $type; |
| 566 | } |
| 567 | else |
| 568 | { |
| 569 | $order->coupon = null; |
| 570 | $this->detach($order, 'coupon_str'); |
| 571 | } |
| 572 | |
| 573 | // decode stored CF data |
| 574 | $order->custom_f = $order->custom_f ? (array) json_decode($order->custom_f, true) : []; |
| 575 | $order->fields = $order->custom_f; |
| 576 | $order->displayFields = $order->fields; |
| 577 | |
| 578 | $vars = array_values($order->fields); |
| 579 | $vars = array_filter($vars, function($elem) |
| 580 | { |
| 581 | if (is_array($elem)) |
| 582 | { |
| 583 | return (bool) $elem; |
| 584 | } |
| 585 | |
| 586 | return strlen($elem); |
| 587 | }); |
| 588 | |
| 589 | $order->hasFields = (bool) $vars; |
| 590 | |
| 591 | // decode uploads |
| 592 | $order->uploads = json_decode($order->uploads); |
| 593 | |
| 594 | // decode attendees |
| 595 | $order->attendees = $order->attendees ? (array) json_decode($order->attendees, true) : array(); |
| 596 | |
| 597 | /** |
| 598 | * Get rid of records that do not specify any details for the attendee. |
| 599 | * |
| 600 | * @since 1.7.1 |
| 601 | */ |
| 602 | $order->attendees = array_filter($order->attendees, function($attendee) |
| 603 | { |
| 604 | if (!empty($attendee['fields'])) |
| 605 | { |
| 606 | foreach ((array) $attendee['fields'] as $v) |
| 607 | { |
| 608 | if (strlen($v)) |
| 609 | { |
| 610 | return true; |
| 611 | } |
| 612 | } |
| 613 | } |
| 614 | |
| 615 | if (!empty($attendee['uploads'])) |
| 616 | { |
| 617 | foreach ((array) $attendee['uploads'] as $v) |
| 618 | { |
| 619 | if (strlen($v)) |
| 620 | { |
| 621 | return true; |
| 622 | } |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | return false; |
| 627 | }); |
| 628 | |
| 629 | // fetch payment data |
| 630 | if ($order->payment_file) |
| 631 | { |
| 632 | $order->payment = new stdClass; |
| 633 | $order->payment->id = $this->detach($order, 'id_payment'); |
| 634 | $order->payment->name = $this->detach($order, 'payment_name'); |
| 635 | $order->payment->driver = $this->detach($order, 'payment_file'); |
| 636 | $order->payment->iconType = $this->detach($order, 'payment_icontype'); |
| 637 | $order->payment->icon = $this->detach($order, 'payment_icon'); |
| 638 | |
| 639 | if ($order->payment->iconType == 1) |
| 640 | { |
| 641 | // Font Icon |
| 642 | $order->payment->fontIcon = $order->payment->icon; |
| 643 | } |
| 644 | else |
| 645 | { |
| 646 | // Image Icon |
| 647 | $order->payment->iconURI = JUri::root() . $order->payment->icon; |
| 648 | |
| 649 | // fetch Font Icon based on payment driver |
| 650 | switch ($order->payment->driver) |
| 651 | { |
| 652 | case 'bank_transfer.php': |
| 653 | $order->payment->fontIcon = 'fas fa-money-bill'; |
| 654 | break; |
| 655 | |
| 656 | case 'paypal.php': |
| 657 | $order->payment->fontIcon = 'fab fa-paypal'; |
| 658 | break; |
| 659 | |
| 660 | default: |
| 661 | $order->payment->fontIcon = 'fas fa-credit-card'; |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | $order->payment->notes = new stdClass; |
| 666 | $order->payment->notes->beforePurchase = $this->detach($order, 'payment_prenote'); |
| 667 | $order->payment->notes->afterPurchase = $this->detach($order, 'payment_note'); |
| 668 | } |
| 669 | else |
| 670 | { |
| 671 | $order->payment = null; |
| 672 | } |
| 673 | |
| 674 | // setup totals |
| 675 | $order->totals = new stdClass; |
| 676 | $order->totals->net = $this->detach($order, 'total_net'); |
| 677 | $order->totals->tax = $this->detach($order, 'total_tax'); |
| 678 | $order->totals->gross = $this->detach($order, 'total_cost'); |
| 679 | $order->totals->deposit = $this->detach($order, 'deposit'); |
| 680 | $order->totals->discount = $this->detach($order, 'discount'); |
| 681 | $order->totals->paid = $this->detach($order, 'tot_paid'); |
| 682 | $order->totals->payCharge = $this->detach($order, 'payment_charge'); |
| 683 | $order->totals->payTax = $this->detach($order, 'payment_tax'); |
| 684 | $order->totals->due = $order->totals->gross - $order->totals->paid; |
| 685 | |
| 686 | if (!$order->paid) |
| 687 | { |
| 688 | // fetch paid flag based on current order status |
| 689 | $order->paid = JHtml::fetch('vaphtml.status.ispaid', 'appointments', $order->status); |
| 690 | } |
| 691 | |
| 692 | if ($order->paid) |
| 693 | { |
| 694 | // amount paid, no remaining balance |
| 695 | $order->totals->due = 0; |
| 696 | } |
| 697 | |
| 698 | $order->statusRole = null; |
| 699 | |
| 700 | // fetch status role |
| 701 | if (JHtml::fetch('vaphtml.status.ispending', 'appointments', $order->status)) |
| 702 | { |
| 703 | $order->statusRole = 'PENDING'; |
| 704 | } |
| 705 | else if (JHtml::fetch('vaphtml.status.isapproved', 'appointments', $order->status)) |
| 706 | { |
| 707 | $order->statusRole = 'APPROVED'; |
| 708 | } |
| 709 | else if (JHtml::fetch('vaphtml.status.isremoved', 'appointments', $order->status)) |
| 710 | { |
| 711 | $order->statusRole = 'EXPIRED'; |
| 712 | } |
| 713 | else if (JHtml::fetch('vaphtml.status.iscancelled', 'appointments', $order->status)) |
| 714 | { |
| 715 | $order->statusRole = 'CANCELLED'; |
| 716 | } |
| 717 | |
| 718 | // flag used to check whether all the appointments have been |
| 719 | // booked for the same employee |
| 720 | $order->sameEmp = count(array_unique($order->sameEmp)) == 1; |
| 721 | |
| 722 | /** |
| 723 | * External plugins can use this event to manipulate the object holding |
| 724 | * the details of the order. Useful to inject all the additional data |
| 725 | * fetched with the manipulation of the query. |
| 726 | * |
| 727 | * @param mixed $order The order details object. |
| 728 | * @param array $list The query resulting array. |
| 729 | * |
| 730 | * @return void |
| 731 | */ |
| 732 | $dispatcher->trigger('onSetupAppointmentsOrderDetails', array($order, $list)); |
| 733 | |
| 734 | $unsetList = array( |
| 735 | 'id_service', |
| 736 | 'id_employee', |
| 737 | 'checkin_ts', |
| 738 | 'duration', |
| 739 | 'sleep', |
| 740 | 'people', |
| 741 | 'tax_breakdown', |
| 742 | 'timezone', |
| 743 | 'view_emp', |
| 744 | // unset notes because they are loaded through the getNotes() method |
| 745 | 'notes', |
| 746 | ); |
| 747 | |
| 748 | // get rid of not needed properties |
| 749 | foreach (get_object_vars($order) as $k => $v) |
| 750 | { |
| 751 | if (preg_match("/^(employee|service|option|payment)_/", $k)) |
| 752 | { |
| 753 | // the property is probably related to the appointment |
| 754 | // or it results blank (so it was not parsed) |
| 755 | unset($order->{$k}); |
| 756 | } |
| 757 | else if (preg_match("/^__/", $k)) |
| 758 | { |
| 759 | // remove deprecated (back-up) property |
| 760 | unset($order->{$k}); |
| 761 | } |
| 762 | else if (in_array($k, $unsetList)) |
| 763 | { |
| 764 | // remove property if contained in the list |
| 765 | unset($order->{$k}); |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | return $order; |
| 770 | } |
| 771 | |
| 772 | /** |
| 773 | * @override |
| 774 | * Translates the internal properties. |
| 775 | * |
| 776 | * @param mixed $langtag The language tag. If null, the default one will be used. |
| 777 | * |
| 778 | * @return void |
| 779 | */ |
| 780 | protected function translate($langtag = null) |
| 781 | { |
| 782 | $dispatcher = VAPFactory::getEventDispatcher(); |
| 783 | |
| 784 | if (!$langtag) |
| 785 | { |
| 786 | // use order lang tag in case it was not specified |
| 787 | $langtag = $this->get('langtag', null); |
| 788 | |
| 789 | if (!$langtag) |
| 790 | { |
| 791 | // the order is not assigned to any lang tag, use the current one |
| 792 | $langtag = JFactory::getLanguage()->getTag(); |
| 793 | } |
| 794 | } |
| 795 | |
| 796 | // load front-end language |
| 797 | VikAppointments::loadLanguage($langtag, JPATH_SITE); |
| 798 | |
| 799 | // get translator |
| 800 | $translator = VAPFactory::getTranslator(); |
| 801 | |
| 802 | $service_ids = array(); |
| 803 | $ser_group_ids = array(); |
| 804 | $emp_group_ids = array(); |
| 805 | $employee_ids = array(); |
| 806 | $option_ids = array(); |
| 807 | $optvar_ids = array(); |
| 808 | |
| 809 | foreach ($this->appointments as $app) |
| 810 | { |
| 811 | $service_ids[] = $app->service->id; |
| 812 | $employee_ids[] = $app->employee->id; |
| 813 | |
| 814 | if ($app->service->group) |
| 815 | { |
| 816 | $ser_group_ids[] = $app->service->group->id; |
| 817 | } |
| 818 | |
| 819 | if ($app->employee->group) |
| 820 | { |
| 821 | $emp_group_ids[] = $app->employee->group->id; |
| 822 | } |
| 823 | |
| 824 | foreach ($app->options as $opt) |
| 825 | { |
| 826 | $option_ids[] = $opt->id; |
| 827 | |
| 828 | if ($opt->id_variation > 0) |
| 829 | { |
| 830 | $optvar_ids[] = $opt->id_variation; |
| 831 | } |
| 832 | } |
| 833 | } |
| 834 | |
| 835 | // pre-load services translations |
| 836 | $serLang = $translator->load('service', array_unique($service_ids), $langtag); |
| 837 | // pre-load services groups translations |
| 838 | $serGroupLang = $translator->load('group', array_unique($ser_group_ids), $langtag); |
| 839 | // pre-load employees translations |
| 840 | $empLang = $translator->load('employee', array_unique($employee_ids), $langtag); |
| 841 | // pre-load employees groups translations |
| 842 | $empGroupLang = $translator->load('empgroup', array_unique($emp_group_ids), $langtag); |
| 843 | // pre-load options translations |
| 844 | $optLang = $translator->load('option', array_unique($option_ids), $langtag); |
| 845 | // pre-load options variations translations |
| 846 | $varLang = $translator->load('optionvar', array_unique($optvar_ids), $langtag); |
| 847 | |
| 848 | // iterate appointments and apply translationss |
| 849 | foreach ($this->appointments as $k => $app) |
| 850 | { |
| 851 | // translate service name for the given language |
| 852 | $ser_tx = $serLang->getTranslation($app->service->id, $langtag); |
| 853 | |
| 854 | if ($ser_tx) |
| 855 | { |
| 856 | $app->service->name = $ser_tx->name; |
| 857 | $app->service->description = $ser_tx->description; |
| 858 | } |
| 859 | |
| 860 | // translate employee name for the given language |
| 861 | $emp_tx = $empLang->getTranslation($app->employee->id, $langtag); |
| 862 | |
| 863 | if ($emp_tx) |
| 864 | { |
| 865 | $app->employee->name = $emp_tx->nickname; |
| 866 | } |
| 867 | |
| 868 | if ($app->service->group) |
| 869 | { |
| 870 | // translate service group for the given language |
| 871 | $grp_tx = $serGroupLang->getTranslation($app->service->group->id, $langtag); |
| 872 | |
| 873 | if ($grp_tx) |
| 874 | { |
| 875 | $app->service->group->name = $grp_tx->name; |
| 876 | } |
| 877 | } |
| 878 | |
| 879 | if ($app->employee->group) |
| 880 | { |
| 881 | // translate employee group for the given language |
| 882 | $grp_tx = $empGroupLang->getTranslation($app->employee->group->id, $langtag); |
| 883 | |
| 884 | if ($grp_tx) |
| 885 | { |
| 886 | $app->employee->group->name = $grp_tx->name; |
| 887 | } |
| 888 | } |
| 889 | |
| 890 | foreach ($app->options as $j => $opt) |
| 891 | { |
| 892 | // translate option name for the given language |
| 893 | $opt_tx = $optLang->getTranslation($opt->id, $langtag); |
| 894 | |
| 895 | if ($opt_tx) |
| 896 | { |
| 897 | $opt->name = $opt_tx->name; |
| 898 | } |
| 899 | |
| 900 | if ($opt->id_variation > 0) |
| 901 | { |
| 902 | // translate variation name for the given language |
| 903 | $var_tx = $varLang->getTranslation($opt->id_variation, $langtag); |
| 904 | |
| 905 | if ($var_tx) |
| 906 | { |
| 907 | $opt->varName = $var_tx->name; |
| 908 | } |
| 909 | } |
| 910 | |
| 911 | $opt->fullName = $opt->name . ($opt->varName ? ' - ' . $opt->varName : ''); |
| 912 | |
| 913 | // update option |
| 914 | $app->options[$j] = $opt; |
| 915 | } |
| 916 | |
| 917 | // update appointment |
| 918 | $this->appointments[$k] = $app; |
| 919 | } |
| 920 | |
| 921 | // translate payment if specified |
| 922 | if ($this->payment) |
| 923 | { |
| 924 | // get payment translation |
| 925 | $pay_tx = $translator->translate('payment', $this->payment->id, $langtag); |
| 926 | |
| 927 | if ($pay_tx) |
| 928 | { |
| 929 | // inject translation within order details |
| 930 | $this->payment->name = $pay_tx->name; |
| 931 | $this->payment->notes->beforePurchase = $pay_tx->prenote; |
| 932 | $this->payment->notes->afterPurchase = $pay_tx->note; |
| 933 | } |
| 934 | } |
| 935 | |
| 936 | // import custom fields loader |
| 937 | VAPLoader::import('libraries.customfields.loader'); |
| 938 | |
| 939 | // get relevant custom fields only |
| 940 | $fields = VAPCustomFieldsLoader::getInstance() |
| 941 | ->translate($langtag) |
| 942 | ->noRequiredCheckbox() |
| 943 | ->noSeparator() |
| 944 | ->forService($service_ids); |
| 945 | |
| 946 | if (count(array_unique($employee_ids)) == 1) |
| 947 | { |
| 948 | $fields->ofEmployee($employee_ids[0]); |
| 949 | } |
| 950 | |
| 951 | $cf = $fields->fetch(); |
| 952 | |
| 953 | // translate CF data object |
| 954 | $this->fields = VAPCustomFieldsLoader::translateObject($this->fields, $cf, $langtag); |
| 955 | |
| 956 | // reset display fields |
| 957 | $this->displayFields = array(); |
| 958 | |
| 959 | foreach ($cf as $field) |
| 960 | { |
| 961 | $k = $field['name']; |
| 962 | |
| 963 | if (!array_key_exists($k, $this->fields)) |
| 964 | { |
| 965 | // field not found inside the given object, go to next one |
| 966 | continue; |
| 967 | } |
| 968 | |
| 969 | $v = $this->fields[$k]; |
| 970 | |
| 971 | // take only if the value is not empty |
| 972 | if ((is_scalar($v) && strlen($v)) || !empty($v)) |
| 973 | { |
| 974 | // get a more readable label/text of the saved value |
| 975 | $this->displayFields[$field['langname']] = $v; |
| 976 | } |
| 977 | } |
| 978 | |
| 979 | // translate custom fields of the attendees |
| 980 | foreach ($this->attendees as $i => $attendee) |
| 981 | { |
| 982 | // translate CF data object |
| 983 | $attFields = VAPCustomFieldsLoader::translateObject($attendee['fields'], $cf, $langtag); |
| 984 | |
| 985 | // reset display fields |
| 986 | $attendee['display'] = array(); |
| 987 | |
| 988 | foreach ($cf as $field) |
| 989 | { |
| 990 | $k = $field['name']; |
| 991 | |
| 992 | if (!array_key_exists($k, $attFields) || !$field['repeat']) |
| 993 | { |
| 994 | // field not found inside the given object, go to next one |
| 995 | continue; |
| 996 | } |
| 997 | |
| 998 | $v = $attFields[$k]; |
| 999 | |
| 1000 | // take only if the value is not empty |
| 1001 | if ((is_scalar($v) && strlen($v)) || !empty($v)) |
| 1002 | { |
| 1003 | // get a more readable label/text of the saved value |
| 1004 | $attendee['display'][$field['langname']] = $v; |
| 1005 | } |
| 1006 | } |
| 1007 | |
| 1008 | $this->attendees[$i] = $attendee; |
| 1009 | } |
| 1010 | |
| 1011 | /** |
| 1012 | * External plugins can use this event to apply the translations to |
| 1013 | * additional details manually included within the order object. |
| 1014 | * |
| 1015 | * @param mixed $order The order details object. |
| 1016 | * @param string $langtag The requested language tag. |
| 1017 | * |
| 1018 | * @return void |
| 1019 | * |
| 1020 | * @since 1.7 |
| 1021 | */ |
| 1022 | $dispatcher->trigger('onTranslateAppointmentsOrderDetails', array($this, $langtag)); |
| 1023 | } |
| 1024 | |
| 1025 | /** |
| 1026 | * @override |
| 1027 | * Returns the billing details of the user that made the order. |
| 1028 | * |
| 1029 | * @return object |
| 1030 | */ |
| 1031 | protected function getBilling() |
| 1032 | { |
| 1033 | $dbo = JFactory::getDbo(); |
| 1034 | |
| 1035 | $q = $dbo->getQuery(true) |
| 1036 | ->select('*') |
| 1037 | ->from($dbo->qn('#__vikappointments_users')) |
| 1038 | ->where($dbo->qn('id') . ' = ' . (int) $this->id_user) |
| 1039 | ->orWhere(array( |
| 1040 | $dbo->qn('billing_mail') . ' <> ' . $dbo->q(''), |
| 1041 | $dbo->qn('billing_mail') . ' IS NOT NULL', |
| 1042 | $dbo->qn('billing_mail') . ' = ' . $dbo->q($this->purchaser_mail), |
| 1043 | ), 'AND'); |
| 1044 | |
| 1045 | $dbo->setQuery($q, 0, 1); |
| 1046 | return $dbo->loadObject() ?? false; |
| 1047 | } |
| 1048 | |
| 1049 | /** |
| 1050 | * @override |
| 1051 | * Returns the account details of the order author. |
| 1052 | * |
| 1053 | * @return object |
| 1054 | */ |
| 1055 | protected function getAuthor() |
| 1056 | { |
| 1057 | if ($this->createdby <= 0) |
| 1058 | { |
| 1059 | // no registered author, do not go ahead |
| 1060 | return false; |
| 1061 | } |
| 1062 | |
| 1063 | $dbo = JFactory::getDbo(); |
| 1064 | |
| 1065 | $q = $dbo->getQuery(true) |
| 1066 | ->select($dbo->qn('name')) |
| 1067 | ->select($dbo->qn('username')) |
| 1068 | ->select($dbo->qn('email')) |
| 1069 | ->from($dbo->qn('#__users')) |
| 1070 | ->where($dbo->qn('id') . ' = ' . (int) $this->createdby); |
| 1071 | |
| 1072 | $dbo->setQuery($q, 0, 1); |
| 1073 | return $dbo->loadObject() ?? false; |
| 1074 | } |
| 1075 | |
| 1076 | /** |
| 1077 | * @override |
| 1078 | * Returns the invoice details of the order. |
| 1079 | * |
| 1080 | * @return mixed The invoice object if exists, false otherwise. |
| 1081 | */ |
| 1082 | protected function getInvoice() |
| 1083 | { |
| 1084 | $id_order = $this->id; |
| 1085 | |
| 1086 | if ($this->id_parent != -1 && $this->id != $this->id_parent) |
| 1087 | { |
| 1088 | $id_order = $this->id_parent; |
| 1089 | } |
| 1090 | |
| 1091 | // load invoice details |
| 1092 | return JModelVAP::getInstance('invoice')->getInvoice($id_order, 'appointments'); |
| 1093 | } |
| 1094 | |
| 1095 | /** |
| 1096 | * @override |
| 1097 | * Returns the history of the status codes set for the order. |
| 1098 | * |
| 1099 | * @return array |
| 1100 | */ |
| 1101 | protected function getHistory() |
| 1102 | { |
| 1103 | return VAPOrderStatus::getInstance()->getOrderTrack($this->id, $locale = true); |
| 1104 | } |
| 1105 | |
| 1106 | /** |
| 1107 | * @override |
| 1108 | * Returns a list of notes assigned to this order. |
| 1109 | * |
| 1110 | * @return array |
| 1111 | */ |
| 1112 | protected function getNotes() |
| 1113 | { |
| 1114 | $dbo = JFactory::getDbo(); |
| 1115 | |
| 1116 | $q = $dbo->getQuery(true) |
| 1117 | ->select('n.*') |
| 1118 | ->select($dbo->qn('u.name', 'authorName')) |
| 1119 | ->from($dbo->qn('#__vikappointments_user_notes', 'n')) |
| 1120 | ->leftjoin($dbo->qn('#__users', 'u') . ' ON ' . $dbo->qn('u.id') . ' = ' . $dbo->qn('n.author')) |
| 1121 | ->where($dbo->qn('n.group') . ' = ' . $dbo->q('appointments')); |
| 1122 | |
| 1123 | if (count($this->appointments) > 1) |
| 1124 | { |
| 1125 | $ids = array((int) $this->id); |
| 1126 | |
| 1127 | foreach ($this->appointments as $app) |
| 1128 | { |
| 1129 | $ids[] = (int) $app->id; |
| 1130 | } |
| 1131 | |
| 1132 | // load all notes assigned to the parent order and its children |
| 1133 | $q->where($dbo->qn('n.id_parent') . ' IN (' . implode(',', $ids) . ')'); |
| 1134 | } |
| 1135 | else |
| 1136 | { |
| 1137 | // load all notes assigned to this appointment |
| 1138 | $q->where($dbo->qn('n.id_parent') . ' = ' . (int) $this->id); |
| 1139 | } |
| 1140 | |
| 1141 | // sort by descending modify/create date |
| 1142 | $q->order(sprintf( |
| 1143 | 'IFNULL(%s, %s) %s', |
| 1144 | $dbo->qn('n.modifiedon'), |
| 1145 | $dbo->qn('n.createdon'), |
| 1146 | 'DESC' |
| 1147 | )); |
| 1148 | |
| 1149 | $dbo->setQuery($q); |
| 1150 | |
| 1151 | // load all assigned notes |
| 1152 | $notes = $dbo->loadObjectList(); |
| 1153 | |
| 1154 | if (!$notes) |
| 1155 | { |
| 1156 | // no assigned notes |
| 1157 | return []; |
| 1158 | } |
| 1159 | |
| 1160 | // get tags model |
| 1161 | $tagModel = JModelVAP::getInstance('tag'); |
| 1162 | |
| 1163 | foreach ($notes as $note) |
| 1164 | { |
| 1165 | // JSON decode attachments |
| 1166 | $note->attachments = $note->attachments ? (array) json_decode($note->attachments, true) : array(); |
| 1167 | |
| 1168 | // get file details of the attachments |
| 1169 | $note->attachments = array_map(function($attachment) |
| 1170 | { |
| 1171 | $file = new stdClass; |
| 1172 | $file->name = basename($attachment); |
| 1173 | $file->path = VAPCUSTOMERS_DOCUMENTS . DIRECTORY_SEPARATOR . $attachment; |
| 1174 | $file->uri = VAPCUSTOMERS_DOCUMENTS_URI . str_replace(DIRECTORY_SEPARATOR, '/', $attachment); |
| 1175 | |
| 1176 | return is_file($file->path) ? $file : null; |
| 1177 | }, $note->attachments); |
| 1178 | |
| 1179 | // get rid of missing files |
| 1180 | $note->attachments = array_values(array_filter($note->attachments)); |
| 1181 | |
| 1182 | // get details of the specified tags |
| 1183 | $note->tags = $tagModel->readTags($note->tags, '*'); |
| 1184 | } |
| 1185 | |
| 1186 | return $notes; |
| 1187 | } |
| 1188 | } |
| 1189 |