PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/views/editorder/tmpl/default.php +1609 -224 1.6.9 → 1.8.16 View file →
@@ -15,8 +15,10 @@
15 15 $busy = $this->busy;
16 16 $customer = $this->customer;
17 17 $payments = $this->payments;
18 18
19 +// load registry and history
20 +$registry = VBOBookingRegistry::getInstance($this->row, $this->rooms);
19 21 $history_obj = VikBooking::getBookingHistoryInstance($row['id']);
20 22
21 23 // availability helper
22 24 $av_helper = VikBooking::getAvailabilityInstance();
@@ -31,14 +33,29 @@
31 33 JText::script('VBDELCONFIRM');
32 34 JText::script('VBSENDCANCORDEMAIL');
33 35 JText::script('VBO_SET_STANDBY');
34 36 JText::script('VBO_CONF_RM_OVERBOOKING_FLAG');
37 +JText::script('VBO_NEW_PAYSCHEDULE');
38 +JText::script('VBANNULLA');
39 +JText::script('VBOCLOSE');
40 +JText::script('VBSAVE');
41 +JText::script('VBELIMINA');
42 +JText::script('VBO_REG_NEW_PAYMENT');
43 +JText::script('VBO_UPDATE_PAYMENT');
44 +JText::script('VBSENDSMSACTION');
45 +JText::script('VBSENDEMAILACTION');
46 +JText::script('VBO_AI_AUTO_GUEST_REV_EXCLUDE');
47 +JText::script('VBO_AI_AUTO_GUEST_REV_INCLUDE');
35 48
36 49 $dbo = JFactory::getDbo();
37 50
38 51 $vbo_app = VikBooking::getVboApplication();
39 52 $vbo_app->loadVisualEditorAssets();
53 +$vbo_app->prepareJavaScriptCurrency();
40 54
55 +// preload chat assets
56 +VBOFactory::getChatMediator()->useAssets();
57 +
41 58 /**
42 59 * This View can render within a modal the "bookingcheckin" View, and with WP
43 60 * this is loaded via AJAX. In order to not interfere or reset the contextual menu,
44 61 * we load the assets from the parent View. Context menu may also be used in this View.
@@ -53,8 +70,14 @@
53 70
54 71 $canDo = JFactory::getUser();
55 72 $vbo_auth_bookings = $canDo->authorise('core.vbo.bookings', 'com_vikbooking');
56 73
74 +// check if automatic payment collection is enabled for all bookings
75 +$automatic_payment_collection = VBOFactory::getConfig()->getBool('auto_payment_collection', false);
76 +
77 +$booking_has_damage_dep = 0;
78 +$current_ts = time();
79 +$ota_type_data = !empty($row['ota_type_data']) ? ((array) json_decode($row['ota_type_data'], true)) : [];
57 80 $currencyname = VikBooking::getCurrencyName();
58 81 $currencysymb = VikBooking::getCurrencySymb();
59 82 $after_tax = VikBooking::ivaInclusa();
60 83 $nowdf = VikBooking::getDateFormat(true);
@@ -134,8 +157,9 @@
134 157 $daysdiff = ceil($daysdiff);
135 158 }
136 159 }
137 160 }
161 +
138 162 $otachannel = '';
139 163 $otachannel_name = '';
140 164 $otachannel_bid = '';
141 165 $otacurrency = '';
@@ -147,11 +171,23 @@
147 171 if (strstr($otachannel, '.') !== false) {
148 172 $otaccparts = explode('.', $otachannel);
149 173 $otachannel = $otaccparts[0];
150 174 }
151 - $otacurrency = strlen($row['chcurrency']) > 0 ? $row['chcurrency'] : '';
175 + $otacurrency = strlen((string) $row['chcurrency']) > 0 ? $row['chcurrency'] : '';
152 176 }
153 177
178 +/**
179 + * Tell whether the current reservation has or may have a CC/VCC available.
180 + */
181 +$may_have_cc_details = false;
182 +if (!empty($row['paymentlog']) && stripos($row['paymentlog'], 'card number') !== false && strpos($row['paymentlog'], '****') !== false) {
183 + // turn flag on for partial card details available
184 + $may_have_cc_details = true;
185 +} elseif (!empty($row['channel']) && !strcasecmp($otachannel_name, 'Booking.com')) {
186 + // the CM may be able to retrieve the CC details via API
187 + $may_have_cc_details = true;
188 +}
189 +
154 190 $status_type = '';
155 191 if (!empty($row['type'])) {
156 192 $status_type = JText::translate('VBO_BTYPE_' . strtoupper($row['type']));
157 193 if (!strcasecmp($row['type'], 'overbooking')) {
@@ -181,8 +217,24 @@
181 217
182 218 // extend administration sidebar
183 219 $adminSidebarForms = $this->onDisplayView('AdministrationSidebar');
184 220
221 +/**
222 + * Check if messaging accounts have been configured through the E4jConnect Channel Manager.
223 + * If some messaging API accounts have been configured, SMS fallback will no longer be displayed.
224 + *
225 + * @since 1.18.8 (J) - 1.8.8 (WP)
226 + */
227 +$messagingAccountConfigurations = [];
228 +if (class_exists('VCMMessagingAccountsModel')) {
229 + $messagingAccountConfigurations = VCMMessagingAccountsModel::getInstance()->getConfigurationsData(
230 + new VCMMessagingTemplateDecoratorBooking($registry),
231 + [
232 + 'listings' => $registry->getBookedListingIds(),
233 + ]
234 + );
235 +}
236 +
185 237 //Prepare modal (used for the Registration and for reconstructing the credit card details through the channel manager)
186 238 echo $vbo_app->getJmodalScript();
187 239 //end Prepare modal
188 240 ?>
@@ -312,9 +364,9 @@
312 364 <span><?php echo date(str_replace("/", $datesep, $df).' H:i', $row['ts']); ?></span>
313 365 </div>
314 366 </div>
315 367 <?php
316 - if (!$printreceipt && count($customer)) {
368 + if (!$printreceipt && $customer) {
317 369 ?>
318 370 <div class="vbo-bookdet-wrap">
319 371 <div class="vbo-bookdet-head">
320 372 <span><?php echo JText::translate('VBCUSTOMERNOMINATIVE'); ?></span>
@@ -417,9 +469,9 @@
417 469 $checked_btn = 'btn-warning';
418 470 break;
419 471 default:
420 472 $checked_status = JText::translate('VBOCHECKEDSTATUSZERO');
421 - if (count($customer) && isset($customer['pax_data']) && !empty($customer['pax_data'])) {
473 + if ($customer && isset($customer['pax_data']) && !empty($customer['pax_data'])) {
422 474 // pre check-in performed via front-end
423 475 $checked_status = JText::translate('VBOCHECKEDSTATUSPRECHECKIN');
424 476 }
425 477 break;
@@ -441,23 +493,48 @@
441 493 //end Prepare modal
442 494 }
443 495
444 496 /**
445 - * Check whether the booking can be refunded.
497 + * Check whether the booking can be refunded or charged through an off-session capturing.
446 498 *
447 499 * @since 1.14 (J) - 1.4.0 (WP)
500 + * @since 1.18.0 (J) - 1.8.0 (WP) added support to off-session capturing.
448 501 */
449 502
450 - // refundable up to 7 days after check-out day
503 + // refundable/capturable up to 7 days after check-out day
451 504 $max_refund_ts = mktime(23, 59, 59, $checkout_info['mon'], ($checkout_info['mday'] + 7), $checkout_info['year']);
505 +
452 506 // current payment driver must be set
453 507 $tn_driver = is_array($payment) ? $payment['file'] : null;
508 +
454 509 // transaction data validation callback
455 510 $tn_data_callback = function($data) use ($tn_driver) {
456 511 return (is_object($data) && isset($data->driver) && basename($data->driver, '.php') == basename($tn_driver, '.php'));
457 512 };
458 513 $prev_tn_data = $history_obj->getEventsWithData(array('P0', 'PN'), $tn_data_callback);
459 - $refundable = ($row['status'] != 'standby' && $max_refund_ts >= time() && is_array($prev_tn_data) && count($prev_tn_data));
514 +
515 + // whether it's refundable/capturable
516 + $refundable = ($row['status'] != 'standby' && !empty($row['totpaid']) && $max_refund_ts >= $current_ts && is_array($prev_tn_data) && $prev_tn_data);
517 + $off_session_capturing = ($row['status'] != 'standby' && $max_refund_ts >= $current_ts && is_array($prev_tn_data) && array_filter($prev_tn_data, function($data) {
518 + return is_object($data) && ($data->future_usage ?? null);
519 + }));
520 +
521 + if ($off_session_capturing) {
522 + ?>
523 + <div class="vbo-bookdet-wrap">
524 + <div class="vbo-bookdet-head">
525 + <span><?php echo JText::translate('VBO_CREDIT_CARD'); ?></span>
526 + </div>
527 + <div class="vbo-bookdet-foot">
528 + <button type="button" class="btn btn-small vbo-config-btn vbo-offsessioncapture-btn" onclick="VBOCore.handleDisplayWidgetNotification({widget_id: 'virtual_terminal'}, {bid: <?php echo $row['id']; ?>});">
529 + <?php VikBookingIcons::e('credit-card'); ?>
530 + <?php echo JText::translate('VBO_W_VIRTUALTERMINAL_TITLE'); ?>
531 + </button>
532 + </div>
533 + </div>
534 + <?php
535 + }
536 +
460 537 if ($refundable) {
461 538 // prepare modal (refundtn) with a custom function to trigger the opening
462 539 array_push($vbo_modals_html, $vbo_app->getJmodalHtml('vbo-refund-tn', JText::translate('VBO_ISSUE_REFUND')));
463 540 echo $vbo_app->getJmodalScript('Refund', 'vboDetectRefundChanges();');
@@ -478,9 +555,9 @@
478 555 <div class="vbo-bookdet-head">
479 556 <span><?php echo JText::translate('VBO_ISSUE_REFUND'); ?></span>
480 557 </div>
481 558 <div class="vbo-bookdet-foot">
482 - <button type="button" class="btn btn-small btn-danger vbo-dorefund-btn"<?php echo count($refunds) ? ' data-totrefunds="' . count($refunds) . '"' : ''; ?> onclick="vboOpenJModalRefund('vbo-refund-tn', 'index.php?option=com_vikbooking&task=refundtn&cid[]=<?php echo $row['id']; ?>&tmpl=component');">
559 + <button type="button" class="btn btn-small btn-danger vbo-dorefund-btn"<?php echo $refunds ? ' data-totrefunds="' . count($refunds) . '"' : ''; ?> onclick="vboOpenJModalRefund('vbo-refund-tn', 'index.php?option=com_vikbooking&task=refundtn&cid[]=<?php echo $row['id']; ?>&tmpl=component');">
483 560 <?php echo JText::translate('VBO_REFUND'); ?>
484 561 </button>
485 562 </div>
486 563 </div>
@@ -485,9 +562,8 @@
485 562 </div>
486 563 </div>
487 564 <?php
488 565 }
489 - //
490 566
491 567 if (!$printreceipt && !empty($row['channel'])) {
492 568 $ota_logo_img = VikBooking::getVcmChannelsLogo($row['channel']);
493 569 if ($ota_logo_img === false) {
@@ -586,9 +662,9 @@
586 662 </div>
587 663 </div>
588 664 <?php
589 665 }
590 - if (!empty($row['channel']) && stripos($row['channel'], 'booking.com') !== false && $row['checkin'] <= time() && strtotime('+ 7 days', $row['checkout']) >= time()) {
666 + if (!empty($row['channel']) && stripos($row['channel'], 'booking.com') !== false && $row['checkin'] <= $current_ts && strtotime('+ 7 days', $row['checkout']) >= $current_ts) {
591 667 /**
592 668 * Guest misconduct reporting API for Booking.com.
593 669 * Only after reservation check-in date and no later than 7 days after check-out date.
594 670 *
@@ -662,9 +738,9 @@
662 738 },
663 739 (res) => {
664 740 // parse the JSON response that contains the categories object with all fields for the reporting
665 741 try {
666 - var misconduct_fields_rs = JSON.parse(res);
742 + var misconduct_fields_rs = typeof res === 'string' ? JSON.parse(res) : res;
667 743
668 744 // make sure the request was successful
669 745 if (!misconduct_fields_rs.status) {
670 746 alert(misconduct_fields_rs.error);
@@ -849,9 +925,9 @@
849 925 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikchannelmanager&task=submit_bcom_guestmisconduct'); ?>",
850 926 data: submit_fields
851 927 }).done(function(res) {
852 928 try {
853 - var misconduct_rs = JSON.parse(res);
929 + var misconduct_rs = typeof res === 'string' ? JSON.parse(res) : res;
854 930 console.log(submit_fields, misconduct_rs);
855 931
856 932 // make sure the request was successful
857 933 if (!misconduct_rs.status) {
@@ -980,9 +1056,9 @@
980 1056 $ir_data_callback = function($data) {
981 1057 return (is_object($data) && !empty($data->av_type));
982 1058 };
983 1059 $prev_ir_data = $history_obj->getEventsWithData('IR', $ir_data_callback, true);
984 - if (is_array($prev_ir_data) && count($prev_ir_data)) {
1060 + if (is_array($prev_ir_data) && $prev_ir_data) {
985 1061 // display a message about the availability type at the time of creation of the inquiry reservation
986 1062
987 1063 // parse original stay dates into DateTime objects
988 1064 $orig_checkin_dt_obj = new JDate($prev_ir_data[0]->checkin_date);
@@ -989,9 +1065,9 @@
989 1065 $orig_checkout_dt_obj = new JDate($prev_ir_data[0]->checkout_date);
990 1066 ?>
991 1067 <div class="vbo-bookdet-container vbo-bookdet-inquiry-alert">
992 1068 <div class="vbo-bookdet-inquiry-alert-dismiss">
993 - <button type="button" class="btn btn-warning" onclick="jQuery('.vbo-bookdet-inquiry-alert').remove();"><?php VikBookingIcons::e('times-circle'); ?> <?php echo JText::translate('VBOBTNKEEPREMIND'); ?></button>
1069 + <button type="button" class="btn btn-small btn-warning" onclick="jQuery('.vbo-bookdet-inquiry-alert').remove();"><?php VikBookingIcons::e('times-circle'); ?> <?php echo JText::translate('VBOBTNKEEPREMIND'); ?></button>
994 1070 </div>
995 1071 <div class="vbo-bookdet-inquiry-alert-message">
996 1072 <div class="vbo-bookdet-inquiry-alert-top">
997 1073 <span><?php echo JText::translate('VBO_WEB_INQUIRY_ALERT'); ?></span>
@@ -1025,8 +1101,43 @@
1025 1101 </div>
1026 1102 <div class="vbo-bookdet-inquiry-alert-bottom">
1027 1103 <span><?php echo JText::translate('VBO_WEB_INQUIRY_SUGG'); ?></span>
1028 1104 </div>
1105 + <?php
1106 + if (empty($row['idquote'])) {
1107 + // even if it cannot be a quote, we make sure it's just an inquiry for now
1108 + $createQuoteUri = 'index.php?option=com_vikbooking&view=managequote&' . http_build_query([
1109 + 'customer' => [
1110 + 'id' => $customer['id'] ?? null,
1111 + 'first_name' => $customer['first_name'] ?? null,
1112 + 'last_name' => $customer['last_name'] ?? null,
1113 + 'email' => $customer['email'] ?? null,
1114 + 'phone' => $customer['phone'] ?? null,
1115 + 'country' => $customer['country'] ?? null,
1116 + ],
1117 + 'inquiry' => [
1118 + 'checkin' => date('Y-m-d', $row['checkin']),
1119 + 'checkout' => date('Y-m-d', $row['checkout']),
1120 + 'parties' => array_map(function($or) {
1121 + return [
1122 + 'adults' => $or['adults'] ?? 1,
1123 + 'children' => $or['children'] ?? 0,
1124 + ];
1125 + }, $rooms),
1126 + 'rooms' => array_map(function($or) {
1127 + return [
1128 + 'id' => $or['idroom'] ?? 0,
1129 + ];
1130 + }, $rooms),
1131 + ],
1132 + ]);
1133 + ?>
1134 + <div class="vbo-bookdet-inquiry-alert-bottom">
1135 + <a class="btn vbo-config-btn" href="<?php echo VBOFactory::getPlatform()->getUri()->admin($createQuoteUri); ?>"><?php VikBookingIcons::e('file-alt'); ?> <?php echo JText::translate('VBO_CREATE_QUOTE'); ?></a>
1136 + </div>
1137 + <?php
1138 + }
1139 + ?>
1029 1140 </div>
1030 1141 </div>
1031 1142 <?php
1032 1143 }
@@ -1042,16 +1153,23 @@
1042 1153 <button onclick="document.location.href='index.php?option=com_vikbooking&task=editbusy&cid[]=<?php echo $row['id']; ?>';" class="btn vbo-config-btn" type="button"><i class="icon-pencil"></i> <?php echo JText::translate('VBMODRES'); ?></button>
1043 1154 </div>
1044 1155 <?php
1045 1156 }
1046 - if ((array_key_exists(1, $tars) && count($tars[1]) > 0) || ($is_package || $is_cust_cost)) {
1157 + if (!empty($row['idquote'])) {
1158 + ?>
1159 + <div class="vbo-bookingdet-command">
1160 + <button class="btn vbo-config-btn vbo-quote-details-btn" type="button"><?php VikBookingIcons::e('file-alt'); ?> <?php echo JText::translate('VBO_QUOTE_DETAILS'); ?></button>
1161 + </div>
1162 + <?php
1163 + }
1164 + if ((array_key_exists(1, $tars) && $tars[1]) || ($is_package || $is_cust_cost)) {
1047 1165
1048 1166 if (VBOPlatformDetection::isWordPress()) {
1049 1167 /**
1050 1168 * @wponly display warning message if no valid Shortcodes found
1051 1169 */
1052 - $model = JModel::getInstance('vikbooking', 'shortcodes');
1053 - $itemid = $model->best('booking');
1170 + $model = JModel::getInstance('vikbooking', 'shortcodes');
1171 + $itemid = $model->best('booking');
1054 1172 if (!$itemid) {
1055 1173 VikError::raiseWarning('', 'No Shortcodes found, or no Shortcodes being used in Pages/Posts.');
1056 1174 }
1057 1175 }
@@ -1061,9 +1179,9 @@
1061 1179 <button onclick="window.open('<?php echo VikBooking::externalroute('index.php?option=com_vikbooking&view=booking&sid='.(!empty($row['sid']) ? $row['sid'] : $row['idorderota']).'&ts='.$row['ts'], false); ?>', '_blank');" type="button" class="btn vbo-config-btn"><?php VikBookingIcons::e('external-link'); ?> <?php echo JText::translate('VBVIEWORDFRONT'); ?></button>
1062 1180 </div>
1063 1181 <?php
1064 1182 }
1065 - if (($row['status'] == "confirmed" || ($row['status'] == "standby" && !empty($row['custmail']))) && ((array_key_exists(1, $tars) && count($tars[1]) > 0) || ($is_package || $is_cust_cost))) {
1183 + if (($row['status'] == "confirmed" || ($row['status'] == "standby" && !empty($row['custmail']))) && ((array_key_exists(1, $tars) && $tars[1]) || ($is_package || $is_cust_cost))) {
1066 1184 ?>
1067 1185 <div class="vbo-bookingdet-command">
1068 1186 <button class="btn vbo-config-btn" type="button" onclick="document.location.href='index.php?option=com_vikbooking&task=resendordemail&cid[]=<?php echo $row['id']; ?>';"><?php VikBookingIcons::e('envelope'); ?> <?php echo JText::translate('VBRESENDORDEMAIL'); ?></button>
1069 1187 </div>
@@ -1088,9 +1206,9 @@
1088 1206 </div>
1089 1207 <?php
1090 1208 }
1091 1209 }
1092 - if ($row['status'] == "standby" || ($row['status'] == "cancelled" && $row['checkout'] >= time())) {
1210 + if ($row['status'] == "standby" || ($row['status'] == "cancelled" && $row['checkout'] >= $current_ts)) {
1093 1211 ?>
1094 1212 <div class="vbo-bookingdet-command">
1095 1213 <button class="btn btn-success" type="button" onclick="if (confirm('<?php echo htmlspecialchars(JText::translate('VBSETORDCONFIRMED')); ?> ?')) {document.location.href='index.php?option=com_vikbooking&task=setordconfirmed&cid[]=<?php echo $row['id'] . ($row['status'] == "cancelled" ? '&skip_notification=1' : ''); ?>';}"><?php VikBookingIcons::e('check'); ?> <?php echo JText::translate('VBSETORDCONFIRMED'); ?></button>
1096 1214 </div>
@@ -1099,9 +1217,9 @@
1099 1217 if ($row['status'] == "cancelled" && !$row['closure']) {
1100 1218 ?>
1101 1219 <div class="vbo-bookingdet-command">
1102 1220 <button type="button" class="btn btn-warning vbo-context-menu-btn vbo-context-menu-actionscanc">
1103 - <span class="vbo-context-menu-lbl"><?php echo JText::translate('VBCRONACTIONS'); ?> &#8230;</span>
1221 + <span class="vbo-context-menu-lbl"><?php echo JText::translate('VBCRONACTIONS'); ?></span>
1104 1222 <span class="vbo-context-menu-ico"><?php VikBookingIcons::e('sort-down'); ?></span>
1105 1223 </button>
1106 1224 </div>
1107 1225
@@ -1202,11 +1320,84 @@
1202 1320 <div class="vbo-bookingdet-tab" data-vbotab="vbo-tab-admin"><?php echo JText::translate('VBOBOOKDETTABADMIN'); ?></div>
1203 1321 </div>
1204 1322 </div>
1205 1323
1324 + <?php
1325 + /**
1326 + * Check if the booking has got some tasks assigned.
1327 + *
1328 + * @since 1.18.0 (J) - 1.8.0 (WP)
1329 + */
1330 + $taskManager = VBOFactory::getTaskManager();
1331 + $bookingTasks = VBOTaskModelTask::getInstance()->filterItems(['id_order' => $row['id']]);
1332 +
1333 + /**
1334 + * Check if the booking should have had passcodes generated.
1335 + *
1336 + * @since 1.18.16 (J) - 1.8.16 (WP)
1337 + */
1338 + $shouldHavePasscodes = !$history_obj->hasEvent(['ND', 'MD']) && VBOFactory::getDoorAccessControl()->shouldBookingHavePasscodes($registry);
1339 + ?>
1340 +
1206 1341 <div class="vbo-bookingdet-tab-cont" id="vbo-tab-details" style="display: block;">
1207 1342 <div class="vbo-bookingdet-innercontainer">
1208 1343 <div class="vbo-bookingdet-customer">
1344 + <?php
1345 + if ($bookingTasks) {
1346 + ?>
1347 + <div class="vbo-bookingdet-detcont vbo-bookingdet-detcont-tm vbo-hidein-print">
1348 + <div class="vbo-bookingdet-lblcont">
1349 + <label><?php echo JText::translate('VBO_TASK_MANAGER'); ?></label>
1350 + </div>
1351 + <div class="vbo-bookingdet-inpwrap vbo-bookingdet-tasks-list">
1352 + <?php
1353 + foreach ($bookingTasks as $taskRecord) {
1354 + $task = VBOTaskTaskregistry::getInstance((array) $taskRecord);
1355 + ?>
1356 + <div class="vbo-bookingdet-task-details" data-task-id="<?php echo $task->getID(); ?>">
1357 + <div class="vbo-bookingdet-task-detail" data-type="info">
1358 + <span class="vbo-bookingdet-task-title"><?php echo $task->getTitle(); ?></span>
1359 + <span class="vbo-bookingdet-task-sub-title"><?php echo $task->getAreaName($task->getAreaID()); ?></span>
1360 + </div>
1361 + <?php
1362 + $taskUnreadMessages = (bool) $task->get('hasUnreadMessages', false);
1363 + if ($taskUnreadMessages) {
1364 + ?>
1365 + <div class="vbo-bookingdet-task-detail" data-type="unread-messages">
1366 + <span class="unread-message-dot"><?php VikBookingIcons::e('comment'); ?></span>
1367 + </div>
1368 + <?php
1369 + }
1370 + if ($taskManager->statusTypeExists($task->getStatus())) {
1371 + $taskStatus = $taskManager->getStatusTypeInstance($task->getStatus());
1372 + ?>
1373 + <div class="vbo-bookingdet-task-detail" data-type="status">
1374 + <span class="vbo-tm-task-status-badge vbo-tm-color <?php echo $taskStatus->getColor(); ?>" data-status="<?php echo $this->escape($taskStatus->getEnum()); ?>"><?php echo $taskStatus->getName(); ?></span>
1375 + </div>
1376 + <?php
1377 + }
1378 + ?>
1379 + </div>
1380 + <?php
1381 + }
1382 + ?>
1383 + </div>
1384 + </div>
1385 + <?php
1386 + }
1387 + if ($shouldHavePasscodes) {
1388 + ?>
1389 + <div class="vbo-bookingdet-detcont vbo-bookingdet-detcont-dac vbo-hidein-print">
1390 + <div class="vbo-bookingdet-lblcont">
1391 + <label><?php echo JText::translate('VBO_W_DOORACCESSCONTROL_TITLE'); ?></label>
1392 + </div>
1393 + <div class="vbo-bookingdet-inpwrap vbo-bookingdet-dac-gen">
1394 + <a href="<?php echo VBOFactory::getPlatform()->getUri()->addCSRF('index.php?option=com_vikbooking&task=bookings.generatePasscodes&bid=' . $row['id']); ?>" class="btn"><?php echo JText::translate('VBO_CREATE_PASSCODE'); ?></a>
1395 + </div>
1396 + </div>
1397 + <?php
1398 + }
1399 + ?>
1209 1400 <div class="vbo-bookingdet-detcont<?php echo $row['closure'] > 0 ? ' vbo-bookingdet-closure' : ''; ?>">
1210 1401 <?php
1211 1402 $custdata_parts = explode("\n", $row['custdata']);
1212 1403 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
@@ -1230,11 +1421,11 @@
1230 1421 }
1231 1422 unset($custdet_parts[0]);
1232 1423 if ((!strcasecmp(trim($custdet_parts[1]), 'http') || !strcasecmp(trim($custdet_parts[1]), 'https')) && !empty($custdet_parts[2]) && strpos($custdet_parts[2], '//') !== false) {
1233 1424 // this is a URI
1234 - $custd_val = '<a href="' . trim(implode(':', $custdet_parts)) . '" target="_blank"><i class="' . VikBookingIcons::i('external-link') . '"></i></a>';
1425 + $custd_val = '<a href="' . htmlspecialchars(trim(implode(':', $custdet_parts))) . '" target="_blank"><i class="' . VikBookingIcons::i('external-link') . '"></i></a>';
1235 1426 } else {
1236 - $custd_val = trim(implode(':', $custdet_parts));
1427 + $custd_val = htmlspecialchars(trim(implode(':', $custdet_parts)));
1237 1428 }
1238 1429 }
1239 1430 ?>
1240 1431 <div class="vbo-bookingdet-userdetail">
@@ -1292,13 +1483,20 @@
1292 1483
1293 1484 </div>
1294 1485
1295 1486 <?php
1487 + // build the proper invoice SID for bc
1488 + $inv_sid = $row['sid'] ?: $row['idorderota'] ?: '';
1489 + if (empty($row['sid']) && is_file(implode(DIRECTORY_SEPARATOR, [VBO_SITE_PATH, 'helpers', 'invoices', 'generated', "{$row['id']}_{$row['sid']}.pdf"]))) {
1490 + // use the old file name signature for OTA bookings
1491 + $inv_sid = $row['sid'];
1492 + }
1493 +
1296 1494 // build list of invoice details (path/uri & label/button), one by default
1297 1495 $invoice_details = [
1298 1496 [
1299 - 'path' => implode(DIRECTORY_SEPARATOR, [VBO_SITE_PATH, 'helpers', 'invoices', 'generated', "{$row['id']}_{$row['sid']}.pdf"]),
1300 - 'uri' => VBO_SITE_URI . 'helpers/invoices/generated/' . "{$row['id']}_{$row['sid']}.pdf",
1497 + 'path' => implode(DIRECTORY_SEPARATOR, [VBO_SITE_PATH, 'helpers', 'invoices', 'generated', "{$row['id']}_{$inv_sid}.pdf"]),
1498 + 'uri' => VBO_SITE_URI . 'helpers/invoices/generated/' . "{$row['id']}_{$inv_sid}.pdf",
1301 1499 'label' => JText::translate('VBOCOLORTAGRULEINVONE'),
1302 1500 'btn' => JText::translate('VBOINVDOWNLOAD'),
1303 1501 ]
1304 1502 ];
@@ -1328,14 +1526,30 @@
1328 1526 ?>
1329 1527 <div class="vbo-bookingdet-detcont vbo-bookingdet-detcont-labels-wrap vbo-hidein-print">
1330 1528 <?php
1331 1529 if (!empty($row['channel']) && !empty($row['idorderota'])) {
1530 + $tiny_logo_url = '';
1531 + $channelparts = explode('_', $row['channel']);
1532 + if ($ch_logo = VikBooking::getVcmChannelsLogo($row['channel'], true)) {
1533 + $tiny_logo_url = $ch_logo->getTinyLogoURL();
1534 + }
1535 + $displayOtaBid = !strcasecmp((string) $row['type'], 'Inquiry') && $row['status'] == 'standby' ? '' : $row['idorderota'];
1332 1536 ?>
1333 - <div class="vbo-bookingdet-detcont-label vbo-bookingdet-detcont-label-idorderota">
1537 + <div class="vbo-bookingdet-detcont-label vbo-bookingdet-detcont-label-idorderota <?php echo strtolower(preg_replace('/[^a-z0-9]/i', '', $channelparts[0])); ?>">
1334 1538 <span class="label label-info">
1335 - <span><?php echo $otachannel_name . ' ID'; ?></span>
1336 - <span class="badge"><?php echo !strcasecmp((string)$row['type'], 'Inquiry') && $row['status'] == 'standby' ? '-----' : $row['idorderota']; ?></span>
1539 + <span class="vbo-bookingdet-ota-details">
1540 + <?php
1541 + if ($tiny_logo_url) {
1542 + ?>
1543 + <span class="vbo-bookingdet-ota-logo"><img src="<?php echo $tiny_logo_url; ?>" /></span>
1544 + <?php
1545 + }
1546 + ?>
1547 + <span class="vbo-bookingdet-ota-name"><?php echo $otachannel_name . ' ID'; ?></span>
1548 + </span>
1549 + <span class="badge<?php echo $displayOtaBid ? ' vbo-tooltip vbo-tooltip-top vbo-reservation-copy-otabid' : ''; ?>" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', JText::translate('VBO_COPY')); ?>"><?php echo $displayOtaBid ? $row['idorderota'] : '-----'; ?></span>
1337 1550 </span>
1551 + <textarea class="vbo-textarea-copyable" data-copy="otabid"><?php echo htmlentities($displayOtaBid); ?></textarea>
1338 1552 </div>
1339 1553 <?php
1340 1554 }
1341 1555 if (strlen((string)$row['confirmnumber'])) {
@@ -1347,8 +1561,103 @@
1347 1561 </span>
1348 1562 </div>
1349 1563 <?php
1350 1564 }
1565 +
1566 + /**
1567 + * Check for AI automated host-to-guest reviews.
1568 + *
1569 + * @since 1.16.10 (J) - 1.6.10 (WP)
1570 + */
1571 + if (class_exists('VikChannelManager') && VikChannelManager::hostToGuestReviewSupported($row, $willbe = true)) {
1572 + if (class_exists('VCMAiModelSettings') && (new VCMAiModelSettings)->isGuestReviewAutoResponderEnabled()) {
1573 + // host-to-guest review will be supported and AI auto-guest-review is enabled
1574 + $ai_auto_guest_review_opt = (array) VBOFactory::getConfig()->getArray('ai_auto_guest_review_opt_' . $row['id'], []);
1575 + ?>
1576 + <div class="vbo-bookingdet-detcont-ai-info">
1577 + <p class="vbo-bookingdet-detcont-ai-info-status">
1578 + <span class="vbo-bookingdet-detcont-ai-info-status-on" style="<?php echo ($ai_auto_guest_review_opt['ignore'] ?? 0) ? 'display: none;' : ''; ?>"><?php VikBookingIcons::e('check-circle'); ?> <span><?php echo JText::translate('VBO_AI_AUTO_GUEST_REV_WILLRUN'); ?></span></span>
1579 + <span class="vbo-bookingdet-detcont-ai-info-status-off" style="<?php echo !($ai_auto_guest_review_opt['ignore'] ?? 0) ? 'display: none;' : ''; ?>"><?php VikBookingIcons::e('ban'); ?> <span><?php echo JText::translate('VBO_AI_AUTO_GUEST_REV_RESIGNORE'); ?></span></span>
1580 + </p>
1581 + <button type="button" class="btn btn-small btn-primary vbo-context-menu-btn vbo-context-menu-ai-autoreview">
1582 + <span class="vbo-context-menu-lbl"><?php echo JText::translate('VBCRONACTIONS'); ?></span>
1583 + <span class="vbo-context-menu-ico"><?php VikBookingIcons::e('sort-down'); ?></span>
1584 + </button>
1585 + </div>
1586 +
1587 + <script type="text/javascript">
1588 + // build context menu
1589 + jQuery(function() {
1590 +
1591 + // toggle ignore AI auto-review guest function
1592 + let vbo_action_ignore_ai_autorg = (now_ignore) => {
1593 + if (confirm(Joomla.JText._((now_ignore ? 'VBO_AI_AUTO_GUEST_REV_INCLUDE' : 'VBO_AI_AUTO_GUEST_REV_EXCLUDE')))) {
1594 + // update the auto-review ignore status for this booking
1595 + VBOCore.doAjax(
1596 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.set_ai_auto_guest_review_opt'); ?>",
1597 + {
1598 + bid: '<?php echo $row['id']; ?>',
1599 + opt: {
1600 + ignore: (now_ignore ? 0 : 1),
1601 + },
1602 + },
1603 + (opt) => {
1604 + // populate the new buttons
1605 + let new_auto_review_opt = [];
1606 + new_auto_review_opt.push({
1607 + icon: ((opt.ignore || 0) ? '<?php echo VikBookingIcons::i('check-circle'); ?>' : '<?php echo VikBookingIcons::i('ban'); ?>'),
1608 + text: Joomla.JText._((opt?.ignore ? 'VBO_AI_AUTO_GUEST_REV_INCLUDE' : 'VBO_AI_AUTO_GUEST_REV_EXCLUDE')),
1609 + class: (opt?.ignore ? 'vbo-context-menu-entry-success' : 'vbo-context-menu-entry-danger'),
1610 + separator: true,
1611 + action: (root, config) => {
1612 + // register callback
1613 + vbo_action_ignore_ai_autorg(opt.ignore || 0);
1614 + },
1615 + });
1616 +
1617 + // set new buttons in context menu
1618 + jQuery('.vbo-context-menu-ai-autoreview').vboContextMenu('buttons', new_auto_review_opt);
1619 +
1620 + // display the proper status label
1621 + if (opt?.ignore) {
1622 + jQuery('.vbo-bookingdet-detcont-ai-info-status-on').hide();
1623 + jQuery('.vbo-bookingdet-detcont-ai-info-status-off').show();
1624 + } else {
1625 + jQuery('.vbo-bookingdet-detcont-ai-info-status-off').hide();
1626 + jQuery('.vbo-bookingdet-detcont-ai-info-status-on').show();
1627 + }
1628 + },
1629 + (error) => {
1630 + // display error message
1631 + alert(error.responseText);
1632 + }
1633 + );
1634 + }
1635 + };
1636 +
1637 + // render context menu
1638 + jQuery('.vbo-context-menu-ai-autoreview').vboContextMenu({
1639 + placement: 'bottom-left',
1640 + buttons: [
1641 + {
1642 + icon: '<?php echo VikBookingIcons::i(($ai_auto_guest_review_opt['ignore'] ?? 0) ? 'check-circle' : 'ban'); ?>',
1643 + text: Joomla.JText._('<?php echo ($ai_auto_guest_review_opt['ignore'] ?? 0) ? 'VBO_AI_AUTO_GUEST_REV_INCLUDE' : 'VBO_AI_AUTO_GUEST_REV_EXCLUDE'; ?>'),
1644 + class: '<?php echo ($ai_auto_guest_review_opt['ignore'] ?? 0) ? 'vbo-context-menu-entry-success' : 'vbo-context-menu-entry-danger'; ?>',
1645 + separator: true,
1646 + action: (root, config) => {
1647 + // register callback
1648 + vbo_action_ignore_ai_autorg(<?php echo ($ai_auto_guest_review_opt['ignore'] ?? 0); ?>);
1649 + },
1650 + }
1651 + ],
1652 + });
1653 +
1654 + });
1655 + </script>
1656 + <?php
1657 + }
1658 + }
1659 +
1351 1660 if ($invoiced) {
1352 1661 foreach ($invoice_details as $invoice_detail) {
1353 1662 ?>
1354 1663 <div class="vbo-bookingdet-detcont-label vbo-bookingdet-detcont-label-invoice">
@@ -1362,10 +1671,10 @@
1362 1671 }
1363 1672 if ($this->vcm_review) {
1364 1673 ?>
1365 1674 <div class="vbo-bookingdet-detcont-label vbo-bookingdet-detcont-label-review">
1366 - <span class="label label-warning">
1367 - <a href="index.php?option=com_vikchannelmanager&task=reviews&revid=<?php echo $this->vcm_review['id']; ?>" target="_blank" style="color: #fff;"><i class="vboicn-star-full"></i> <?php echo JText::translate('VBOSEEGUESTREVIEW'); ?></a>
1675 + <span class="label label-warning" onclick="VBOCore.handleDisplayWidgetNotification({widget_id: 'guest_reviews'}, {review_id: '<?php echo $this->vcm_review['id']; ?>'});">
1676 + <a href="javascript: void(0);" style="color: #fff;"><i class="vboicn-star-full"></i> <?php echo JText::translate('VBOSEEGUESTREVIEW'); ?></a>
1368 1677 </span>
1369 1678 </div>
1370 1679 <?php
1371 1680 // check if a notification was clicked
@@ -1370,13 +1679,15 @@
1370 1679 <?php
1371 1680 // check if a notification was clicked
1372 1681 if (VikRequest::getString('notif_action', '', 'request') == 'see_guest_review') {
1373 1682 $vcm_review_link = VBOFactory::getPlatform()->getUri()->admin('index.php?option=com_vikchannelmanager&task=reviews&revid=' . $this->vcm_review['id'], false);
1683 + $vcm_review_id = $this->vcm_review['id'];
1374 1684 JFactory::getDocument()->addScriptDeclaration(
1375 1685 <<<JS
1376 1686 jQuery(function() {
1377 1687 setTimeout(() => {
1378 - document.location.href = '$vcm_review_link';
1688 + // document.location.href = '$vcm_review_link';
1689 + VBOCore.handleDisplayWidgetNotification({widget_id: 'guest_reviews'}, {review_id: '$vcm_review_id'});
1379 1690 }, 200);
1380 1691 });
1381 1692 JS
1382 1693 );
@@ -1387,10 +1698,9 @@
1387 1698 <!-- Define role to detect the supported hook -->
1388 1699 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"invoice","type":"field"} -->
1389 1700
1390 1701 <?php
1391 - if (isset($bookingSidebarForms['invoice']))
1392 - {
1702 + if (isset($bookingSidebarForms['invoice'])) {
1393 1703 echo $bookingSidebarForms['invoice'];
1394 1704 }
1395 1705 ?>
1396 1706
@@ -1405,15 +1715,15 @@
1405 1715 </div>
1406 1716 <div class="vbo-bookingdet-inpwrap">
1407 1717 <div class="vbo-bookingdet-inpcont">
1408 1718 <input type="text" name="custmail" id="custmail" value="<?php echo $this->escape($row['custmail']); ?>" size="25" onkeyup="vboKeyupEmail(event);"/>
1719 + <div class="vbo-bookingdet-btncont vbo-bookingdet-save-email" style="display: none;">
1720 + <button type="button" class="btn btn-success" onclick="document.adminForm.submit();"><?php VikBookingIcons::e('save'); ?> <?php echo JText::translate('VBSAVE'); ?></button>
1721 + </div>
1409 1722 </div>
1410 - <div class="vbo-bookingdet-btncont vbo-bookingdet-save-email" style="display: none;">
1411 - <button type="button" class="btn btn-success" onclick="document.adminForm.submit();"><?php VikBookingIcons::e('save'); ?> <?php echo JText::translate('VBSAVE'); ?></button>
1412 - </div>
1413 1723 <?php if (!empty($row['custmail'])) : ?>
1414 1724 <div class="vbo-bookingdet-btncont">
1415 - <button type="button" class="btn vbo-config-btn" onclick="vboToggleSendEmail();" style="vertical-align: top;"><?php VikBookingIcons::e('envelope'); ?> <?php echo JText::translate('VBSENDEMAILACTION'); ?></button>
1725 + <button type="button" class="btn vbo-config-btn" onclick="vboDisplaySendEmail();" style="vertical-align: top;"><?php VikBookingIcons::e('envelope'); ?> <?php echo JText::translate('VBSENDEMAILACTION'); ?></button>
1416 1726 </div>
1417 1727 <?php endif; ?>
1418 1728 </div>
1419 1729
@@ -1420,10 +1730,9 @@
1420 1730 <!-- Define role to detect the supported hook -->
1421 1731 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"email","type":"field"} -->
1422 1732
1423 1733 <?php
1424 - if (isset($bookingSidebarForms['email']))
1425 - {
1734 + if (isset($bookingSidebarForms['email'])) {
1426 1735 echo $bookingSidebarForms['email'];
1427 1736 }
1428 1737 ?>
1429 1738
@@ -1435,13 +1744,27 @@
1435 1744 <div class="vbo-bookingdet-inpwrap">
1436 1745 <div class="vbo-bookingdet-inpcont">
1437 1746 <?php echo $vbo_app->printPhoneInputField(array('name' => 'custphone', 'id' => 'custphone', 'value' => $this->escape($row['phone'])), array('nationalMode' => false, 'fullNumberOnBlur' => true)); ?>
1438 1747 </div>
1439 - <?php if (!empty($row['phone'])) : ?>
1748 + <?php
1749 + if (!empty($row['phone'])) {
1750 + if ($messagingAccountConfigurations) {
1751 + // send a message to the guest phone number through the messaging API account(s) configured
1752 + ?>
1440 1753 <div class="vbo-bookingdet-btncont">
1441 - <button type="button" class="btn vbo-config-btn" onclick="vboToggleSendSMS();" style="vertical-align: top;"><?php VikBookingIcons::e('comment-dots'); ?> <?php echo JText::translate('VBSENDSMSACTION'); ?></button>
1754 + <button type="button" class="btn vbo-config-btn vbo-bookingdet-send-tplmessage-btn" style="vertical-align: top;"><?php VikBookingIcons::e('paper-plane'); ?> <?php echo JText::translate('VBO_SEND_MESSAGE'); ?></button>
1442 1755 </div>
1443 - <?php endif; ?>
1756 + <?php
1757 + } else {
1758 + // fallback onto SMS
1759 + ?>
1760 + <div class="vbo-bookingdet-btncont">
1761 + <button type="button" class="btn vbo-config-btn" onclick="vboDisplaySendSMS();" style="vertical-align: top;"><?php VikBookingIcons::e('comment-dots'); ?> <?php echo JText::translate('VBSENDSMSACTION'); ?></button>
1762 + </div>
1763 + <?php
1764 + }
1765 + }
1766 + ?>
1444 1767 </div>
1445 1768
1446 1769 <!-- Define role to detect the supported hook -->
1447 1770 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"phone","type":"field"} -->
@@ -1446,24 +1769,48 @@
1446 1769 <!-- Define role to detect the supported hook -->
1447 1770 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"phone","type":"field"} -->
1448 1771
1449 1772 <?php
1450 - if (isset($bookingSidebarForms['phone']))
1451 - {
1773 + if (isset($bookingSidebarForms['phone'])) {
1452 1774 echo $bookingSidebarForms['phone'];
1453 1775 }
1454 1776 ?>
1455 1777
1456 1778 </div>
1779 + <?php
1780 + if ($phoneAlias = $registry->getAliasPhoneNumber()) {
1781 + ?>
1782 + <div class="vbo-bookingdet-detcont vbo-hidein-print">
1783 + <div class="vbo-bookingdet-lblcont">
1784 + <label for="phone_alias"><?php VikBookingIcons::e('user-shield'); ?> <?php echo JText::translate('VBO_PHONE_ALIAS'); ?></label>
1785 + </div>
1786 + <div class="vbo-bookingdet-inpwrap">
1787 + <div class="vbo-bookingdet-inpcont">
1788 + <input type="text" id="phone_alias" value="<?php echo $this->escape($phoneAlias); ?>" readonly />
1789 + </div>
1790 + <span class="vbo-param-setting-comment"><?php echo JText::translate('VBO_PHONE_ALIAS_HELP'); ?></span>
1791 + </div>
1457 1792
1793 + <!-- Define role to detect the supported hook -->
1794 + <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"phone_alias","type":"field"} -->
1795 +
1796 + <?php
1797 + if (isset($bookingSidebarForms['phone_alias'])) {
1798 + echo $bookingSidebarForms['phone_alias'];
1799 + }
1800 + ?>
1801 +
1802 + </div>
1803 + <?php
1804 + }
1805 + ?>
1806 +
1458 1807 <!-- Define role to detect the supported hook -->
1459 1808 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","type":"fieldset"} -->
1460 1809
1461 1810 <?php
1462 - foreach ($bookingSidebarForms as $legend => $form)
1463 - {
1464 - if (in_array($legend, ['userdetails', 'invoice', 'email', 'phone']))
1465 - {
1811 + foreach ($bookingSidebarForms as $legend => $form) {
1812 + if (in_array($legend, ['userdetails', 'invoice', 'email', 'phone'])) {
1466 1813 // skip default forms
1467 1814 continue;
1468 1815 }
1469 1816 ?>
@@ -1513,10 +1860,10 @@
1513 1860 <td class="vbo-bookingdet-summary-room-firstcell">
1514 1861 <div class="vbo-bookingdet-summary-room-wrap">
1515 1862 <div class="vbo-bookingdet-summary-roomnum"><?php VikBookingIcons::e($room_icon); ?> <?php echo JText::translate('VBEDITORDERTHREE').' '.$num; ?></div>
1516 1863 <?php
1517 - //Room Specific Unit
1518 - if (!$row['closure'] && $row['status'] == "confirmed" && !empty($or['params'])) {
1864 + // Room Specific Unit
1865 + if ((!$row['closure'] || ($row['closure'] && count($rooms) === 1) && !empty($or['roomindex'])) && $row['status'] == "confirmed" && !empty($or['params'])) {
1519 1866 $room_params = json_decode($or['params'], true);
1520 1867 $arr_features = array();
1521 1868 $unavailable_indexes = VikBooking::getRoomUnitNumsUnavailable($row, $or['idroom']);
1522 1869 if (is_array($room_params) && array_key_exists('features', $room_params) && !empty($room_params['features'])) {
@@ -1531,9 +1878,9 @@
1531 1878 }
1532 1879 }
1533 1880 }
1534 1881 }
1535 - if (count($arr_features)) {
1882 + if ($arr_features) {
1536 1883 // $or['id'] equals to the ID of each matching record in _ordersrooms
1537 1884 ?>
1538 1885 <div class="vbo-bookingdet-summary-roomnum-chunit">
1539 1886 <?php echo !$printreceipt ? $vbo_app->getNiceSelect($arr_features, $or['roomindex'], 'roomindex['.$or['id'].']', JText::translate('VBOFEATASSIGNUNIT'), JText::translate('VBOFEATASSIGNUNITEMPTY'), '', 'document.adminForm.submit();', $or['id']) : (!empty($or['roomindex']) && isset($arr_features[$or['roomindex']]) ? $arr_features[$or['roomindex']] : ''); ?>
@@ -1559,9 +1906,9 @@
1559 1906 if ($arrpeople[$num]['children'] > 0) {
1560 1907 $age_str = '';
1561 1908 if (!empty($arrpeople[$num]['children_age'])) {
1562 1909 $json_child = json_decode($arrpeople[$num]['children_age'], true);
1563 - if (is_array($json_child) && isset($json_child['age']) && is_array($json_child['age']) && count($json_child['age'])) {
1910 + if (is_array($json_child) && isset($json_child['age']) && is_array($json_child['age']) && $json_child['age']) {
1564 1911 $age_str = ' '.JText::sprintf('VBORDERCHILDAGES', implode(', ', $json_child['age']));
1565 1912 }
1566 1913 }
1567 1914 ?>
@@ -1650,9 +1997,9 @@
1650 1997 </div>
1651 1998 <?php
1652 1999 }
1653 2000
1654 - if ($row['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $or['idroom']) {
2001 + if ($row['split_stay'] && $room_stay_dates && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $or['idroom']) {
1655 2002 // print split stay information for this room
1656 2003 $room_stay_checkin = !empty($room_stay_dates[$ind]['checkin_ts']) ? $room_stay_dates[$ind]['checkin_ts'] : $room_stay_dates[$ind]['checkin'];
1657 2004 $room_stay_checkout = !empty($room_stay_dates[$ind]['checkout_ts']) ? $room_stay_dates[$ind]['checkout_ts'] : $room_stay_dates[$ind]['checkout'];
1658 2005 $room_stay_nights = $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout);
@@ -1664,9 +2011,9 @@
1664 2011 <span class="vbo-cal-splitstay-dates-out" title="<?php echo date('H:i', $room_stay_checkout); ?>"><?php VikBookingIcons::e('plane-departure'); ?> <?php echo date(str_replace("/", $datesep, $df), $room_stay_checkout); ?></span>
1665 2012 </div>
1666 2013 </div>
1667 2014 <?php
1668 - } elseif (!$row['split_stay'] && count($room_stay_records) && isset($room_stay_records[$ind]) && $room_stay_records[$ind]['idroom'] == $or['idroom']) {
2015 + } elseif (!$row['split_stay'] && $room_stay_records && isset($room_stay_records[$ind]) && $room_stay_records[$ind]['idroom'] == $or['idroom']) {
1669 2016 // print modified stay dates information for this room
1670 2017 $room_stay_checkin = $room_stay_records[$ind]['checkin'];
1671 2018 $room_stay_checkout = $room_stay_records[$ind]['checkout'];
1672 2019 $room_stay_nights = $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout);
@@ -1712,8 +2059,17 @@
1712 2059 $actopt = $dbo->loadAssocList();
1713 2060 if (!$actopt) {
1714 2061 continue;
1715 2062 }
2063 +
2064 + // option params
2065 + $opt_params = !empty($actopt[0]['oparams']) ? json_decode($actopt[0]['oparams'], true) : [];
2066 + $opt_params = is_array($opt_params) ? $opt_params : [];
2067 + if ($opt_params['damagedep'] ?? 0) {
2068 + // turn flag on
2069 + $booking_has_damage_dep = 1;
2070 + }
2071 +
1716 2072 $counter++;
1717 2073 $chvar = '';
1718 2074 if (!empty($actopt[0]['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
1719 2075 $optagenames = VikBooking::getOptionIntervalsAges($actopt[0]['ageintervals']);
@@ -1749,9 +2105,9 @@
1749 2105 }
1750 2106 $actopt[0]['chageintv'] = $chvar;
1751 2107 $actopt[0]['name'] .= ' ('.$optagenames[($chvar - 1)].')';
1752 2108 $realcost = (intval($actopt[0]['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $room_stay_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
1753 - } else {
2109 + } elseif (is_numeric($stept[1])) {
1754 2110 // VBO 1.11 - options percentage cost of the room total fee
1755 2111 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
1756 2112 $deftar_basecosts = $or['cust_cost'];
1757 2113 } else {
@@ -1760,8 +2116,10 @@
1760 2116 }
1761 2117 $actopt[0]['cost'] = (int)$actopt[0]['pcentroom'] ? ($deftar_basecosts * $actopt[0]['cost'] / 100) : $actopt[0]['cost'];
1762 2118 //
1763 2119 $realcost = (intval($actopt[0]['perday']) == 1 ? ($actopt[0]['cost'] * $room_stay_nights * $stept[1]) : ($actopt[0]['cost'] * $stept[1]));
2120 + } else {
2121 + $realcost = 0;
1764 2122 }
1765 2123 if ($actopt[0]['maxprice'] > 0 && $realcost > $actopt[0]['maxprice']) {
1766 2124 $realcost = $actopt[0]['maxprice'];
1767 2125 if (intval($actopt[0]['hmany']) == 1 && intval($stept[1]) > 1) {
@@ -1768,8 +2126,19 @@
1768 2126 $realcost = $actopt[0]['maxprice'] * $stept[1];
1769 2127 }
1770 2128 }
1771 2129 $realcost = $actopt[0]['perperson'] == 1 ? ($realcost * $arrpeople[$num]['adults']) : $realcost;
2130 +
2131 + /**
2132 + * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2133 + *
2134 + * @since 1.17.7 (J) - 1.7.7 (WP)
2135 + */
2136 + $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt[0], $row, $or]);
2137 + if ($custom_calculation) {
2138 + $realcost = (float) $custom_calculation[0];
2139 + }
2140 +
1772 2141 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt[0]['idiva']);
1773 2142 ?>
1774 2143 <tr class="vbo-bookingdet-summary-options">
1775 2144 <td class="vbo-bookingdet-summary-options-title"><?php echo $counter == 1 ? JText::translate('VBEDITORDEREIGHT') : '&nbsp;'; ?></td>
@@ -1857,9 +2226,9 @@
1857 2226 //Reservation Total
1858 2227 //Taxes Breakdown (only if tot_taxes is greater than 0)
1859 2228 $tax_breakdown = array();
1860 2229 $base_aliq = 0;
1861 - if (count($all_id_prices) > 0 && $row['tot_taxes'] > 0) {
2230 + if ($all_id_prices && $row['tot_taxes'] > 0) {
1862 2231 //only last type of price assuming that the tax breakdown is equivalent in case of different rates
1863 2232 $q = "SELECT `p`.`id`,`p`.`name`,`p`.`idiva`,`t`.`aliq`,`t`.`breakdown` FROM `#__vikbooking_prices` AS `p` LEFT JOIN `#__vikbooking_iva` `t` ON `p`.`idiva`=`t`.`id` WHERE `p`.`id`=".intval(array_pop($all_id_prices))." LIMIT 1;";
1864 2233 $dbo->setQuery($q);
1865 2234 $breakdown_info = $dbo->loadAssoc();
@@ -1865,9 +2234,9 @@
1865 2234 $breakdown_info = $dbo->loadAssoc();
1866 2235 if ($breakdown_info) {
1867 2236 if (!empty($breakdown_info['breakdown']) && !empty($breakdown_info['aliq'])) {
1868 2237 $tax_breakdown = json_decode($breakdown_info['breakdown'], true);
1869 - $tax_breakdown = is_array($tax_breakdown) && count($tax_breakdown) > 0 ? $tax_breakdown : array();
2238 + $tax_breakdown = is_array($tax_breakdown) && $tax_breakdown ? $tax_breakdown : array();
1870 2239 $base_aliq = $breakdown_info['aliq'];
1871 2240 }
1872 2241 }
1873 2242 }
@@ -1908,9 +2277,9 @@
1908 2277 <div class="vbdiscenter-entry">
1909 2278 <span class="vbdiscenter-label"><?php echo JText::translate('VBTOTALVAT'); ?>:</span><span class="vbdiscenter-value"><?php echo $currencyname; ?> <input type="number" step="any" name="tot_taxes" value="<?php echo $row['tot_taxes']; ?>" size="4"/></span>
1910 2279 </div>
1911 2280 <?php
1912 - if (count($tax_breakdown)) {
2281 + if ($tax_breakdown) {
1913 2282 foreach ($tax_breakdown as $tbkk => $tbkv) {
1914 2283 $tax_break_cost = $row['tot_taxes'] * floatval($tbkv['aliq']) / $base_aliq;
1915 2284 ?>
1916 2285 <div class="vbdiscenter-entry vbdiscenter-entry-breakdown">
@@ -1928,9 +2297,19 @@
1928 2297 </div>
1929 2298 <div class="vbdiscenter-entry">
1930 2299 <span class="vbdiscenter-label hasTooltip"<?php echo !empty($otachannel_name) ? ' title="'.$otachannel_name.'"' : ''; ?>><?php echo JText::translate('VBTOTALCOMMISSIONS'); ?>:</span><span class="vbdiscenter-value"><?php echo $currencyname; ?> <input type="number" step="any" name="cmms" value="<?php echo $row['cmms']; ?>" size="4"/></span>
1931 2300 </div>
2301 + <?php
2302 + if ($booking_has_damage_dep) {
2303 + // allow to overwrite (or see) the amount of damage deposit
2304 + ?>
1932 2305 <div class="vbdiscenter-entry">
2306 + <span class="vbdiscenter-label"><?php echo JText::translate('VBO_DAMAGE_DEPOSIT'); ?>:</span><span class="vbdiscenter-value"><?php echo $currencyname; ?> <input type="number" step="any" name="tot_damage_dep" value="<?php echo $row['tot_damage_dep']; ?>" min="0" size="4"/></span>
2307 + </div>
2308 + <?php
2309 + }
2310 + ?>
2311 + <div class="vbdiscenter-entry">
1933 2312 <span class="vbdiscenter-label"><?php echo JText::translate('VBAPPLYDISCOUNT'); ?>:</span><span class="vbdiscenter-value"><?php echo $currencyname; ?> <input type="number" step="any" name="admindisc" value="<?php echo isset($expcoupon) ? (float)$expcoupon[1] : ''; ?>" size="4"/></span>
1934 2313 </div>
1935 2314 <div class="vbdiscenter-entrycentered">
1936 2315 <button type="submit" class="btn btn-success"><?php echo JText::translate('VBAPPLYDISCOUNTSAVE'); ?></button>
@@ -1937,9 +2316,26 @@
1937 2316 </div>
1938 2317 </div>
1939 2318 </td>
1940 2319 <td>
1941 - <span class="vbo-bookingdet-summary-cost"><?php echo (strlen($otacurrency) > 0 ? '('.$otacurrency.') '.$currencyname : $currencyname); ?> <?php echo VikBooking::numberFormat($row['total']); ?></span>
2320 + <span class="vbo-bookingdet-summary-cost"><?php echo (!empty($otacurrency) && $otacurrency != $currencyname ? '('.$otacurrency.') ' . $currencyname : $currencyname); ?> <?php echo VikBooking::numberFormat($row['total']); ?></span>
2321 + <?php
2322 + /**
2323 + * Display the information about the expected payout, if available and eligible.
2324 + *
2325 + * @since 1.18.4 (J) - 1.8.4 (WP)
2326 + */
2327 + if ($row['status'] == 'confirmed' && !empty($row['channel']) && !empty($row['idorderota']) && $row['checkout'] >= $current_ts && !empty($ota_type_data['expected_payout'])) {
2328 + ?>
2329 + <div class="vbo-bookingdet-exp-payout">
2330 + <span class="vbo-bookingdet-exp-payout-lbl"><?php echo JText::translate('VBO_EXPECTED_PAYOUT'); ?></span>
2331 + <span class="vbo-bookingdet-exp-payout-txt"><?php
2332 + echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($ota_type_data['expected_payout']), (!empty($otacurrency) && $otacurrency != $currencyname ? $otacurrency : $currencysymb));
2333 + ?></span>
2334 + </div>
2335 + <?php
2336 + }
2337 + ?>
1942 2338 </td>
1943 2339 </tr>
1944 2340 <?php
1945 2341 if ($row['total'] > 0 && !$row['closure']) {
@@ -1944,8 +2340,12 @@
1944 2340 <?php
1945 2341 if ($row['total'] > 0 && !$row['closure']) {
1946 2342 $has_paid = ($row['totpaid'] > 0);
1947 2343 $diff_to_pay = $has_paid ? ($row['total'] - $row['totpaid']) : 0;
2344 + if ($diff_to_pay && !empty($row['idorderota']) && !empty($row['channel']) && round($row['cmms'], 2) == round($diff_to_pay, 2)) {
2345 + // the outstanding balance is equal to the OTA commissions amount
2346 + $diff_to_pay = 0;
2347 + }
1948 2348 if ($has_paid || $row['status'] != 'standby') {
1949 2349 ?>
1950 2350 <tr class="vbo-bookingdet-summary-totpaid">
1951 2351 <td>&nbsp;</td>
@@ -2003,9 +2403,9 @@
2003 2403 </tr>
2004 2404 <?php
2005 2405 }
2006 2406 }
2007 - if (!$printreceipt && $row['status'] == 'confirmed' && VikBooking::multiplePayments() && is_array($payment) && !empty($payment['id']) && $row['checkout'] > time()) {
2407 + if (!$printreceipt && $row['status'] == 'confirmed' && VikBooking::multiplePayments() && is_array($payment) && !empty($payment['id']) && $row['checkout'] > strtotime('-10 days')) {
2008 2408 /**
2009 2409 * The amount payable can be modified by the admin.
2010 2410 *
2011 2411 * @since 1.14 (J) - 1.4.0 (WP)
@@ -2111,9 +2511,9 @@
2111 2511 <?php
2112 2512 if (!$row['closure']) {
2113 2513 ?>
2114 2514 <div class="vbo-bookingdet-admin-entry">
2115 - <label for="vbo-searchcust"><?php echo JText::translate(count($customer) ? 'VBOASSIGNNEWCUST' : 'VBFILLCUSTFIELDS'); ?></label>
2515 + <label for="vbo-searchcust"><?php echo JText::translate($customer ? 'VBOASSIGNNEWCUST' : 'VBFILLCUSTFIELDS'); ?></label>
2116 2516 <span style="display: block;"><?php echo JText::translate('VBOSEARCHEXISTCUST'); ?></span>
2117 2517 <span class="vbo-eorder-assigncust" style="margin-bottom: 1px;">
2118 2518 <input type="text" id="vbo-searchcust" autocomplete="off" value="" placeholder="<?php echo JText::translate('VBOSEARCHCUSTBY'); ?>" size="30" style="margin-bottom: 0;" />
2119 2519 </span>
@@ -2149,11 +2549,11 @@
2149 2549 <?php
2150 2550 }
2151 2551 $chpayment = '';
2152 2552 if ($payments) {
2153 - $chpayment = '<div><select name="newpayment" id="newpayment" onchange="changePayment();"><option value="">'.JText::translate('VBCHANGEPAYLABEL').'</option>';
2553 + $chpayment = '<div><select name="newpayment" id="newpayment" onchange="changePayment();"><option value="">- ' . JText::translate('VBCHANGEPAYLABEL') . ' -</option>';
2154 2554 foreach ($payments as $pay) {
2155 - $chpayment .= '<option value="'.$pay['id'].'">'.(is_array($payment) && $payment['id'] == $pay['id'] ? ' ::' : '').$pay['name'].'</option>';
2555 + $chpayment .= '<option value="' . $pay['id'] . '">' . (is_array($payment) && $payment['id'] == $pay['id'] ? '- ' : '') . $pay['name'] . '</option>';
2156 2556 }
2157 2557 $chpayment .= '</select></div>';
2158 2558 }
2159 2559 echo $chpayment;
@@ -2246,9 +2646,9 @@
2246 2646 } else {
2247 2647 $channelparts = explode('_', $row['channel']);
2248 2648 // check if this is a meta search channel
2249 2649 $is_meta_search = false;
2250 - if (preg_match("/(customer).*[0-9]$/", $channelparts[0]) || !strcasecmp($channelparts[0], 'googlehotel') || !strcasecmp($channelparts[0], 'trivago')) {
2650 + if (preg_match("/(customer).*[0-9]$/", $channelparts[0]) || !strcasecmp($channelparts[0], 'googlehotel') || !strcasecmp($channelparts[0], 'googlevr') || !strcasecmp($channelparts[0], 'trivago')) {
2251 2651 $is_meta_search = empty($row['idorderota']);
2252 2652 }
2253 2653 if ($is_meta_search) {
2254 2654 // customer of type sales channel should use front-end reservation chat handler
@@ -2343,9 +2743,9 @@
2343 2743 }
2344 2744
2345 2745 if (!empty($row['paymentlog'])) {
2346 2746 $paylogs_lbl = JText::translate('VBPAYMENTLOGTOGGLE');
2347 - if (stripos($row['paymentlog'], 'card number') !== false && strpos($row['paymentlog'], '****') !== false) {
2747 + if ($may_have_cc_details) {
2348 2748 $paylogs_lbl = JText::translate('VBO_CREDIT_CARD');
2349 2749 }
2350 2750 ?>
2351 2751 <div class="vbo-bookingdet-noteslogs-btn" data-id="paylogs">
@@ -2360,9 +2760,9 @@
2360 2760 ?>
2361 2761 </div>
2362 2762 <div class="vbo-bookingdet-noteslogs-cont">
2363 2763 <div id="vbadminnotesdiv" class="vbo-extra-panel" style="display: block;">
2364 - <textarea name="adminnotes" class="vbadminnotestarea"><?php echo strip_tags((string)$row['adminnotes']); ?></textarea>
2764 + <textarea name="adminnotes" class="vbadminnotestarea"><?php echo JHtml::fetch('esc_textarea', (string) $row['adminnotes']); ?></textarea>
2365 2765 <input type="submit" name="updadmnotes" value="<?php echo JText::translate('VBADMINNOTESUPD'); ?>" class="btn btn-success" />
2366 2766 </div>
2367 2767 <?php
2368 2768 if ($takings) {
@@ -2386,9 +2786,9 @@
2386 2786 foreach (array_reverse($takings) as $hist) {
2387 2787 // calculate amount paid and description
2388 2788 $tn_amount_paid = 0;
2389 2789 $tn_amount_fees = 0;
2390 - $tn_description = strpos($hist['descr'], '<') !== false ? $hist['descr'] : preg_replace("/\R/", ' - ', $hist['descr']);
2790 + $tn_description = strpos((string) $hist['descr'], '<') !== false ? $hist['descr'] : preg_replace("/\R/", ' - ', (string) $hist['descr']);
2391 2791 if (is_object($hist['data'])) {
2392 2792 // calculate amount paid or amount refunded
2393 2793 if (($hist['data']->amount_paid ?? 0)) {
2394 2794 $tn_amount_paid = $hist['data']->amount_paid;
@@ -2419,12 +2819,29 @@
2419 2819 $booking_total_taking += $tn_amount_paid;
2420 2820 $booking_total_prfees += $tn_amount_fees;
2421 2821 ?>
2422 2822 <tr class="vbo-booking-takings-row">
2423 - <td><?php echo $currencysymb . ' ' . VikBooking::numberFormat($tn_amount_paid); ?></td>
2823 + <td>
2824 + <?php
2825 + echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($tn_amount_paid), $currencysymb);
2826 + if (!strcasecmp($hist['type'], 'PU')) {
2827 + /**
2828 + * Allow to edit or delete the history record.
2829 + *
2830 + * @since 1.16.10 (J) - 1.6.10 (WP)
2831 + */
2832 + ?>
2833 + <div class="vbo-booking-taking-cmds">
2834 + <button type="button" class="btn btn-small btn-primary vbo-booking-taking-edit vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $this->escape(JText::translate('VBMAINPAYMENTSEDIT')); ?>" data-historyid="<?php echo $hist['id']; ?>" data-amount="<?php echo $tn_amount_paid; ?>"><?php VikBookingIcons::e('edit'); ?></button>
2835 + <button type="button" class="btn btn-small btn-danger vbo-booking-taking-delete vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $this->escape(JText::translate('VBELIMINA')); ?>" data-historyid="<?php echo $hist['id']; ?>"><?php VikBookingIcons::e('times-circle'); ?></button>
2836 + </div>
2837 + <?php
2838 + }
2839 + ?>
2840 + </td>
2424 2841 <td><?php echo JHtml::fetch('date', $hist['dt'], 'Y-m-d H:i:s'); ?></td>
2425 2842 <td><?php echo $history_obj->validType($hist['type'], true); ?></td>
2426 - <td><?php echo $currencysymb . ' ' . VikBooking::numberFormat($tn_amount_fees); ?></td>
2843 + <td><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($tn_amount_fees), $currencysymb); ?></td>
2427 2844 <td><?php echo $tn_description; ?></td>
2428 2845 </tr>
2429 2846 <?php
2430 2847 }
@@ -2432,19 +2849,262 @@
2432 2849 </tbody>
2433 2850 <tfoot>
2434 2851 <tr>
2435 2852 <td colspan="2">
2436 - <span><?php echo $currencysymb . ' ' . VikBooking::numberFormat($booking_total_taking); ?></span>
2853 + <span><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($booking_total_taking), $currencysymb); ?></span>
2437 2854 </td>
2438 2855 <td></td>
2439 - <td colspan="2">
2440 - <span><?php echo $currencysymb . ' ' . VikBooking::numberFormat($booking_total_prfees); ?></span>
2856 + <td>
2857 + <span><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($booking_total_prfees), $currencysymb); ?></span>
2441 2858 </td>
2859 + <td>
2860 + <button type="button" class="btn btn-small btn-success vbo-takings-addnew"><?php VikBookingIcons::e('plus-circle'); ?> <?php echo JText::translate('VBO_REG_NEW_PAYMENT'); ?></button>
2861 + </td>
2442 2862 </tr>
2443 2863 </tfoot>
2444 2864 </table>
2445 2865 </div>
2866 +
2867 + <div class="vbo-eorder-newtaking-helper-wrap" style="display: none;">
2868 + <div class="vbo-eorder-newtaking-helper-cont">
2869 + <div class="vbo-admin-container vbo-admin-container-full vbo-admin-container-compact">
2870 + <div class="vbo-params-wrap">
2871 + <div class="vbo-params-container">
2872 + <div class="vbo-params-block">
2873 + <div class="vbo-param-container">
2874 + <div class="vbo-param-label">
2875 + <label for="new_taking_amount"><?php echo JText::translate('VBO_CC_AMOUNT'); ?></label>
2876 + </div>
2877 + <div class="vbo-param-setting">
2878 + <div class="vbo-input-currency-wrap">
2879 + <span><?php echo $currencysymb; ?></span>
2880 + <input type="number" id="new_taking_amount" value="" min="0" max="<?php echo $row['total']; ?>" step="any" />
2881 + </div>
2882 + <span class="vbo-param-setting-comment">
2883 + <?php echo JText::translate('VBOCUSTOMERCMMSONTOTAL') . ' ' . VikBooking::formatCurrencyNumber(VikBooking::numberFormat($row['total']), $currencysymb) . ' - ' . JText::translate('VBAMOUNTPAID') . ' ' . VikBooking::formatCurrencyNumber(VikBooking::numberFormat($row['totpaid']), $currencysymb); ?>
2884 + </span>
2885 + </div>
2886 + </div>
2887 + <div class="vbo-param-container">
2888 + <div class="vbo-param-label">
2889 + <label for="new_taking_descr"><?php echo JText::translate('VBLIBPAYNAME'); ?></label>
2890 + </div>
2891 + <div class="vbo-param-setting">
2892 + <select id="new_taking_payid">
2893 + <option value="">&nbsp;</option>
2894 + <?php
2895 + foreach ($this->payments as $paymeth) {
2896 + ?>
2897 + <option value="<?php echo $paymeth['id']; ?>"><?php echo $paymeth['name']; ?></option>
2898 + <?php
2899 + }
2900 + ?>
2901 + </select>
2902 + </div>
2903 + </div>
2904 + <div class="vbo-param-container vbo-param-nested" id="vbo-new-taking-custom-wrap">
2905 + <div class="vbo-param-label">&nbsp;</div>
2906 + <div class="vbo-param-setting">
2907 + <input id="new_taking_descr" type="text" value="" maxlength="200" placeholder="<?php echo $this->escape(JText::translate('VBPVIEWOPTIONALSTWO')); ?>" />
2908 + </div>
2909 + </div>
2910 + </div>
2911 + </div>
2912 + </div>
2913 + </div>
2914 + </div>
2915 + </div>
2916 +
2446 2917 </div>
2918 +
2919 + <script type="text/javascript">
2920 + jQuery(function() {
2921 +
2922 + jQuery('#new_taking_payid').on('change', function() {
2923 + if (jQuery(this).val()) {
2924 + jQuery('#vbo-new-taking-custom-wrap').hide();
2925 + jQuery('#new_taking_descr').val('');
2926 + } else {
2927 + jQuery('#vbo-new-taking-custom-wrap').show();
2928 + }
2929 + });
2930 +
2931 + jQuery('.vbo-takings-addnew').on('click', function() {
2932 + // build modal buttons
2933 + let footer_left = jQuery('<button></button>');
2934 + footer_left.addClass('btn');
2935 + footer_left.attr('type', 'button');
2936 + footer_left.text(Joomla.JText._('VBANNULLA'));
2937 + footer_left.on('click', function() {
2938 + // dismiss the modal
2939 + VBOCore.emitEvent('vbo-eorder-new-taking-dismiss');
2940 + });
2941 +
2942 + let footer_right = jQuery('<button></button>');
2943 + footer_right.addClass('btn btn-success');
2944 + footer_right.attr('type', 'button');
2945 + footer_right.text(Joomla.JText._('VBSAVE'));
2946 + footer_right.on('click', function() {
2947 + // disable button to prevent double submissions
2948 + let submit_btn = jQuery(this);
2949 + submit_btn.prop('disabled', true);
2950 + // make the request
2951 + VBOCore.doAjax(
2952 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.add_taking'); ?>",
2953 + {
2954 + bid: '<?php echo $row['id']; ?>',
2955 + amount: jQuery('#new_taking_amount').val(),
2956 + payid: jQuery('#new_taking_payid').val(),
2957 + descr: jQuery('#new_taking_descr').val(),
2958 + },
2959 + (success) => {
2960 + // dismiss the modal
2961 + VBOCore.emitEvent('vbo-eorder-new-taking-dismiss');
2962 + // reload the page by following the response URL
2963 + document.location.href = success.url;
2964 + },
2965 + (error) => {
2966 + // re-enable submit button
2967 + submit_btn.prop('disabled', false);
2968 + // display error message
2969 + alert(error.responseText);
2970 + }
2971 + );
2972 + });
2973 +
2974 + // display modal
2975 + let modal_body = VBOCore.displayModal({
2976 + suffix: 'vbo-new-taking',
2977 + extra_class: 'vbo-modal-rounded vbo-modal-dialog',
2978 + title: Joomla.JText._('VBO_REG_NEW_PAYMENT'),
2979 + body: '',
2980 + body_prepend: true,
2981 + footer_left: footer_left,
2982 + footer_right: footer_right,
2983 + draggable: true,
2984 + dismiss_event: 'vbo-eorder-new-taking-dismiss',
2985 + onDismiss: () => {
2986 + jQuery('.vbo-eorder-newtaking-helper-cont').appendTo(
2987 + jQuery('.vbo-eorder-newtaking-helper-wrap')
2988 + );
2989 + },
2990 + });
2991 +
2992 + // reset helper input values
2993 + jQuery('#new_taking_amount').val('');
2994 + jQuery('#new_taking_payid').prop('disabled', false).val('').trigger('change');
2995 + jQuery('#new_taking_descr').val('');
2996 +
2997 + // append modal body
2998 + jQuery('.vbo-eorder-newtaking-helper-cont').appendTo(modal_body);
2999 + });
3000 +
3001 + jQuery('.vbo-booking-taking-edit').on('click', function() {
3002 + let edit_btn = jQuery(this);
3003 + let history_id = edit_btn.attr('data-historyid');
3004 + let curr_amount_paid = edit_btn.attr('data-amount');
3005 + let curr_pay_descr = edit_btn.closest('tr.vbo-booking-takings-row').find('td').last().text();
3006 +
3007 + // build modal buttons
3008 + let footer_left = jQuery('<button></button>');
3009 + footer_left.addClass('btn');
3010 + footer_left.attr('type', 'button');
3011 + footer_left.text(Joomla.JText._('VBANNULLA'));
3012 + footer_left.on('click', function() {
3013 + // dismiss the modal
3014 + VBOCore.emitEvent('vbo-eorder-edit-taking-dismiss');
3015 + });
3016 +
3017 + let footer_right = jQuery('<button></button>');
3018 + footer_right.addClass('btn btn-success');
3019 + footer_right.attr('type', 'button');
3020 + footer_right.text(Joomla.JText._('VBO_UPDATE_PAYMENT'));
3021 + footer_right.on('click', function() {
3022 + // disable button to prevent double submissions
3023 + let submit_btn = jQuery(this);
3024 + submit_btn.prop('disabled', true);
3025 + // make the request
3026 + VBOCore.doAjax(
3027 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.update_taking'); ?>",
3028 + {
3029 + bid: '<?php echo $row['id']; ?>',
3030 + hid: history_id,
3031 + amount: jQuery('#new_taking_amount').val(),
3032 + descr: jQuery('#new_taking_descr').val(),
3033 + htype: 'PU',
3034 + },
3035 + (success) => {
3036 + // dismiss the modal
3037 + VBOCore.emitEvent('vbo-eorder-edit-taking-dismiss');
3038 + // reload the page by following the response URL
3039 + document.location.href = success.url;
3040 + },
3041 + (error) => {
3042 + // re-enable submit button
3043 + submit_btn.prop('disabled', false);
3044 + // display error message
3045 + alert(error.responseText);
3046 + }
3047 + );
3048 + });
3049 +
3050 + // display modal
3051 + let modal_body = VBOCore.displayModal({
3052 + suffix: 'vbo-edit-taking',
3053 + extra_class: 'vbo-modal-rounded vbo-modal-dialog',
3054 + title: Joomla.JText._('VBO_UPDATE_PAYMENT'),
3055 + body: '',
3056 + body_prepend: true,
3057 + footer_left: footer_left,
3058 + footer_right: footer_right,
3059 + draggable: true,
3060 + dismiss_event: 'vbo-eorder-edit-taking-dismiss',
3061 + onDismiss: () => {
3062 + jQuery('.vbo-eorder-newtaking-helper-cont').appendTo(
3063 + jQuery('.vbo-eorder-newtaking-helper-wrap')
3064 + );
3065 + },
3066 + });
3067 +
3068 + // populate helper input values
3069 + jQuery('#new_taking_amount').val(curr_amount_paid);
3070 + jQuery('#new_taking_payid').val('').trigger('change').prop('disabled', true);
3071 + jQuery('#new_taking_descr').val(curr_pay_descr);
3072 +
3073 + // append modal body
3074 + jQuery('.vbo-eorder-newtaking-helper-cont').appendTo(modal_body);
3075 + });
3076 +
3077 + jQuery('.vbo-booking-taking-delete').on('click', function() {
3078 + if (!confirm(Joomla.JText._('VBDELCONFIRM'))) {
3079 + return false;
3080 + }
3081 + let del_btn = jQuery(this);
3082 + del_btn.prop('disabled', true);
3083 + let history_id = del_btn.attr('data-historyid');
3084 + // make the request
3085 + VBOCore.doAjax(
3086 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.delete_history_record'); ?>",
3087 + {
3088 + bid: '<?php echo $row['id']; ?>',
3089 + hid: history_id,
3090 + htype: 'PU',
3091 + },
3092 + (success) => {
3093 + // reload the page by following the response URL
3094 + document.location.href = success.url;
3095 + },
3096 + (error) => {
3097 + // re-enable delete button
3098 + del_btn.prop('disabled', false);
3099 + // display error message
3100 + alert(error.responseText);
3101 + }
3102 + );
3103 + });
3104 +
3105 + });
3106 + </script>
2447 3107 <?php
2448 3108 }
2449 3109 if ($history) {
2450 3110 ?>
@@ -2462,9 +3122,9 @@
2462 3122 </thead>
2463 3123 <tbody>
2464 3124 <?php
2465 3125 foreach ($history as $hist) {
2466 - $hdescr = strpos($hist['descr'], '<') !== false ? $hist['descr'] : nl2br($hist['descr']);
3126 + $hdescr = strpos((string) $hist['descr'], '<') !== false ? $hist['descr'] : nl2br((string) $hist['descr']);
2467 3127 ?>
2468 3128 <tr class="vbo-booking-history-row">
2469 3129 <td><?php echo $history_obj->validType($hist['type'], true); ?></td>
2470 3130 <td><?php echo JHtml::fetch('date', $hist['dt'], 'Y-m-d H:i:s'); ?></td>
@@ -2517,21 +3177,419 @@
2517 3177 *
2518 3178 * @since 1.13
2519 3179 */
2520 3180 $cardlimit = mktime(23, 59, 59, $checkout_info['mon'], ($checkout_info['mday'] + 10), $checkout_info['year']);
2521 - if (time() < $cardlimit) {
2522 - $plain_log = htmlspecialchars($row['paymentlog']);
2523 - if (stripos($plain_log, 'card number') !== false && strpos($plain_log, '****') !== false) {
2524 - // log contains credit card details
2525 - // Prepare modal (Credit Card Details)
2526 - array_push($vbo_modals_html, $vbo_app->getJmodalHtml('vbo-vcm-pcid', JText::translate('GETFULLCARDDETAILS'), '', 'width: 80%; height: 60%; margin-left: -40%; top: 20% !important;'));
2527 - // end Prepare modal
3181 + if ($current_ts < $cardlimit && $may_have_cc_details) {
3182 + // log contains credit card details
3183 + // prepare modal (Credit Card Details)
3184 + array_push($vbo_modals_html, $vbo_app->getJmodalHtml('vbo-vcm-pcid', JText::translate('GETFULLCARDDETAILS'), '', 'width: 80%; height: 60%; margin-left: -40%; top: 20% !important;'));
2528 3185 ?>
2529 3186 <div class="vcm-notif-pcidrq-container">
2530 3187 <button type="button" class="btn vbo-config-btn vcm-pcid-launch" onclick="vboOpenJModal('vbo-vcm-pcid', 'index.php?option=com_vikchannelmanager&task=execpcid&channel_source=<?php echo $channel_source; ?>&otaid=<?php echo $row['idorderota']; ?>&tmpl=component');"><?php VikBookingIcons::e('credit-card'); ?> <?php echo JText::translate('GETFULLCARDDETAILS'); ?></button>
2531 3188 </div>
2532 3189 <?php
3190 + /**
3191 + * Check for automatic payment schedules support when an active CC/VCC is available.
3192 + *
3193 + * @since 1.16.10 (J) - 1.6.10 (WP)
3194 + */
3195 + $can_payschedule = false;
3196 + $active_payschedules = [];
3197 + try {
3198 + $processor = VBOModelReservation::getInstance($row, true)->getPaymentProcessor();
3199 + if (method_exists($processor, 'isDirectChargeSupported') && $processor->isDirectChargeSupported()) {
3200 + // turn flag on
3201 + $can_payschedule = true;
3202 + // load current schedules
3203 + $active_payschedules = VBOModelPayschedules::getInstance()->getItems([
3204 + 'idorder' => [
3205 + 'value' => $row['id'],
3206 + ],
3207 + ]);
3208 + }
3209 + } catch (Exception $e) {
3210 + // do nothing
2533 3211 }
3212 +
3213 + if ($can_payschedule) {
3214 + // calculate the total amount scheduled
3215 + $tot_scheduled = 0;
3216 + foreach ($active_payschedules as $active_payschedule) {
3217 + if (!$active_payschedule->status) {
3218 + $tot_scheduled += $active_payschedule->amount;
3219 + }
3220 + }
3221 +
3222 + // display action buttons for payment schedules
3223 + ?>
3224 + <div class="vbo-eorder-payschedules-wrap">
3225 + <button type="button" class="btn btn-<?php echo $active_payschedules || $automatic_payment_collection ? 'success' : 'primary'; ?> vbo-context-menu-btn vbo-context-menu-payschedules">
3226 + <span class="vbo-context-menu-lbl"><?php echo JText::translate('VBO_AUTOPAY_SCHEDULED'); ?></span>
3227 + <span class="vbo-context-menu-ico"><?php VikBookingIcons::e('sort-down'); ?></span>
3228 + </button>
3229 + </div>
3230 +
3231 + <div class="vbo-eorder-newpayschedule-helper-wrap" style="display: none;">
3232 + <div class="vbo-eorder-newpayschedule-helper-cont" data-auto-pay-schedules="<?php echo (int) $automatic_payment_collection; ?>">
3233 + <p class="info" data-auto-pay-schedules="help" style="display: none;"><?php echo JText::translate('VBO_AUTO_PAY_COLLECT_HELP'); ?></p>
3234 + <div class="vbo-admin-container vbo-admin-container-full vbo-admin-container-compact">
3235 + <div class="vbo-params-wrap">
3236 + <div class="vbo-params-container">
3237 + <div class="vbo-params-block">
3238 + <div class="vbo-param-container">
3239 + <div class="vbo-param-label">
3240 + <label for="new_payschedule_amount"><?php echo JText::translate('VBO_CC_AMOUNT'); ?></label>
3241 + </div>
3242 + <div class="vbo-param-setting">
3243 + <div class="vbo-input-currency-wrap">
3244 + <span><?php echo $currencysymb; ?></span>
3245 + <input type="number" id="new_payschedule_amount" value="<?php echo $tot_scheduled < $row['total'] ? ($row['total'] - $tot_scheduled) : 0; ?>" min="0" step="any" />
3246 + </div>
3247 + <span class="vbo-param-setting-comment"><?php echo JText::translate('VBEDITORDERNINE') . ' ' . VikBooking::formatCurrencyNumber(VikBooking::numberFormat($row['total']), $currencysymb); ?></span>
3248 + </div>
3249 + </div>
3250 + <div class="vbo-param-container">
3251 + <div class="vbo-param-label">
3252 + <label for="new_payschedule_fordt"><?php echo JText::translate('VBPVIEWORDERSONE'); ?></label>
3253 + </div>
3254 + <div class="vbo-param-setting">
3255 + <?php echo $vbo_app->getCalendar(date($df, $row['checkin']), 'new_payschedule_fordt', 'new_payschedule_fordt', $nowdf, array('class'=>'', 'size'=>'10', 'maxlength'=>'19', 'todayBtn' => 'true')); ?>
3256 + </div>
3257 + </div>
3258 + <div class="vbo-param-container">
3259 + <div class="vbo-param-label">
3260 + <label for="new_payschedule_fordtime"><?php echo JText::translate('VBO_REMINDER_TIME'); ?></label>
3261 + </div>
3262 + <div class="vbo-param-setting">
3263 + <select id="new_payschedule_fordtime">
3264 + <?php
3265 + for ($h = 0; $h < 24; $h++) {
3266 + $say_htime = ($h < 10 ? '0' : '') . $h;
3267 + ?>
3268 + <option value="<?php echo $say_htime; ?>:00"><?php echo $say_htime; ?>:00</option>
3269 + <option value="<?php echo $say_htime; ?>:30"><?php echo $say_htime; ?>:30</option>
3270 + <?php
3271 + }
3272 + ?>
3273 + </select>
3274 + </div>
3275 + </div>
3276 + </div>
3277 + </div>
3278 + </div>
3279 + </div>
3280 + </div>
3281 + </div>
3282 +
3283 + <script type="text/javascript">
3284 +
3285 + // build base buttons list for context menu
3286 + var vbo_payschedule_btns = [
3287 + // toggle automatic payment collection scheduling for all bookings
3288 + {
3289 + activeState: <?php echo $automatic_payment_collection ? 'true' : 'false'; ?>,
3290 + text: <?php echo json_encode(JText::translate('VBO_ENABLED_FOR_ALL_BOOKINGS')); ?>,
3291 + separator: true,
3292 + icon: function() {
3293 + return this.activeState ? '<?php echo VikBookingIcons::i('toggle-on', 'vbo-enabled-icon'); ?>' : '<?php echo VikBookingIcons::i('toggle-off'); ?>';
3294 + },
3295 + action: function(root, event) {
3296 + // create reference to button object
3297 + let that = this;
3298 +
3299 + // get new state
3300 + let newState = !this.activeState;
3301 +
3302 + // determine confirmation message
3303 + let confirm_enable = <?php echo json_encode(JText::translate('VBO_AUTO_PAY_COLLECT_ENABLE_CONF')); ?>;
3304 + let confirm_disable = <?php echo json_encode(JText::translate('VBO_AUTO_PAY_COLLECT_DISABLE_CONF')); ?>;
3305 +
3306 + if ((newState && confirm(confirm_enable)) || (!newState && confirm(confirm_disable))) {
3307 + // silently update configuration setting
3308 + VBOCore.doAjax(
3309 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=configuration.update'); ?>",
3310 + {
3311 + settings: {
3312 + auto_payment_collection: newState ? 1 : 0,
3313 + }
3314 + },
3315 + (success) => {
3316 + // toggle state
3317 + that.activeState = newState;
3318 + // update data attribute with the newly saved configuration value
3319 + let statusEl = document.querySelector('.vbo-eorder-newpayschedule-helper-cont[data-auto-pay-schedules]');
3320 + if (statusEl) {
3321 + statusEl.setAttribute('data-auto-pay-schedules', (newState ? 1 : 0));
3322 + }
3323 + },
3324 + (error) => {
3325 + console.error(error);
3326 + }
3327 + );
3328 + }
3329 + },
3330 + },
3331 + // add new payment schedule button
3332 + {
3333 + icon: '<?php echo VikBookingIcons::i('plus-circle'); ?>',
3334 + text: Joomla.JText._('VBO_NEW_PAYSCHEDULE'),
3335 + separator: true,
3336 + action: (root, config) => {
3337 + // build modal buttons
3338 + let footer_left = jQuery('<button></button>');
3339 + footer_left.addClass('btn');
3340 + footer_left.attr('type', 'button');
3341 + footer_left.text(Joomla.JText._('VBANNULLA'));
3342 + footer_left.on('click', function() {
3343 + // dismiss the modal
3344 + VBOCore.emitEvent('vbo-eorder-new-payschedule-dismiss');
3345 + });
3346 +
3347 + let footer_right = jQuery('<button></button>');
3348 + footer_right.addClass('btn btn-success');
3349 + footer_right.attr('type', 'button');
3350 + footer_right.text(Joomla.JText._('VBSAVE'));
3351 + footer_right.on('click', function() {
3352 + // disable button to prevent double submissions
3353 + let submit_btn = jQuery(this);
3354 + submit_btn.prop('disabled', true);
3355 + // make the request
3356 + VBOCore.doAjax(
3357 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=payschedules.save'); ?>",
3358 + {
3359 + bid: '<?php echo $row['id']; ?>',
3360 + amount: jQuery('#new_payschedule_amount').val(),
3361 + dt: jQuery('#new_payschedule_fordt').val(),
3362 + time: jQuery('#new_payschedule_fordtime').val(),
3363 + },
3364 + (new_pay_schedules) => {
3365 + // re-enable submit button
3366 + submit_btn.prop('disabled', false);
3367 + // dismiss the modal
3368 + VBOCore.emitEvent('vbo-eorder-new-payschedule-dismiss');
3369 + // populate the new schedule buttons
3370 + let new_pay_schedule_btns = [];
3371 + // push button to toggle automatic payment collection scheduling for all bookings
3372 + new_pay_schedule_btns.push(vbo_payschedule_btns[0]);
3373 + // push button to add a new schedule
3374 + new_pay_schedule_btns.push(vbo_payschedule_btns[1]);
3375 + // push all active schedules
3376 + new_pay_schedules.forEach((payschedule) => {
3377 + new_pay_schedule_btns.push(
3378 + vboBuildPayScheduleBtn(payschedule)
3379 + );
3380 + });
3381 + // update trigger button class
3382 + if (new_pay_schedules.length) {
3383 + jQuery('.vbo-context-menu-payschedules').removeClass('btn-primary').addClass('btn-success');
3384 + } else {
3385 + jQuery('.vbo-context-menu-payschedules').removeClass('btn-success').addClass('btn-primary');
3386 + }
3387 + // set new buttons in context menu
3388 + jQuery('.vbo-context-menu-payschedules').vboContextMenu('buttons', new_pay_schedule_btns);
3389 + },
3390 + (error) => {
3391 + // re-enable submit button
3392 + submit_btn.prop('disabled', false);
3393 + // display error message
3394 + alert(error.responseText);
3395 + }
3396 + );
3397 + });
3398 +
3399 + // display modal
3400 + let modal_body = VBOCore.displayModal({
3401 + suffix: 'vbo-new-pay-schedule',
3402 + extra_class: 'vbo-modal-rounded vbo-modal-dialog',
3403 + title: Joomla.JText._('VBO_NEW_PAYSCHEDULE'),
3404 + body: '',
3405 + body_prepend: true,
3406 + footer_left: footer_left,
3407 + footer_right: footer_right,
3408 + draggable: true,
3409 + dismiss_event: 'vbo-eorder-new-payschedule-dismiss',
3410 + onDismiss: () => {
3411 + jQuery('.vbo-eorder-newpayschedule-helper-cont').appendTo(
3412 + jQuery('.vbo-eorder-newpayschedule-helper-wrap')
3413 + );
3414 + },
3415 + });
3416 +
3417 + // check current automatic payment schedules status
3418 + let statusEl = document.querySelector('.vbo-eorder-newpayschedule-helper-cont[data-auto-pay-schedules]');
3419 + if (statusEl) {
3420 + let autoPayStatus = statusEl.getAttribute('data-auto-pay-schedules');
3421 + if (autoPayStatus == 1) {
3422 + // show help element
3423 + statusEl.querySelector('[data-auto-pay-schedules="help"]').style.display = '';
3424 + } else {
3425 + // hide help element
3426 + statusEl.querySelector('[data-auto-pay-schedules="help"]').style.display = 'none';
3427 + }
3428 + }
3429 +
3430 + // append modal body
3431 + jQuery('.vbo-eorder-newpayschedule-helper-cont').appendTo(modal_body);
3432 + },
3433 + }
3434 + ];
3435 +
3436 + /**
3437 + * Builds a button object for the context menu from the given payment schedule object.
3438 + */
3439 + function vboBuildPayScheduleBtn(payschedule) {
3440 + let icn_to_process = '<?php echo VikBookingIcons::i('clock'); ?>';
3441 + let icn_processed = '<?php echo VikBookingIcons::i('check-circle'); ?>';
3442 + let icn_error = '<?php echo VikBookingIcons::i('exclamation-triangle'); ?>';
3443 + let dt_unit = (payschedule.dt_diff.days || payschedule.dt_diff.weeks || payschedule.dt_diff.months || payschedule.dt_diff.years || 0);
3444 + let say_dt = !payschedule.dt_diff.past && dt_unit > 0 ? payschedule.dt_diff.relative : payschedule?.fordt;
3445 + if (!payschedule.dt_diff.past && (payschedule.dt_diff.sameday || payschedule.dt_diff.today)) {
3446 + say_dt = payschedule.dt_diff.relative + ' (' + payschedule.time_hm + ')';
3447 + } else if (!payschedule.dt_diff.past && payschedule.dt_diff.tomorrow) {
3448 + say_dt = payschedule.dt_diff.relative + ' (' + payschedule.time_hm + ')';
3449 + }
3450 +
3451 + return {
3452 + icon: ((payschedule.status || 0) > 0 ? (payschedule.status == 1 ? icn_processed : icn_error) : icn_to_process),
3453 + text: VBOCore.getCurrency().format(payschedule?.amount) + ' - ' + say_dt,
3454 + class: ((payschedule.status || 0) == 1 ? 'vbo-context-menu-entry-success' : ((payschedule.status || 0) == 2 ? 'vbo-context-menu-entry-warning' : 'vbo-context-menu-entry-danger')),
3455 + separator: false,
3456 + action: (root, config) => {
3457 + if (payschedule.status == 0 || !payschedule.logs) {
3458 + // confirm deletion without displaying anything
3459 + if (confirm(Joomla.JText._('VBDELCONFIRM'))) {
3460 + // make the request
3461 + VBOCore.doAjax(
3462 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=payschedules.delete'); ?>",
3463 + {
3464 + bid: payschedule.idorder,
3465 + schedule_id: payschedule.id,
3466 + },
3467 + (new_pay_schedules) => {
3468 + // populate the new schedule buttons
3469 + let new_pay_schedule_btns = [];
3470 + // push button to toggle automatic payment collection scheduling for all bookings
3471 + new_pay_schedule_btns.push(vbo_payschedule_btns[0]);
3472 + // push button to add a new schedule
3473 + new_pay_schedule_btns.push(vbo_payschedule_btns[1]);
3474 + // push all active schedules
3475 + new_pay_schedules.forEach((payschedule) => {
3476 + new_pay_schedule_btns.push(
3477 + vboBuildPayScheduleBtn(payschedule)
3478 + );
3479 + });
3480 + // update trigger button class
3481 + if (new_pay_schedules.length) {
3482 + jQuery('.vbo-context-menu-payschedules').removeClass('btn-primary').addClass('btn-success');
3483 + } else {
3484 + jQuery('.vbo-context-menu-payschedules').removeClass('btn-success').addClass('btn-primary');
3485 + }
3486 + // set new buttons in context menu
3487 + jQuery('.vbo-context-menu-payschedules').vboContextMenu('buttons', new_pay_schedule_btns);
3488 + },
3489 + (error) => {
3490 + // display error message
3491 + alert(error.responseText);
3492 + }
3493 + );
3494 + }
3495 + } else {
3496 + // display modal on click with the execution logs
3497 +
3498 + // build modal buttons
3499 + let footer_left = jQuery('<button></button>');
3500 + footer_left.addClass('btn');
3501 + footer_left.attr('type', 'button');
3502 + footer_left.text(Joomla.JText._('VBANNULLA'));
3503 + footer_left.on('click', function() {
3504 + // dismiss the modal
3505 + VBOCore.emitEvent('vbo-eorder-payschedule-logs-dismiss');
3506 + });
3507 +
3508 + let footer_right = jQuery('<button></button>');
3509 + footer_right.addClass('btn btn-danger');
3510 + footer_right.attr('type', 'button');
3511 + footer_right.text(Joomla.JText._('VBELIMINA'));
3512 + footer_right.on('click', function() {
3513 + // confirm record deletion
3514 + if (confirm(Joomla.JText._('VBDELCONFIRM'))) {
3515 + // make the request
3516 + VBOCore.doAjax(
3517 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=payschedules.delete'); ?>",
3518 + {
3519 + bid: payschedule.idorder,
3520 + schedule_id: payschedule.id,
3521 + },
3522 + (new_pay_schedules) => {
3523 + // dismiss the modal
3524 + VBOCore.emitEvent('vbo-eorder-payschedule-logs-dismiss');
3525 + // populate the new schedule buttons
3526 + let new_pay_schedule_btns = [];
3527 + // push button to toggle automatic payment collection scheduling for all bookings
3528 + new_pay_schedule_btns.push(vbo_payschedule_btns[0]);
3529 + // push button to add a new schedule
3530 + new_pay_schedule_btns.push(vbo_payschedule_btns[1]);
3531 + // push all active schedules
3532 + new_pay_schedules.forEach((payschedule) => {
3533 + new_pay_schedule_btns.push(
3534 + vboBuildPayScheduleBtn(payschedule)
3535 + );
3536 + });
3537 + // update trigger button class
3538 + if (new_pay_schedules.length) {
3539 + jQuery('.vbo-context-menu-payschedules').removeClass('btn-primary').addClass('btn-success');
3540 + } else {
3541 + jQuery('.vbo-context-menu-payschedules').removeClass('btn-success').addClass('btn-primary');
3542 + }
3543 + // set new buttons in context menu
3544 + jQuery('.vbo-context-menu-payschedules').vboContextMenu('buttons', new_pay_schedule_btns);
3545 + },
3546 + (error) => {
3547 + // display error message
3548 + alert(error.responseText);
3549 + }
3550 + );
3551 + }
3552 + });
3553 +
3554 + // display modal
3555 + let modal_body = VBOCore.displayModal({
3556 + suffix: 'vbo-pay-schedule-logs',
3557 + extra_class: 'vbo-modal-rounded vbo-modal-dialog',
3558 + title: payschedule.fordt,
3559 + body: '<pre>' + payschedule.logs + '</pre>',
3560 + footer_left: footer_left,
3561 + footer_right: footer_right,
3562 + draggable: true,
3563 + dismiss_event: 'vbo-eorder-payschedule-logs-dismiss',
3564 + });
3565 + }
3566 + },
3567 + };
3568 + }
3569 +
3570 + // build context menu
3571 + jQuery(function() {
3572 +
3573 + // append active payment schedule buttons
3574 + let active_payschedules = <?php echo json_encode($active_payschedules); ?>;
3575 + active_payschedules.forEach((payschedule) => {
3576 + vbo_payschedule_btns.push(
3577 + vboBuildPayScheduleBtn(payschedule)
3578 + );
3579 + });
3580 +
3581 + // render context menu
3582 + jQuery('.vbo-context-menu-payschedules').vboContextMenu({
3583 + placement: 'bottom-left',
3584 + buttons: vbo_payschedule_btns,
3585 + });
3586 +
3587 + });
3588 + </script>
3589 + <?php
3590 + }
3591 + // end automatic payment schedules
2534 3592 }
2535 3593 }
2536 3594 } elseif (empty($row['idorderota']) && !empty($row['paymentlog']) && $row['status'] == 'standby') {
2537 3595 ?>
@@ -2560,9 +3618,9 @@
2560 3618 <?php
2561 3619 // render the reporting-invalidcc layout
2562 3620 echo JLayoutHelper::render('reporting.invalidcc', $layout_data, null, [
2563 3621 'component' => 'com_vikchannelmanager',
2564 - 'client' => 'administrator',
3622 + 'client' => 'admin',
2565 3623 ]);
2566 3624 ?>
2567 3625 </div>
2568 3626 <?php
@@ -2653,90 +3711,94 @@
2653 3711 <input type="hidden" name="cid[]" value="<?php echo $row['id']; ?>" />
2654 3712 <input type="hidden" name="invnotes" id="invnotes-hid" value="" />
2655 3713 </form>
2656 3714
2657 -<div class="vbo-modal-overlay-block vbo-modal-overlay-block-sms-email">
2658 - <a class="vbo-modal-overlay-close" href="javascript: void(0);"></a>
2659 - <div class="vbo-modal-overlay-content vbo-modal-overlay-content-large vbo-modal-overlay-content-sms-email">
3715 +<div class="vbo-editorder-sms-email-send-helper" style="display: none;">
2660 3716
2661 - <div class="vbo-modal-overlay-content-head vbo-modal-overlay-content-sms-email-head">
2662 - <h3>
2663 - <span></span>
2664 - <span class="vbo-modal-overlay-close-times" onclick="vboToggleSendEmail();">&times;</span>
2665 - </h3>
2666 - </div>
3717 + <div class="vbo-editorder-sms-send-wrap">
3718 + <form action="index.php?option=com_vikbooking" method="post" id="vbo-modal-form-sms">
3719 + <div class="vbo-admin-container vbo-admin-container-full vbo-admin-container-compact">
3720 + <div class="vbo-params-wrap">
3721 + <div class="vbo-params-container">
2667 3722
2668 - <div class="vbo-modal-overlay-content-body vbo-modal-overlay-content-body-scroll">
3723 + <div class="vbo-param-container vbo-param-container-full">
3724 + <div class="vbo-param-label">
3725 + <label for="smscont"><?php echo JText::translate('VBSENDSMSCUSTCONT'); ?></label>
3726 + </div>
3727 + <div class="vbo-param-setting">
3728 + <textarea name="smscont" id="smscont" style="width: 99%; min-width: 99%; max-width: 99%; height: 120px;"></textarea>
3729 + </div>
3730 + </div>
2669 3731
2670 - <div id="vbo-overlay-sms-cont" style="display: none;">
2671 - <h4 style="display: none;"><?php echo JText::translate('VBSENDSMSACTION'); ?>: <span id="smstophone-lbl"><?php echo $row['phone']; ?></span></h4>
2672 - <form action="index.php?option=com_vikbooking" method="post" id="vbo-modal-form-sms">
2673 - <div class="vbo-calendar-cfield-entry">
2674 - <label for="smscont"><?php echo JText::translate('VBSENDSMSCUSTCONT'); ?></label>
2675 - <span><textarea name="smscont" id="smscont" style="width: 99%; min-width: 99%;max-width: 99%; height: 120px;"></textarea></span>
2676 3732 </div>
2677 - <input type="hidden" name="phone" id="smstophone" value="<?php echo $row['phone']; ?>" />
2678 - <input type="hidden" name="goto" value="<?php echo urlencode('index.php?option=com_vikbooking&task=editorder&cid[]='.$row['id']); ?>" />
2679 - <input type="hidden" name="task" value="sendcustomsms" />
2680 - </form>
3733 + </div>
2681 3734 </div>
3735 + <input type="hidden" name="phone" id="smstophone" value="<?php echo $row['phone']; ?>" />
3736 + <input type="hidden" name="goto" value="<?php echo urlencode('index.php?option=com_vikbooking&task=editorder&cid[]='.$row['id']); ?>" />
3737 + <input type="hidden" name="task" value="sendcustomsms" />
3738 + </form>
3739 + </div>
2682 3740
2683 - <div id="vbo-overlay-email-cont" style="display: none;">
2684 - <h4 style="display: none;"><?php echo JText::translate('VBSENDEMAILACTION'); ?>: <span id="emailto-lbl"><?php echo $row['custmail']; ?></span></h4>
2685 - <form action="index.php?option=com_vikbooking" method="post" enctype="multipart/form-data" id="vbo-modal-form-email">
2686 - <input type="hidden" name="bid" value="<?php echo $row['id']; ?>" />
2687 - <?php
2688 - $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
2689 - $dbo->setQuery($q);
2690 - $cur_emtpl = $dbo->loadResult();
3741 + <div class="vbo-editorder-email-send-wrap">
3742 + <form action="index.php?option=com_vikbooking" method="post" enctype="multipart/form-data" id="vbo-modal-form-email">
3743 + <div class="vbo-admin-container vbo-admin-container-full vbo-admin-container-compact">
3744 + <div class="vbo-params-wrap">
3745 + <div class="vbo-params-container">
2691 3746
2692 - $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
2693 - $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
3747 + <?php
3748 + $cur_emtpl = VBOFactory::getConfig()->getArray('customemailtpls', []);
3749 + if ($cur_emtpl) {
3750 + ?>
3751 + <div class="vbo-param-container">
3752 + <div class="vbo-param-setting">
3753 + <select id="emtpl-customemail" onchange="vboLoadEmailTpl(this.value);">
3754 + <option value=""><?php echo JText::translate('VBEMAILCUSTFROMTPL'); ?></option>
3755 + <?php
3756 + foreach ($cur_emtpl as $emk => $emv) {
3757 + ?>
3758 + <optgroup label="<?php echo $emv['emailsubj']; ?>">
3759 + <option value="<?php echo $emk; ?>"><?php echo JText::translate('VBEMAILCUSTFROMTPLUSE'); ?></option>
3760 + <option value="rm<?php echo $emk; ?>"><?php echo JText::translate('VBEMAILCUSTFROMTPLRM'); ?></option>
3761 + </optgroup>
3762 + <?php
3763 + }
3764 + ?>
3765 + </select>
3766 + </div>
3767 + </div>
3768 + <?php
3769 + }
2694 3770
2695 - if ($cur_emtpl) {
2696 - ?>
2697 - <div class="vbo-calendar-custmail-tpls-wrap">
2698 - <select id="emtpl-customemail" onchange="vboLoadEmailTpl(this.value);">
2699 - <option value=""><?php echo JText::translate('VBEMAILCUSTFROMTPL'); ?></option>
2700 - <?php
2701 - foreach ($cur_emtpl as $emk => $emv) {
2702 - ?>
2703 - <optgroup label="<?php echo $emv['emailsubj']; ?>">
2704 - <option value="<?php echo $emk; ?>"><?php echo JText::translate('VBEMAILCUSTFROMTPLUSE'); ?></option>
2705 - <option value="rm<?php echo $emk; ?>"><?php echo JText::translate('VBEMAILCUSTFROMTPLRM'); ?></option>
2706 - </optgroup>
2707 - <?php
3771 + /**
3772 + * Load all conditional text special tags.
3773 + *
3774 + * @since 1.14 (J) - 1.4.0 (WP)
3775 + */
3776 + $extra_btns = [];
3777 + $condtext_tags = VikBooking::getConditionalRulesInstance()->getSpecialTags();
3778 + if ($condtext_tags) {
3779 + $condtext_tags = array_keys($condtext_tags);
3780 + foreach ($condtext_tags as $tag) {
3781 + array_push($extra_btns, '<button type="button" class="btn btn-secondary btn-small vbo-condtext-specialtag-btn" onclick="setSpecialTplTag(\'emailcont\', \'' . $tag . '\');">' . $tag . '</button>');
2708 3782 }
2709 - ?>
2710 - </select>
2711 - </div>
2712 - <?php
2713 - }
2714 -
2715 - /**
2716 - * Load all conditional text special tags.
2717 - *
2718 - * @since 1.14 (J) - 1.4.0 (WP)
2719 - */
2720 - $extra_btns = array();
2721 - $condtext_tags = VikBooking::getConditionalRulesInstance()->getSpecialTags();
2722 - if (count($condtext_tags)) {
2723 - $condtext_tags = array_keys($condtext_tags);
2724 - foreach ($condtext_tags as $tag) {
2725 - array_push($extra_btns, '<button type="button" class="btn btn-secondary btn-small vbo-condtext-specialtag-btn" onclick="setSpecialTplTag(\'emailcont\', \'' . $tag . '\');">' . $tag . '</button>');
2726 3783 }
2727 - }
2728 - //
2729 - ?>
2730 - <div class="vbo-calendar-cfields-wrap">
2731 - <div class="vbo-calendar-cfield-entry">
2732 - <label for="emailsubj"><?php echo JText::translate('VBSENDEMAILCUSTSUBJ'); ?></label>
2733 - <span><input type="text" name="emailsubj" id="emailsubj" value="" size="30" /></span>
3784 + ?>
3785 + <div class="vbo-param-container vbo-param-container-full">
3786 + <div class="vbo-param-label">
3787 + <label for="emailsubj"><?php echo JText::translate('VBSENDEMAILCUSTSUBJ'); ?></label>
3788 + </div>
3789 + <div class="vbo-param-setting">
3790 + <input type="text" name="emailsubj" id="emailsubj" value="" size="30" />
3791 + </div>
2734 3792 </div>
2735 - <div class="vbo-calendar-cfield-entry">
2736 - <label for="emailcont"><?php echo JText::translate('VBSENDEMAILCUSTCONT'); ?></label>
3793 +
3794 + <div class="vbo-param-container vbo-param-container-full">
3795 + <div class="vbo-param-label">
3796 + <label for="emailcont"><?php echo JText::translate('VBSENDEMAILCUSTCONT'); ?></label>
3797 + </div>
3798 + <div class="vbo-param-setting">
2737 3799 <?php
2738 - $special_tags_base = array(
3800 + $special_tags_base = [
2739 3801 '{customer_name}',
2740 3802 '{booking_id}',
2741 3803 '{checkin_date}',
2742 3804 '{checkout_date}',
@@ -2749,9 +3811,9 @@
2749 3811 '{total}',
2750 3812 '{total_paid}',
2751 3813 '{remaining_balance}',
2752 3814 '{booking_link}',
2753 - );
3815 + ];
2754 3816
2755 3817 $special_tags_base_html = '';
2756 3818 foreach ($special_tags_base as $sp_tag) {
2757 3819 $special_tags_base_html .= '<button type="button" class="btn btn-secondary btn-small" onclick="setSpecialTplTag(\'emailcont\', \'' . $sp_tag . '\');">' . $sp_tag . '</button>' . "\n";
@@ -2761,110 +3823,211 @@
2761 3823 * Use the rich text editor (visual editor) to build custom email messages.
2762 3824 *
2763 3825 * @since 1.15.0 (J) - 1.5.0 (WP)
2764 3826 */
2765 - $tarea_attr = array(
3827 + $tarea_attr = [
2766 3828 'id' => 'emailcont',
2767 3829 'rows' => '7',
2768 3830 'cols' => '170',
2769 3831 'style' => 'width: 99%; min-width: 99%; max-width: 99%; height: 120px; margin-bottom: 1px;',
2770 - );
2771 - $editor_opts = array(
2772 - 'modes' => array(
3832 + ];
3833 + $editor_opts = [
3834 + 'modes' => [
3835 + 'visual',
2773 3836 'text',
2774 - 'visual',
2775 - ),
2776 - );
3837 + ],
3838 + 'gen_ai' => [
3839 + 'booking' => $row,
3840 + 'customer' => $customer,
3841 + 'environment' => 'booking',
3842 + ],
3843 + ];
2777 3844 $editor_btns = $special_tags_base;
2778 - if (count($condtext_tags)) {
3845 + if ($condtext_tags) {
2779 3846 $editor_btns = array_merge($editor_btns, $condtext_tags);
2780 3847 }
3848 +
3849 + /**
3850 + * Trigger event to allow third-party plugins to add custom special tags.
3851 + *
3852 + * @since 1.16.10 (J) - 1.6.10 (WP)
3853 + */
3854 + $custom_btns = VBOFactory::getPlatform()->getDispatcher()->filter('onRenderCustomEmailSpecialTags', [$row, $editor_btns]);
3855 + foreach ($custom_btns as $custom_btn) {
3856 + // merge custom buttons
3857 + $editor_btns = array_merge($editor_btns, (array) $custom_btn);
3858 + }
3859 +
3860 + // make sure to filter empty buttons
3861 + $editor_btns = array_filter($editor_btns);
3862 +
3863 + // render visual editor
2781 3864 echo $vbo_app->renderVisualEditor('emailcont', '', $tarea_attr, $editor_opts, $editor_btns);
2782 3865 ?>
2783 - <div class="btn-group pull-left vbo-smstpl-bgroup vbo-custmail-bgroup vik-contentbuilder-textmode-sptags">
2784 - <?php echo $special_tags_base_html . "\n" . implode("\n", $extra_btns); ?>
3866 + <div class="btn-group pull-left vbo-smstpl-bgroup vbo-custmail-bgroup vik-contentbuilder-textmode-sptags">
3867 + <?php echo $special_tags_base_html . "\n" . implode("\n", $extra_btns); ?>
3868 + </div>
2785 3869 </div>
2786 3870 </div>
2787 - <div class="vbo-calendar-cfield-entry">
2788 - <label for="emailattch"><?php echo JText::translate('VBSENDEMAILCUSTATTCH'); ?></label>
2789 - <span><input type="file" name="emailattch" id="emailattch" /></span>
3871 +
3872 + <div class="vbo-param-container vbo-param-container-full">
3873 + <div class="vbo-param-label">
3874 + <label for="emailattch"><?php echo JText::translate('VBSENDEMAILCUSTATTCH'); ?></label>
3875 + </div>
3876 + <div class="vbo-param-setting">
3877 + <input type="file" name="emailattch" id="emailattch" />
3878 + </div>
2790 3879 </div>
2791 - <div class="vbo-calendar-cfield-entry">
2792 - <label for="emailfrom"><?php echo JText::translate('VBSENDEMAILCUSTFROM'); ?></label>
2793 - <span><input type="text" name="emailfrom" id="emailfrom" value="<?php echo VikBooking::getSenderMail(); ?>" size="30" /></span>
3880 +
3881 + <div class="vbo-param-container vbo-param-container-full">
3882 + <div class="vbo-param-label">
3883 + <label for="emailfrom"><?php echo JText::translate('VBSENDEMAILCUSTFROM'); ?></label>
3884 + </div>
3885 + <div class="vbo-param-setting">
3886 + <input type="text" name="emailfrom" id="emailfrom" value="<?php echo VikBooking::getSenderMail(); ?>" size="30" />
3887 + </div>
2794 3888 </div>
3889 +
2795 3890 </div>
2796 - <input type="hidden" name="email" id="emailto" value="<?php echo $row['custmail']; ?>" />
2797 - <input type="hidden" name="goto" value="<?php echo urlencode('index.php?option=com_vikbooking&task=editorder&cid[]='.$row['id']); ?>" />
2798 - <input type="hidden" name="task" value="sendcustomemail" />
2799 - </form>
3891 + </div>
2800 3892 </div>
3893 + <input type="hidden" name="bid" value="<?php echo $row['id']; ?>" />
3894 + <input type="hidden" name="email" id="emailto" value="<?php echo $row['custmail']; ?>" />
3895 + <input type="hidden" name="goto" value="<?php echo urlencode('index.php?option=com_vikbooking&task=editorder&cid[]='.$row['id']); ?>" />
3896 + <input type="hidden" name="task" value="sendcustomemail" />
3897 + </form>
3898 + </div>
2801 3899
2802 - </div>
3900 + <div id="vbo-modal-footer-sms" style="display: none;">
3901 + <button type="submit" class="btn vbo-config-btn" onclick="document.getElementById('vbo-modal-form-sms').submit();"><?php VikBookingIcons::e('comment'); ?> <?php echo JText::translate('VBSENDSMSACTION'); ?></button>
3902 + </div>
3903 + <div id="vbo-modal-footer-email" style="display: none;">
3904 + <button type="submit" class="btn vbo-config-btn" onclick="document.getElementById('vbo-modal-form-email').submit();"><?php VikBookingIcons::e('envelope'); ?> <?php echo JText::translate('VBSENDEMAILACTION'); ?></button>
3905 + </div>
3906 +</div>
2803 3907
2804 - <div class="vbo-modal-overlay-content-footer">
2805 - <div class="vbo-modal-overlay-content-footer-right">
2806 - <div id="vbo-modal-footer-sms" style="display: none;">
2807 - <button type="submit" class="btn vbo-config-btn" onclick="document.getElementById('vbo-modal-form-sms').submit();"><?php VikBookingIcons::e('comment'); ?> <?php echo JText::translate('VBSENDSMSACTION'); ?></button>
3908 +<div class="vbo-bookingdet-send-tplmessage-helper" style="display: none;">
3909 + <div class="vbo-bookingdet-send-tplmessage-wrap">
3910 + <div class="vbo-admin-container vbo-admin-container-full vbo-admin-container-compact">
3911 + <div class="vbo-params-wrap">
3912 + <div class="vbo-params-container">
3913 + <div class="vbo-params-block">
3914 + <?php
3915 + echo VBOParamsRendering::getInstance(
3916 + [
3917 + 'ma_identifier' => [
3918 + 'type' => 'elements',
3919 + 'label' => JText::translate('VBO_MESSAGE_TEMPLATE'),
3920 + 'asset_options' => [
3921 + 'placeholder' => JText::translate('VBO_MESSAGE_TEMPLATE'),
3922 + 'allowClear' => false,
3923 + ],
3924 + 'inline' => false,
3925 + 'wrapdivcls' => 'vbo-singleselect-inline-elems-wrap vbo-singleselect-inline-small-icons',
3926 + 'style_selection' => true,
3927 + 'attributes' => [
3928 + 'data-element' => 'message-template',
3929 + ],
3930 + 'elements' => array_map(function($maConfig) {
3931 + if (!empty($maConfig['channel_logo'])) {
3932 + $messTplName = sprintf('%s (%s)', $maConfig['name'], $maConfig['lang']);
3933 + } else {
3934 + $messTplName = sprintf('%s - %s (%s)', $maConfig['channel_name'], $maConfig['name'], $maConfig['lang']);
3935 + }
3936 + return array_filter([
3937 + 'id' => $maConfig['identifier'],
3938 + 'name' => $messTplName,
3939 + 'img_uri' => ($maConfig['channel_logo'] ?? '') ?: null,
3940 + ]);
3941 + }, $messagingAccountConfigurations),
3942 + ],
3943 + ],
3944 + []
3945 + )->setInputName('send_ma_tpl')->getHtml();
3946 + ?>
3947 + <div class="vbo-param-container">
3948 + <div class="vbo-param-label"><?php echo JText::translate('VBOPREVIEW'); ?></div>
3949 + <div class="vbo-param-setting">
3950 + <div class="vbo-ma-send-tplmessage-preview"></div>
3951 + </div>
3952 + </div>
3953 + </div>
2808 3954 </div>
2809 - <div id="vbo-modal-footer-email" style="display: none;">
2810 - <button type="submit" class="btn vbo-config-btn" onclick="document.getElementById('vbo-modal-form-email').submit();"><?php VikBookingIcons::e('envelope'); ?> <?php echo JText::translate('VBSENDEMAILACTION'); ?></button>
2811 - </div>
2812 3955 </div>
2813 3956 </div>
2814 -
2815 3957 </div>
2816 3958 </div>
2817 3959
2818 3960 <script type="text/javascript">
2819 -var vbo_overlay_on = false;
2820 3961 var vbo_print_only = false;
2821 -if (jQuery.isFunction(jQuery.fn.tooltip)) {
3962 +if (typeof jQuery.fn.tooltip === 'function') {
2822 3963 jQuery(".hasTooltip").tooltip();
2823 3964 }
2824 -function vboToggleSendSMS() {
3965 +function vboDisplaySendSMS() {
2825 3966 var cur_phone = jQuery("#smstophone").val();
2826 3967 var phone_set = jQuery("#custphone").trigger('vboupdatephonenumber').val();
2827 3968 if (phone_set.length && phone_set != cur_phone) {
2828 3969 jQuery("#smstophone").val(phone_set);
2829 - jQuery("#smstophone-lbl").text(phone_set);
2830 3970 }
2831 3971
2832 - // toggle email/sms contents
2833 - jQuery("#vbo-overlay-email-cont, #vbo-modal-footer-email").hide();
2834 - jQuery("#vbo-overlay-sms-cont, #vbo-modal-footer-sms").show();
2835 - var use_modal_title = jQuery("#vbo-overlay-sms-cont").find('h4').first().html();
2836 - jQuery('.vbo-modal-overlay-content-sms-email-head').find('h3').find('span').first().html(use_modal_title);
3972 + // define the modal cancel button
3973 + let cancel_btn = jQuery('<button></button>')
3974 + .attr('type', 'button')
3975 + .addClass('btn')
3976 + .text(Joomla.JText._('VBANNULLA'))
3977 + .on('click', function() {
3978 + VBOCore.emitEvent('vbo-editorder-send-sms-dismiss');
3979 + });
2837 3980
2838 - jQuery(".vbo-modal-overlay-block-sms-email").fadeToggle(400, function() {
2839 - if (jQuery(".vbo-modal-overlay-block-sms-email").is(":visible")) {
2840 - vbo_overlay_on = true;
2841 - } else {
2842 - vbo_overlay_on = false;
2843 - }
3981 + // display modal for email/sms contents
3982 + let modal_body = VBOCore.displayModal({
3983 + suffix: 'editorder-send-sms',
3984 + extra_class: 'vbo-modal-large',
3985 + lock_scroll: true,
3986 + title: Joomla.JText._('VBSENDSMSACTION') + ': ' + (phone_set && phone_set != cur_phone ? phone_set : cur_phone),
3987 + footer_left: cancel_btn,
3988 + footer_right: jQuery('#vbo-modal-footer-sms').find('button').clone(),
3989 + loading_event: 'vbo-editorder-send-sms-loading',
3990 + dismiss_event: 'vbo-editorder-send-sms-dismiss',
3991 + onDismiss: () => {
3992 + jQuery('.vbo-editorder-sms-send-wrap').appendTo('.vbo-editorder-sms-email-send-helper');
3993 + },
2844 3994 });
3995 +
3996 + jQuery('.vbo-editorder-sms-send-wrap').appendTo(modal_body);
2845 3997 }
2846 -function vboToggleSendEmail() {
3998 +function vboDisplaySendEmail() {
2847 3999 var cur_email = jQuery("#emailto").val();
2848 4000 var email_set = jQuery("#custmail").val();
2849 4001 if (email_set.length && email_set != cur_email) {
2850 4002 jQuery("#emailto").val(email_set);
2851 - jQuery("#emailto-lbl").text(email_set);
2852 4003 }
2853 4004
2854 - // toggle email/sms contents
2855 - jQuery("#vbo-overlay-sms-cont, #vbo-modal-footer-sms").hide();
2856 - jQuery("#vbo-overlay-email-cont, #vbo-modal-footer-email").show();
2857 - var use_modal_title = jQuery("#vbo-overlay-email-cont").find('h4').first().html();
2858 - jQuery('.vbo-modal-overlay-content-sms-email-head').find('h3').find('span').first().html(use_modal_title);
4005 + // define the modal cancel button
4006 + let cancel_btn = jQuery('<button></button>')
4007 + .attr('type', 'button')
4008 + .addClass('btn')
4009 + .text(Joomla.JText._('VBANNULLA'))
4010 + .on('click', function() {
4011 + VBOCore.emitEvent('vbo-editorder-send-email-dismiss');
4012 + });
2859 4013
2860 - jQuery(".vbo-modal-overlay-block-sms-email").fadeToggle(400, function() {
2861 - if (jQuery(".vbo-modal-overlay-block-sms-email").is(":visible")) {
2862 - vbo_overlay_on = true;
2863 - } else {
2864 - vbo_overlay_on = false;
2865 - }
4014 + // display modal for email/sms contents
4015 + let modal_body = VBOCore.displayModal({
4016 + suffix: 'editorder-send-email',
4017 + extra_class: 'vbo-modal-large',
4018 + lock_scroll: true,
4019 + title: Joomla.JText._('VBSENDEMAILACTION') + ': ' + (email_set && email_set != cur_email ? email_set : cur_email),
4020 + footer_left: cancel_btn,
4021 + footer_right: jQuery('#vbo-modal-footer-email').find('button').clone(),
4022 + loading_event: 'vbo-editorder-send-email-loading',
4023 + dismiss_event: 'vbo-editorder-send-email-dismiss',
4024 + onDismiss: () => {
4025 + jQuery('.vbo-editorder-email-send-wrap').appendTo('.vbo-editorder-sms-email-send-helper');
4026 + },
2866 4027 });
4028 +
4029 + jQuery('.vbo-editorder-email-send-wrap').appendTo(modal_body);
2867 4030 }
2868 4031 function vboKeyupEmail(event) {
2869 4032 if (event.key && event.key == 'Enter') {
2870 4033 event.preventDefault();
@@ -2901,8 +4064,12 @@
2901 4064 }
2902 4065 // reload the page to display the new information about the booking just modified
2903 4066 location.reload();
2904 4067 });
4068 + // register click event for the button to see the quote details
4069 + document.querySelector('.vbo-quote-details-btn')?.addEventListener('click', (e) => {
4070 + VBOCore.handleDisplayWidgetNotification({widget_id: 'quotes'}, {bid: <?php echo $row['id']; ?>});
4071 + });
2905 4072 // remove overbooking flag
2906 4073 jQuery('.vbo-label-overbooking').on('click', function() {
2907 4074 if (confirm(Joomla.JText._('VBO_CONF_RM_OVERBOOKING_FLAG'))) {
2908 4075 VBOCore.doAjax(
@@ -2998,24 +4165,8 @@
2998 4165 return;
2999 4166 }
3000 4167 });
3001 4168 // Search customer - End
3002 - jQuery(document).mouseup(function(e) {
3003 - if (!vbo_overlay_on) {
3004 - return false;
3005 - }
3006 - var vbo_overlay_cont = jQuery(".vbo-modal-overlay-content-sms-email");
3007 - if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0 && !jQuery(e.target).is('svg')) {
3008 - jQuery(".vbo-modal-overlay-block-sms-email").fadeOut();
3009 - vbo_overlay_on = false;
3010 - }
3011 - });
3012 - jQuery(document).keyup(function(e) {
3013 - if (e.keyCode == 27 && vbo_overlay_on) {
3014 - jQuery(".vbo-modal-overlay-block-sms-email").fadeOut();
3015 - vbo_overlay_on = false;
3016 - }
3017 - });
3018 4169 jQuery(".vbo-bookingdet-tab").click(function() {
3019 4170 var newtabrel = jQuery(this).attr('data-vbotab');
3020 4171 var oldtabrel = jQuery(".vbo-bookingdet-tab-active").attr('data-vbotab');
3021 4172 if (newtabrel == oldtabrel) {
@@ -3146,8 +4297,242 @@
3146 4297 idorder: '<?php echo $row['id']; ?>',
3147 4298 }
3148 4299 });
3149 4300
4301 + // register click event on the booking task(s)
4302 + document.querySelectorAll('.vbo-bookingdet-task-details[data-task-id]').forEach((task) => {
4303 + const taskId = task.getAttribute('data-task-id');
4304 + task.addEventListener('click', (e) => {
4305 + e.stopPropagation();
4306 + // define the modal cancel button
4307 + let cancel_btn = jQuery('<button></button>')
4308 + .attr('type', 'button')
4309 + .addClass('btn')
4310 + .text(Joomla.JText._('VBOCLOSE'))
4311 + .on('click', () => {
4312 + VBOCore.emitEvent('vbo-tm-edittask-dismiss');
4313 + });
4314 +
4315 + // define the modal save button
4316 + let save_btn = jQuery('<button></button>')
4317 + .attr('type', 'button')
4318 + .addClass('btn btn-success')
4319 + .text(Joomla.JText._('VBSAVE'))
4320 + .on('click', function() {
4321 + // disable button to prevent double submissions
4322 + let submit_btn = jQuery(this);
4323 + submit_btn.prop('disabled', true);
4324 +
4325 + // start loading animation
4326 + VBOCore.emitEvent('vbo-tm-edittask-loading');
4327 +
4328 + // get form data
4329 + const taskForm = new FormData(document.querySelector('#vbo-tm-task-manage-form'));
4330 +
4331 + // build query parameters for the request
4332 + let qpRequest = new URLSearchParams(taskForm);
4333 +
4334 + // make sure the request always includes the assignees query parameter, even if the list is empty
4335 + if (!qpRequest.has('data[assignees][]')) {
4336 + qpRequest.append('data[assignees][]', []);
4337 + }
4338 +
4339 + // make sure the request always includes the tags query parameter, even if the list is empty
4340 + if (!qpRequest.has('data[tags][]')) {
4341 + qpRequest.append('data[tags][]', []);
4342 + }
4343 +
4344 + // make the request
4345 + VBOCore.doAjax(
4346 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.updateTask'); ?>",
4347 + qpRequest.toString(),
4348 + (resp) => {
4349 + // dismiss the modal on success
4350 + VBOCore.emitEvent('vbo-tm-edittask-dismiss');
4351 +
4352 + // reload the page
4353 + location.reload();
4354 + },
4355 + (error) => {
4356 + // display error message
4357 + alert(error.responseText);
4358 +
4359 + // re-enable submit button
4360 + submit_btn.prop('disabled', false);
4361 +
4362 + // stop loading
4363 + VBOCore.emitEvent('vbo-tm-edittask-loading');
4364 + }
4365 + );
4366 + });
4367 +
4368 + // display modal
4369 + let modalBody = VBOCore.displayModal({
4370 + suffix: 'tm_edittask_modal',
4371 + title: <?php echo json_encode(JText::translate('VBO_TASK')); ?> + ' #' + taskId,
4372 + extra_class: 'vbo-modal-rounded vbo-modal-taller vbo-modal-large',
4373 + body_prepend: true,
4374 + lock_scroll: true,
4375 + escape_dismiss: false,
4376 + footer_left: cancel_btn,
4377 + footer_right: save_btn,
4378 + loading_event: 'vbo-tm-edittask-loading',
4379 + dismiss_event: 'vbo-tm-edittask-dismiss',
4380 + });
4381 +
4382 + // start loading animation
4383 + VBOCore.emitEvent('vbo-tm-edittask-loading');
4384 +
4385 + // make the request
4386 + VBOCore.doAjax(
4387 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.renderLayout'); ?>",
4388 + {
4389 + type: 'tasks.managetask',
4390 + data: {
4391 + task_id: taskId,
4392 + form_id: 'vbo-tm-task-manage-form',
4393 + },
4394 + },
4395 + (resp) => {
4396 + // stop loading
4397 + VBOCore.emitEvent('vbo-tm-edittask-loading');
4398 +
4399 + try {
4400 + // decode the response (if needed), and append the content to the modal body
4401 + let obj_res = typeof resp === 'string' ? JSON.parse(resp) : resp;
4402 + modalBody.append(obj_res['html']);
4403 + } catch (err) {
4404 + console.error('Error decoding the response', err, resp);
4405 + }
4406 + },
4407 + (error) => {
4408 + // display error message
4409 + alert(error.responseText);
4410 +
4411 + // stop loading
4412 + VBOCore.emitEvent('vbo-tm-edittask-loading');
4413 + }
4414 + );
4415 + });
4416 + });
4417 +
4418 + // register click event to copy the OTA booking ID
4419 + const otaBidEl = document.querySelector('.vbo-reservation-copy-otabid');
4420 + if (otaBidEl) {
4421 + otaBidEl.addEventListener('click', () => {
4422 + let copyEl = document.querySelector('textarea.vbo-textarea-copyable[data-copy="otabid"]');
4423 + VBOCore.copyToClipboard(copyEl).then((success) => {
4424 + otaBidEl.setAttribute('data-tooltiptext', <?php echo json_encode(JText::translate('VBO_COPIED') . '!'); ?>);
4425 + }).catch((err) => {
4426 + // do nothing
4427 + });
4428 + });
4429 + }
4430 +
4431 + // register events for sending a message template through a messaging account configuration
4432 + const messagingAccountsData = <?php echo json_encode($messagingAccountConfigurations); ?>;
4433 + const sendTplMessBtn = document.querySelector('.vbo-bookingdet-send-tplmessage-btn');
4434 + const sendTplMessContWrap = document.querySelector('.vbo-bookingdet-send-tplmessage-wrap');
4435 + jQuery(sendTplMessContWrap).find('select[data-element="message-template"]').on('change', function() {
4436 + let maIdentifier = this.value;
4437 + let previewEl = sendTplMessContWrap.querySelector('.vbo-ma-send-tplmessage-preview');
4438 + previewEl.innerHTML = '';
4439 + if (maIdentifier) {
4440 + messagingAccountsData.forEach((maData) => {
4441 + if (maData?.identifier == maIdentifier) {
4442 + // update preview content
4443 + previewEl.innerHTML = maData?.preview_html || '';
4444 + }
4445 + });
4446 + }
4447 + });
4448 + if (sendTplMessBtn) {
4449 + sendTplMessBtn.addEventListener('click', () => {
4450 + let cancelBtn = document.createElement('button');
4451 + cancelBtn.setAttribute('type', 'button');
4452 + cancelBtn.classList.add('btn');
4453 + cancelBtn.textContent = <?php echo json_encode(JText::translate('VBANNULLA')); ?>;
4454 + cancelBtn.addEventListener('click', () => {
4455 + VBOCore.emitEvent('vbo-send-tplmessage-dismiss');
4456 + });
4457 +
4458 + let confirmBtn = document.createElement('button');
4459 + confirmBtn.setAttribute('type', 'button');
4460 + confirmBtn.classList.add('btn', 'btn-primary');
4461 + confirmBtn.innerHTML = '<?php VikBookingIcons::e('paper-plane'); ?> ' + <?php echo json_encode(JText::translate('VBO_SEND_MESSAGE')); ?>;
4462 + confirmBtn.addEventListener('click', () => {
4463 + // get message template identifier data
4464 + let tplIdentifier = sendTplMessContWrap.querySelector('select[data-element="message-template"]')?.value;
4465 + if (!tplIdentifier) {
4466 + alert('Invalid message template');
4467 + return;
4468 + }
4469 +
4470 + // obtain selected template details
4471 + let tplIdentifierParts = tplIdentifier.split(':');
4472 +
4473 + // start loading
4474 + VBOCore.emitEvent('vbo-send-tplmessage-loading');
4475 + confirmBtn.disabled = true;
4476 +
4477 + // make the request
4478 + VBOCore.doAjax(
4479 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=messaging.sendMessageTemplate'); ?>",
4480 + {
4481 + account_id: tplIdentifierParts[0],
4482 + phone_id: tplIdentifierParts[1],
4483 + config_id: tplIdentifierParts[2],
4484 + booking_id: <?php echo $this->row['id']; ?>,
4485 + },
4486 + (resp) => {
4487 + // dismiss modal on success
4488 + VBOCore.emitEvent('vbo-send-tplmessage-dismiss');
4489 +
4490 + // display success message
4491 + VBOToast.enqueue(new VBOToastMessage({
4492 + body: <?php echo json_encode(JText::translate('VBSENDEMAILOK')); ?>,
4493 + icon: '<?php echo VikBookingIcons::i('check-circle'); ?>',
4494 + status: VBOToast.SUCCESS_STATUS,
4495 + action: () => {
4496 + VBOToast.dispose(true);
4497 + },
4498 + }));
4499 +
4500 + // reload the page (just for the new history event)
4501 + location.reload();
4502 + },
4503 + (error) => {
4504 + // display error message
4505 + alert(error.responseText);
4506 +
4507 + // stop loading
4508 + VBOCore.emitEvent('vbo-send-tplmessage-loading');
4509 + confirmBtn.disabled = false;
4510 + }
4511 + );
4512 + });
4513 +
4514 + let modalBody = VBOCore.displayModal({
4515 + suffix: 'vbo-send-tplmessage',
4516 + extra_class: 'vbo-modal-rounded',
4517 + title: <?php echo json_encode(JText::translate('VBO_SEND_MESSAGE')); ?>,
4518 + footer_left: cancelBtn,
4519 + footer_right: confirmBtn,
4520 + draggable: true,
4521 + lock_scroll: true,
4522 + dismiss_event: 'vbo-send-tplmessage-dismiss',
4523 + loading_event: 'vbo-send-tplmessage-loading',
4524 + onDismiss: () => {
4525 + document.querySelector('.vbo-bookingdet-send-tplmessage-helper').append(
4526 + sendTplMessContWrap
4527 + );
4528 + },
4529 + });
4530 +
4531 + (modalBody[0] || modalBody).append(sendTplMessContWrap);
4532 + });
4533 + }
4534 +
3150 4535 });
3151 4536 var cur_emtpl = <?php echo json_encode($cur_emtpl); ?>;
3152 4537 function vboLoadEmailTpl(tplind) {
3153 4538 if (!(tplind.length > 0)) {
@@ -3174,9 +4559,9 @@
3174 4559 <?php
3175 4560 $pcustomemail = VikRequest::getInt('customemail', '', 'request');
3176 4561 if ($pcustomemail > 0) {
3177 4562 ?>
3178 - vboToggleSendEmail();
4563 + vboDisplaySendEmail();
3179 4564 <?php
3180 4565 }
3181 4566 if ($printreceipt) {
3182 4567 ?>