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 +2057 -501 1.6.7 → 1.8.16 View file →
@@ -15,10 +15,11 @@
15 15 $busy = $this->busy;
16 16 $customer = $this->customer;
17 17 $payments = $this->payments;
18 18
19 -$history_obj = VikBooking::getBookingHistoryInstance();
20 -$history_obj->setBid($row['id']);
19 +// load registry and history
20 +$registry = VBOBookingRegistry::getInstance($this->row, $this->rooms);
21 +$history_obj = VikBooking::getBookingHistoryInstance($row['id']);
21 22
22 23 // availability helper
23 24 $av_helper = VikBooking::getAvailabilityInstance();
24 25
@@ -32,14 +33,29 @@
32 33 JText::script('VBDELCONFIRM');
33 34 JText::script('VBSENDCANCORDEMAIL');
34 35 JText::script('VBO_SET_STANDBY');
35 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');
36 48
37 49 $dbo = JFactory::getDbo();
38 50
39 51 $vbo_app = VikBooking::getVboApplication();
40 52 $vbo_app->loadVisualEditorAssets();
53 +$vbo_app->prepareJavaScriptCurrency();
41 54
55 +// preload chat assets
56 +VBOFactory::getChatMediator()->useAssets();
57 +
42 58 /**
43 59 * This View can render within a modal the "bookingcheckin" View, and with WP
44 60 * this is loaded via AJAX. In order to not interfere or reset the contextual menu,
45 61 * we load the assets from the parent View. Context menu may also be used in this View.
@@ -54,9 +70,16 @@
54 70
55 71 $canDo = JFactory::getUser();
56 72 $vbo_auth_bookings = $canDo->authorise('core.vbo.bookings', 'com_vikbooking');
57 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)) : [];
58 80 $currencyname = VikBooking::getCurrencyName();
81 +$currencysymb = VikBooking::getCurrencySymb();
59 82 $after_tax = VikBooking::ivaInclusa();
60 83 $nowdf = VikBooking::getDateFormat(true);
61 84 if ($nowdf == "%d/%m/%Y") {
62 85 $df = 'd/m/Y';
@@ -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 3 days after check-out day
451 - $max_refund_ts = mktime(23, 59, 59, $checkout_info['mon'], ($checkout_info['mday'] + 3), $checkout_info['year']);
503 + // refundable/capturable up to 7 days after check-out day
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 *
@@ -615,309 +691,314 @@
615 691 }
616 692 ?>
617 693 <a id="vbo-gm-reloadlink" style="display: none;" href="index.php?option=com_vikbooking&task=editorder&cid[]=<?php echo $row['id']; ?>"></a>
618 694 </div>
619 - </div>
620 695
621 - <div class="vbo-modal-overlay-block vbo-modal-overlay-block-guestmisconduct">
622 - <a class="vbo-modal-overlay-close" href="javascript: void(0);"></a>
623 - <div class="vbo-modal-overlay-content vbo-modal-overlay-content-guestmisconduct">
624 - <div class="vbo-modal-overlay-content-head vbo-modal-overlay-content-head-guestmisconduct">
625 - <h3><?php VikBookingIcons::e('exclamation-circle'); ?> <?php echo JText::sprintf('VBOREPGUESTMISCONDUCTTO', 'Booking.com'); ?> <span class="vbo-modal-overlay-close-times" onclick="hideVboDialogGuestMisconduct();">&times;</span></h3>
626 - </div>
627 - <div class="vbo-modal-overlay-content-body">
628 - <div class="vbo-modal-guestmisconduct-addnew" data-bid="<?php echo $row['id']; ?>" data-otabid="<?php echo $row['idorderota']; ?>">
629 - <div class="vbo-modal-guestmisconduct-report-elems"></div>
630 - <div class="vbo-modal-guestmisconduct-addnew-save">
631 - <button type="button" id="vbo-guest-misconduct-submit-btn" class="btn btn-success" onclick="vboSubmitGuestMisconduct();" style="display: none;"><?php echo JText::translate('VBCHANNELMANAGERSENDRQ'); ?></button>
632 - <button type="button" class="btn btn-danger" onclick="hideVboDialogGuestMisconduct();"><?php echo JText::translate('VBOCLOSE'); ?></button>
696 + <div class="vbo-modal-overlay-block vbo-modal-overlay-block-guestmisconduct">
697 + <a class="vbo-modal-overlay-close" href="javascript: void(0);"></a>
698 + <div class="vbo-modal-overlay-content vbo-modal-overlay-content-guestmisconduct">
699 + <div class="vbo-modal-overlay-content-head vbo-modal-overlay-content-head-guestmisconduct">
700 + <h3><?php VikBookingIcons::e('exclamation-circle'); ?> <?php echo JText::sprintf('VBOREPGUESTMISCONDUCTTO', 'Booking.com'); ?> <span class="vbo-modal-overlay-close-times" onclick="hideVboDialogGuestMisconduct();">&times;</span></h3>
701 + </div>
702 + <div class="vbo-modal-overlay-content-body">
703 + <div class="vbo-modal-guestmisconduct-addnew" data-bid="<?php echo $row['id']; ?>" data-otabid="<?php echo $row['idorderota']; ?>">
704 + <div class="vbo-modal-guestmisconduct-report-elems"></div>
705 + <div class="vbo-modal-guestmisconduct-addnew-save">
706 + <button type="button" id="vbo-guest-misconduct-submit-btn" class="btn btn-success" onclick="vboSubmitGuestMisconduct();" style="display: none;"><?php echo JText::translate('VBCHANNELMANAGERSENDRQ'); ?></button>
707 + <button type="button" class="btn btn-secondary" onclick="hideVboDialogGuestMisconduct();"><?php echo JText::translate('VBOCLOSE'); ?></button>
708 + </div>
633 709 </div>
634 710 </div>
635 711 </div>
636 712 </div>
637 - </div>
638 713
639 - <script type="text/javascript">
640 - var vbodialogguestmisconduct_on = false;
641 - var vbo_guest_misconduct_fields = new Array;
714 + <script type="text/javascript">
715 + var vbodialogguestmisconduct_on = false;
716 + var vbo_guest_misconduct_fields = new Array;
642 717
643 - function vboShowGuestMisconductReported() {
644 - // this is like simulating the navigation to the booking history tab
645 - jQuery(".vbo-bookingdet-tab[data-vbotab='vbo-tab-admin']").trigger('click');
646 - vbToggleHistory(document.getElementById('vbo-trig-bookhistory'));
647 - }
718 + function vboShowGuestMisconductReported() {
719 + // this is like simulating the navigation to the booking history tab
720 + jQuery(".vbo-bookingdet-tab[data-vbotab='vbo-tab-admin']").trigger('click');
721 + vbToggleHistory(document.getElementById('vbo-trig-bookhistory'));
722 + }
648 723
649 - function vboRenderGuestMisconduct() {
650 - // empty content
651 - jQuery('.vbo-modal-guestmisconduct-report-elems').html('<?php echo $this->escape(JText::translate('VIKLOADING') . '...'); ?>');
652 - vbo_guest_misconduct_fields = new Array;
653 - // display modal
654 - jQuery('.vbo-modal-overlay-block-guestmisconduct').fadeIn();
655 - vbodialogguestmisconduct_on = true;
656 - // make AJAX request to VCM to always reload the form fields
657 - var jqxhr = jQuery.ajax({
658 - type: "POST",
659 - url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikchannelmanager&task=get_bcom_misconduct_categories'); ?>",
660 - data: { tmpl: "component" }
661 - }).done(function(res) {
662 - // parse the JSON response that contains the categories object with all fields for the reporting
663 - try {
664 - var misconduct_fields_rs = JSON.parse(res);
665 - console.log(misconduct_fields_rs);
724 + function vboRenderGuestMisconduct() {
725 + // empty content
726 + jQuery('.vbo-modal-guestmisconduct-report-elems').html('<?php echo $this->escape(JText::translate('VIKLOADING') . '...'); ?>');
727 + vbo_guest_misconduct_fields = new Array;
666 728
667 - // make sure the request was successful
668 - if (!misconduct_fields_rs.status) {
669 - alert(misconduct_fields_rs.error);
670 - hideVboDialogGuestMisconduct();
671 - return false;
672 - }
729 + // display modal
730 + jQuery('.vbo-modal-overlay-block-guestmisconduct').fadeIn();
731 + vbodialogguestmisconduct_on = true;
673 732
674 - // render form from response obtained
675 - var misconduct_html = '';
676 - var misconduct_fields = misconduct_fields_rs.data;
677 - if (!misconduct_fields.hasOwnProperty('categories') || !misconduct_fields.hasOwnProperty('fields')) {
678 - alert('Unexptected response. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
679 - hideVboDialogGuestMisconduct();
680 - return false;
681 - }
682 -
683 - // main category of misconduct
684 - misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem">' + "\n";
685 - misconduct_html += '<label for="vbo-gm-category">' + misconduct_fields['labels']['category'] + '</label>';
686 - misconduct_html += '<select id="vbo-gm-category" data-gmname="category_id" onchange="vboGuestMisconductChangeCategory(this.value);">' + "\n";
687 - for (var category in misconduct_fields['categories']) {
688 - if (!misconduct_fields['categories'].hasOwnProperty(category)) {
689 - continue;
690 - }
691 - misconduct_html += '<option value="' + misconduct_fields['categories'][category]['id'] + '">' + misconduct_fields['categories'][category]['title'] + '</option>' + "\n";
692 - }
693 - misconduct_html += '</select>' + "\n";
694 - misconduct_html += '</div>' + "\n";
695 - // push category field in fields list for later submit
696 - vbo_guest_misconduct_fields.push('vbo-gm-category');
733 + // make AJAX request to VCM to always reload the form fields
734 + VBOCore.doAjax(
735 + "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikchannelmanager&task=get_bcom_misconduct_categories'); ?>",
736 + {
737 + tmpl: "component"
738 + },
739 + (res) => {
740 + // parse the JSON response that contains the categories object with all fields for the reporting
741 + try {
742 + var misconduct_fields_rs = typeof res === 'string' ? JSON.parse(res) : res;
697 743
698 - // sub-categories or additional fields of misconduct categories
699 - for (var category in misconduct_fields['categories']) {
700 - if (!misconduct_fields['categories'].hasOwnProperty(category)) {
701 - continue;
702 - }
703 - if (!misconduct_fields['categories'][category].hasOwnProperty('subcategories') && !misconduct_fields['categories'][category].hasOwnProperty('additional_fields')) {
704 - // at least one of these two properties must be set
705 - continue;
706 - }
707 - if (misconduct_fields['categories'][category].hasOwnProperty('subcategories') && misconduct_fields['categories'][category]['subcategories'].length) {
708 - // sub-categories in drop down
709 - var subcat_field_id = 'vbo-gm-subcategory' + misconduct_fields['categories'][category]['id'];
710 - misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem vbo-modal-guestmisconduct-addnew-cond" data-gmcid="' + misconduct_fields['categories'][category]['id'] + '" style="display: none;">' + "\n";
711 - misconduct_html += '<label for="' + subcat_field_id + '">' + misconduct_fields['labels']['subcategory'] + '</label>';
712 - misconduct_html += '<select id="' + subcat_field_id + '" data-gmname="subcategory_id">' + "\n";
713 - for (var subcategory in misconduct_fields['categories'][category]['subcategories']) {
714 - if (!misconduct_fields['categories'][category]['subcategories'].hasOwnProperty(subcategory)) {
744 + // make sure the request was successful
745 + if (!misconduct_fields_rs.status) {
746 + alert(misconduct_fields_rs.error);
747 + hideVboDialogGuestMisconduct();
748 + return false;
749 + }
750 +
751 + // render form from response obtained
752 + var misconduct_html = '';
753 + var misconduct_fields = misconduct_fields_rs.data;
754 + if (!misconduct_fields.hasOwnProperty('categories') || !misconduct_fields.hasOwnProperty('fields')) {
755 + alert('Unexptected response. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
756 + hideVboDialogGuestMisconduct();
757 + return false;
758 + }
759 +
760 + // main category of misconduct
761 + misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem">' + "\n";
762 + misconduct_html += '<label for="vbo-gm-category">' + misconduct_fields['labels']['category'] + '</label>';
763 + misconduct_html += '<select id="vbo-gm-category" data-gmname="category_id" onchange="vboGuestMisconductChangeCategory(this.value);">' + "\n";
764 + for (var category in misconduct_fields['categories']) {
765 + if (!misconduct_fields['categories'].hasOwnProperty(category)) {
715 766 continue;
716 767 }
717 - misconduct_html += '<option value="' + misconduct_fields['categories'][category]['subcategories'][subcategory]['id'] + '">' + misconduct_fields['categories'][category]['subcategories'][subcategory]['title'] + '</option>' + "\n";
768 + misconduct_html += '<option value="' + misconduct_fields['categories'][category]['id'] + '">' + misconduct_fields['categories'][category]['title'] + '</option>' + "\n";
718 769 }
719 770 misconduct_html += '</select>' + "\n";
720 771 misconduct_html += '</div>' + "\n";
721 - // push sub-category field in fields list for later submit
722 - if (vbo_guest_misconduct_fields.indexOf(subcat_field_id) < 0) {
723 - vbo_guest_misconduct_fields.push(subcat_field_id);
772 + // push category field in fields list for later submit
773 + vbo_guest_misconduct_fields.push('vbo-gm-category');
774 +
775 + // sub-categories or additional fields of misconduct categories
776 + for (var category in misconduct_fields['categories']) {
777 + if (!misconduct_fields['categories'].hasOwnProperty(category)) {
778 + continue;
779 + }
780 + if (!misconduct_fields['categories'][category].hasOwnProperty('subcategories') && !misconduct_fields['categories'][category].hasOwnProperty('additional_fields')) {
781 + // at least one of these two properties must be set
782 + continue;
783 + }
784 + if (misconduct_fields['categories'][category].hasOwnProperty('subcategories') && misconduct_fields['categories'][category]['subcategories'].length) {
785 + // sub-categories in drop down
786 + var subcat_field_id = 'vbo-gm-subcategory' + misconduct_fields['categories'][category]['id'];
787 + misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem vbo-modal-guestmisconduct-addnew-cond" data-gmcid="' + misconduct_fields['categories'][category]['id'] + '" style="display: none;">' + "\n";
788 + misconduct_html += '<label for="' + subcat_field_id + '">' + misconduct_fields['labels']['subcategory'] + '</label>';
789 + misconduct_html += '<select id="' + subcat_field_id + '" data-gmname="subcategory_id">' + "\n";
790 + for (var subcategory in misconduct_fields['categories'][category]['subcategories']) {
791 + if (!misconduct_fields['categories'][category]['subcategories'].hasOwnProperty(subcategory)) {
792 + continue;
793 + }
794 + misconduct_html += '<option value="' + misconduct_fields['categories'][category]['subcategories'][subcategory]['id'] + '">' + misconduct_fields['categories'][category]['subcategories'][subcategory]['title'] + '</option>' + "\n";
795 + }
796 + misconduct_html += '</select>' + "\n";
797 + misconduct_html += '</div>' + "\n";
798 + // push sub-category field in fields list for later submit
799 + if (vbo_guest_misconduct_fields.indexOf(subcat_field_id) < 0) {
800 + vbo_guest_misconduct_fields.push(subcat_field_id);
801 + }
802 + } else if (misconduct_fields['categories'][category].hasOwnProperty('additional_fields') && misconduct_fields['categories'][category]['additional_fields'].length) {
803 + // additional fields
804 + for (var extra_field in misconduct_fields['categories'][category]['additional_fields']) {
805 + if (!misconduct_fields['categories'][category]['additional_fields'].hasOwnProperty(extra_field)) {
806 + continue;
807 + }
808 + var field_id = 'vbo-gm-extrafield-' + misconduct_fields['categories'][category]['additional_fields'][extra_field]['name'];
809 + var field_name = misconduct_fields['categories'][category]['additional_fields'][extra_field]['name'];
810 + misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem vbo-modal-guestmisconduct-addnew-cond" data-gmcid="' + misconduct_fields['categories'][category]['id'] + '" style="display: none;">' + "\n";
811 + misconduct_html += '<label for="' + field_id + '">' + misconduct_fields['categories'][category]['additional_fields'][extra_field]['label'] + '</label>';
812 + if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'number') {
813 + misconduct_html += '<input data-gmname="' + field_name + '" type="number" id="' + field_id + '" value="" step="any" min="0" />';
814 + } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'text') {
815 + misconduct_html += '<input data-gmname="' + field_name + '" type="text" id="' + field_id + '" value="" />';
816 + } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'text') {
817 + misconduct_html += '<input data-gmname="' + field_name + '" type="text" id="' + field_id + '" value="" />';
818 + } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'textarea') {
819 + misconduct_html += '<textarea data-gmname="' + field_name + '" id="' + field_id + '" rows="5" cols="50" maxlength="240"></textarea>';
820 + } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'select') {
821 + misconduct_html += '<select data-gmname="' + field_name + '" id="' + field_id + '">';
822 + for (var choicekey in misconduct_fields['categories'][category]['additional_fields'][extra_field]['choices']) {
823 + // this is either an array when keys are numeric from 0, or an object when keys start from like 1, but with choicekey we take the proper key in the object
824 + if (!misconduct_fields['categories'][category]['additional_fields'][extra_field]['choices'].hasOwnProperty(choicekey)) {
825 + continue;
826 + }
827 + misconduct_html += '<option value="' + choicekey + '">' + misconduct_fields['categories'][category]['additional_fields'][extra_field]['choices'][choicekey] + '</option>';
828 + }
829 + misconduct_html += '</select>';
830 + }
831 + misconduct_html += '</div>' + "\n";
832 + // push additional field in fields list for later submit
833 + vbo_guest_misconduct_fields.push(field_id);
834 + }
835 + }
724 836 }
725 - } else if (misconduct_fields['categories'][category].hasOwnProperty('additional_fields') && misconduct_fields['categories'][category]['additional_fields'].length) {
726 - // additional fields
727 - for (var extra_field in misconduct_fields['categories'][category]['additional_fields']) {
728 - if (!misconduct_fields['categories'][category]['additional_fields'].hasOwnProperty(extra_field)) {
837 +
838 + // mandatory fields for all categories of guest misconduct reporting
839 + for (var mandfield in misconduct_fields['fields']) {
840 + if (!misconduct_fields['fields'].hasOwnProperty(mandfield)) {
729 841 continue;
730 842 }
731 - var field_id = 'vbo-gm-extrafield-' + misconduct_fields['categories'][category]['additional_fields'][extra_field]['name'];
732 - var field_name = misconduct_fields['categories'][category]['additional_fields'][extra_field]['name'];
733 - misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem vbo-modal-guestmisconduct-addnew-cond" data-gmcid="' + misconduct_fields['categories'][category]['id'] + '" style="display: none;">' + "\n";
734 - misconduct_html += '<label for="' + field_id + '">' + misconduct_fields['categories'][category]['additional_fields'][extra_field]['label'] + '</label>';
735 - if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'number') {
843 + var field_id = 'vbo-gm-mandfield-' + misconduct_fields['fields'][mandfield]['name'];
844 + var field_name = misconduct_fields['fields'][mandfield]['name'];
845 + misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem">' + "\n";
846 + misconduct_html += '<label for="' + field_id + '">' + misconduct_fields['fields'][mandfield]['label'] + '</label>';
847 + if (misconduct_fields['fields'][mandfield]['type'] == 'number') {
736 848 misconduct_html += '<input data-gmname="' + field_name + '" type="number" id="' + field_id + '" value="" step="any" min="0" />';
737 - } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'text') {
849 + } else if (misconduct_fields['fields'][mandfield]['type'] == 'text') {
738 850 misconduct_html += '<input data-gmname="' + field_name + '" type="text" id="' + field_id + '" value="" />';
739 - } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'text') {
851 + } else if (misconduct_fields['fields'][mandfield]['type'] == 'text') {
740 852 misconduct_html += '<input data-gmname="' + field_name + '" type="text" id="' + field_id + '" value="" />';
741 - } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'textarea') {
853 + } else if (misconduct_fields['fields'][mandfield]['type'] == 'textarea') {
742 854 misconduct_html += '<textarea data-gmname="' + field_name + '" id="' + field_id + '" rows="5" cols="50" maxlength="240"></textarea>';
743 - } else if (misconduct_fields['categories'][category]['additional_fields'][extra_field]['type'] == 'select') {
855 + } else if (misconduct_fields['fields'][mandfield]['type'] == 'select') {
744 856 misconduct_html += '<select data-gmname="' + field_name + '" id="' + field_id + '">';
745 - for (var choicekey in misconduct_fields['categories'][category]['additional_fields'][extra_field]['choices']) {
746 - // this is either an array when keys are numeric from 0, or an object when keys start from like 1, but with choicekey we take the proper key in the object
747 - if (!misconduct_fields['categories'][category]['additional_fields'][extra_field]['choices'].hasOwnProperty(choicekey)) {
857 + for (var choicekey in misconduct_fields['fields'][mandfield]['choices']) {
858 + if (!misconduct_fields['fields'][mandfield]['choices'].hasOwnProperty(choicekey)) {
748 859 continue;
749 860 }
750 - misconduct_html += '<option value="' + choicekey + '">' + misconduct_fields['categories'][category]['additional_fields'][extra_field]['choices'][choicekey] + '</option>';
861 + misconduct_html += '<option value="' + choicekey + '">' + misconduct_fields['fields'][mandfield]['choices'][choicekey] + '</option>';
751 862 }
752 863 misconduct_html += '</select>';
753 864 }
754 865 misconduct_html += '</div>' + "\n";
755 - // push additional field in fields list for later submit
866 + // push mandatory field in fields list for later submit
756 867 vbo_guest_misconduct_fields.push(field_id);
757 868 }
758 - }
759 - }
760 869
761 - // mandatory fields for all categories of guest misconduct reporting
762 - for (var mandfield in misconduct_fields['fields']) {
763 - if (!misconduct_fields['fields'].hasOwnProperty(mandfield)) {
764 - continue;
870 + // display HTML content and submit button
871 + jQuery('.vbo-modal-guestmisconduct-report-elems').html(misconduct_html);
872 + jQuery('#vbo-guest-misconduct-submit-btn').show();
873 + // trigger change of main category drop down
874 + jQuery('#vbo-gm-category').trigger('change');
875 + } catch (e) {
876 + console.log(res);
877 + alert('Invalid response. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
878 + hideVboDialogGuestMisconduct();
879 + return false;
765 880 }
766 - var field_id = 'vbo-gm-mandfield-' + misconduct_fields['fields'][mandfield]['name'];
767 - var field_name = misconduct_fields['fields'][mandfield]['name'];
768 - misconduct_html += '<div class="vbo-modal-form-addnew-elem vbo-modal-guestmisconduct-addnew-elem">' + "\n";
769 - misconduct_html += '<label for="' + field_id + '">' + misconduct_fields['fields'][mandfield]['label'] + '</label>';
770 - if (misconduct_fields['fields'][mandfield]['type'] == 'number') {
771 - misconduct_html += '<input data-gmname="' + field_name + '" type="number" id="' + field_id + '" value="" step="any" min="0" />';
772 - } else if (misconduct_fields['fields'][mandfield]['type'] == 'text') {
773 - misconduct_html += '<input data-gmname="' + field_name + '" type="text" id="' + field_id + '" value="" />';
774 - } else if (misconduct_fields['fields'][mandfield]['type'] == 'text') {
775 - misconduct_html += '<input data-gmname="' + field_name + '" type="text" id="' + field_id + '" value="" />';
776 - } else if (misconduct_fields['fields'][mandfield]['type'] == 'textarea') {
777 - misconduct_html += '<textarea data-gmname="' + field_name + '" id="' + field_id + '" rows="5" cols="50" maxlength="240"></textarea>';
778 - } else if (misconduct_fields['fields'][mandfield]['type'] == 'select') {
779 - misconduct_html += '<select data-gmname="' + field_name + '" id="' + field_id + '">';
780 - for (var choicekey in misconduct_fields['fields'][mandfield]['choices']) {
781 - if (!misconduct_fields['fields'][mandfield]['choices'].hasOwnProperty(choicekey)) {
782 - continue;
783 - }
784 - misconduct_html += '<option value="' + choicekey + '">' + misconduct_fields['fields'][mandfield]['choices'][choicekey] + '</option>';
785 - }
786 - misconduct_html += '</select>';
787 - }
788 - misconduct_html += '</div>' + "\n";
789 - // push mandatory field in fields list for later submit
790 - vbo_guest_misconduct_fields.push(field_id);
881 + },
882 + (err) => {
883 + hideVboDialogGuestMisconduct();
884 + alert('Request failed. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
791 885 }
886 + );
887 + }
792 888
793 - // display HTML content and submit button
794 - jQuery('.vbo-modal-guestmisconduct-report-elems').html(misconduct_html);
795 - jQuery('#vbo-guest-misconduct-submit-btn').show();
796 - // trigger change of main category drop down
797 - jQuery('#vbo-gm-category').trigger('change');
798 - } catch (e) {
799 - console.log(res);
800 - alert('Invalid response. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
801 - hideVboDialogGuestMisconduct();
889 + function vboSubmitGuestMisconduct() {
890 + if (!vbo_guest_misconduct_fields.length) {
891 + alert('Empty data to submit. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
802 892 return false;
803 893 }
804 - }).fail(function() {
805 - alert('Request failed. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
806 - hideVboDialogGuestMisconduct();
807 - });
808 - }
894 +
895 + // prevent double submissions
896 + vboGuestMisconductDisableSubmit();
897 + //
809 898
810 - function vboSubmitGuestMisconduct() {
811 - if (!vbo_guest_misconduct_fields.length) {
812 - alert('Empty data to submit. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
813 - return false;
814 - }
815 -
816 - // prevent double submissions
817 - vboGuestMisconductDisableSubmit();
818 - //
899 + var submit_fields = {
900 + tmpl: "component",
901 + bid: jQuery('.vbo-modal-guestmisconduct-addnew').attr('data-bid'),
902 + otabid: jQuery('.vbo-modal-guestmisconduct-addnew').attr('data-otabid'),
903 + bcom_keys: new Array
904 + };
905 + for (var elemid in vbo_guest_misconduct_fields) {
906 + if (!vbo_guest_misconduct_fields.hasOwnProperty(elemid)) {
907 + continue;
908 + }
909 + if (!jQuery('#' + vbo_guest_misconduct_fields[elemid]).length || !jQuery('#' + vbo_guest_misconduct_fields[elemid]).closest('.vbo-modal-guestmisconduct-addnew-elem').length) {
910 + // form element not found, or parent container not found
911 + continue;
912 + }
913 + if (jQuery('#' + vbo_guest_misconduct_fields[elemid]).closest('.vbo-modal-guestmisconduct-addnew-elem').is(':visible')) {
914 + // we include this value in the AJAX request
915 + var field_name = jQuery('#' + vbo_guest_misconduct_fields[elemid]).attr('data-gmname');
916 + var submit_key = 'bcom_' + field_name;
917 + submit_fields[submit_key] = jQuery('#' + vbo_guest_misconduct_fields[elemid]).val();
918 + submit_fields['bcom_keys'].push(submit_key);
919 + }
920 + }
819 921
820 - var submit_fields = {
821 - tmpl: "component",
822 - bid: jQuery('.vbo-modal-guestmisconduct-addnew').attr('data-bid'),
823 - otabid: jQuery('.vbo-modal-guestmisconduct-addnew').attr('data-otabid'),
824 - bcom_keys: new Array
825 - };
826 - for (var elemid in vbo_guest_misconduct_fields) {
827 - if (!vbo_guest_misconduct_fields.hasOwnProperty(elemid)) {
828 - continue;
829 - }
830 - if (!jQuery('#' + vbo_guest_misconduct_fields[elemid]).length || !jQuery('#' + vbo_guest_misconduct_fields[elemid]).closest('.vbo-modal-guestmisconduct-addnew-elem').length) {
831 - // form element not found, or parent container not found
832 - continue;
833 - }
834 - if (jQuery('#' + vbo_guest_misconduct_fields[elemid]).closest('.vbo-modal-guestmisconduct-addnew-elem').is(':visible')) {
835 - // we include this value in the AJAX request
836 - var field_name = jQuery('#' + vbo_guest_misconduct_fields[elemid]).attr('data-gmname');
837 - var submit_key = 'bcom_' + field_name;
838 - submit_fields[submit_key] = jQuery('#' + vbo_guest_misconduct_fields[elemid]).val();
839 - submit_fields['bcom_keys'].push(submit_key);
840 - }
841 - }
922 + // make the AJAX request to VCM
923 + var jqxhr = jQuery.ajax({
924 + type: "POST",
925 + url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikchannelmanager&task=submit_bcom_guestmisconduct'); ?>",
926 + data: submit_fields
927 + }).done(function(res) {
928 + try {
929 + var misconduct_rs = typeof res === 'string' ? JSON.parse(res) : res;
930 + console.log(submit_fields, misconduct_rs);
842 931
843 - // make the AJAX request to VCM
844 - var jqxhr = jQuery.ajax({
845 - type: "POST",
846 - url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikchannelmanager&task=submit_bcom_guestmisconduct'); ?>",
847 - data: submit_fields
848 - }).done(function(res) {
849 - try {
850 - var misconduct_rs = JSON.parse(res);
851 - console.log(submit_fields, misconduct_rs);
932 + // make sure the request was successful
933 + if (!misconduct_rs.status) {
934 + alert(misconduct_rs.error);
935 + vboGuestMisconductEnableSubmit();
936 + return false;
937 + }
852 938
853 - // make sure the request was successful
854 - if (!misconduct_rs.status) {
855 - alert(misconduct_rs.error);
939 + // make another AJAX request (now on VBO) to update the booking history
940 + var event_descr = jQuery('#vbo-gm-mandfield-details_text').length ? jQuery('#vbo-gm-mandfield-details_text').val() : '';
941 + jQuery.ajax({
942 + type: "POST",
943 + url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=store_booking_history_event'); ?>",
944 + data: {
945 + tmpl: "component",
946 + bid: jQuery('.vbo-modal-guestmisconduct-addnew').attr('data-bid'),
947 + event: 'GM',
948 + descr: event_descr
949 + }
950 + }).done(function(res) {
951 + // just reload the page in case of success
952 + document.location.href = jQuery('#vbo-gm-reloadlink').attr('href');
953 + }).fail(function() {
954 + // we reload the page even in case of failure
955 + document.location.href = jQuery('#vbo-gm-reloadlink').attr('href');
956 + });
957 + } catch (e) {
958 + console.log(res);
959 + alert('Invalid update response. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
856 960 vboGuestMisconductEnableSubmit();
961 + hideVboDialogGuestMisconduct();
857 962 return false;
858 963 }
964 + }).fail(function() {
965 + alert('Update Request failed. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
966 + vboGuestMisconductEnableSubmit();
967 + return false;
968 + });
969 + }
859 970
860 - // make another AJAX request (now on VBO) to update the booking history
861 - var event_descr = jQuery('#vbo-gm-mandfield-details_text').length ? jQuery('#vbo-gm-mandfield-details_text').val() : '';
862 - jQuery.ajax({
863 - type: "POST",
864 - url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=store_booking_history_event'); ?>",
865 - data: {
866 - tmpl: "component",
867 - bid: jQuery('.vbo-modal-guestmisconduct-addnew').attr('data-bid'),
868 - event: 'GM',
869 - descr: event_descr
870 - }
871 - }).done(function(res) {
872 - // just reload the page in case of success
873 - document.location.href = jQuery('#vbo-gm-reloadlink').attr('href');
874 - }).fail(function() {
875 - // we reload the page even in case of failure
876 - document.location.href = jQuery('#vbo-gm-reloadlink').attr('href');
971 + function hideVboDialogGuestMisconduct() {
972 + if (vbodialogguestmisconduct_on === true) {
973 + jQuery(".vbo-modal-overlay-block-guestmisconduct").fadeOut(400, function () {
974 + jQuery(".vbo-modal-overlay-content-guestmisconduct").show();
877 975 });
878 - } catch (e) {
879 - console.log(res);
880 - alert('Invalid update response. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
881 - vboGuestMisconductEnableSubmit();
882 - hideVboDialogGuestMisconduct();
883 - return false;
976 + // turn flag off
977 + vbodialogguestmisconduct_on = false;
884 978 }
885 - }).fail(function() {
886 - alert('Update Request failed. Vik Channel Manager needs to be installed, configured and updated to the latest version.');
887 - vboGuestMisconductEnableSubmit();
888 - return false;
889 - });
890 - }
979 + }
891 980
892 - function hideVboDialogGuestMisconduct() {
893 - if (vbodialogguestmisconduct_on === true) {
894 - jQuery(".vbo-modal-overlay-block-guestmisconduct").fadeOut(400, function () {
895 - jQuery(".vbo-modal-overlay-content-guestmisconduct").show();
981 + function vboGuestMisconductChangeCategory(gmcid) {
982 + jQuery('.vbo-modal-guestmisconduct-addnew-cond').each(function() {
983 + if (jQuery(this).attr('data-gmcid') == gmcid) {
984 + jQuery(this).show();
985 + } else {
986 + jQuery(this).hide();
987 + }
896 988 });
897 - // turn flag off
898 - vbodialogguestmisconduct_on = false;
899 989 }
900 - }
901 990
902 - function vboGuestMisconductChangeCategory(gmcid) {
903 - jQuery('.vbo-modal-guestmisconduct-addnew-cond').each(function() {
904 - if (jQuery(this).attr('data-gmcid') == gmcid) {
905 - jQuery(this).show();
906 - } else {
907 - jQuery(this).hide();
908 - }
909 - });
910 - }
991 + function vboGuestMisconductDisableSubmit() {
992 + jQuery('#vbo-guest-misconduct-submit-btn').prepend('<i class="<?php echo VikBookingIcons::i('refresh', 'fa-spin fa-fw'); ?>"></i> ').prop('disabled', true);
993 + }
911 994
912 - function vboGuestMisconductDisableSubmit() {
913 - jQuery('#vbo-guest-misconduct-submit-btn').prepend('<i class="<?php echo VikBookingIcons::i('refresh', 'fa-spin fa-fw'); ?>"></i> ').prop('disabled', true);
914 - }
995 + function vboGuestMisconductEnableSubmit() {
996 + jQuery('#vbo-guest-misconduct-submit-btn').prop('disabled', false).find('i').remove();
997 + }
998 + </script>
915 999
916 - function vboGuestMisconductEnableSubmit() {
917 - jQuery('#vbo-guest-misconduct-submit-btn').prop('disabled', false).find('i').remove();
918 - }
919 - </script>
1000 + </div>
920 1001 <?php
921 1002 }
922 1003 if ($this->vcm_host_to_guest_review) {
923 1004 ?>
@@ -975,9 +1056,9 @@
975 1056 $ir_data_callback = function($data) {
976 1057 return (is_object($data) && !empty($data->av_type));
977 1058 };
978 1059 $prev_ir_data = $history_obj->getEventsWithData('IR', $ir_data_callback, true);
979 - if (is_array($prev_ir_data) && count($prev_ir_data)) {
1060 + if (is_array($prev_ir_data) && $prev_ir_data) {
980 1061 // display a message about the availability type at the time of creation of the inquiry reservation
981 1062
982 1063 // parse original stay dates into DateTime objects
983 1064 $orig_checkin_dt_obj = new JDate($prev_ir_data[0]->checkin_date);
@@ -984,9 +1065,9 @@
984 1065 $orig_checkout_dt_obj = new JDate($prev_ir_data[0]->checkout_date);
985 1066 ?>
986 1067 <div class="vbo-bookdet-container vbo-bookdet-inquiry-alert">
987 1068 <div class="vbo-bookdet-inquiry-alert-dismiss">
988 - <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>
989 1070 </div>
990 1071 <div class="vbo-bookdet-inquiry-alert-message">
991 1072 <div class="vbo-bookdet-inquiry-alert-top">
992 1073 <span><?php echo JText::translate('VBO_WEB_INQUIRY_ALERT'); ?></span>
@@ -1020,8 +1101,43 @@
1020 1101 </div>
1021 1102 <div class="vbo-bookdet-inquiry-alert-bottom">
1022 1103 <span><?php echo JText::translate('VBO_WEB_INQUIRY_SUGG'); ?></span>
1023 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 + ?>
1024 1140 </div>
1025 1141 </div>
1026 1142 <?php
1027 1143 }
@@ -1037,16 +1153,23 @@
1037 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>
1038 1154 </div>
1039 1155 <?php
1040 1156 }
1041 - 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)) {
1042 1165
1043 1166 if (VBOPlatformDetection::isWordPress()) {
1044 1167 /**
1045 1168 * @wponly display warning message if no valid Shortcodes found
1046 1169 */
1047 - $model = JModel::getInstance('vikbooking', 'shortcodes');
1048 - $itemid = $model->best('booking');
1170 + $model = JModel::getInstance('vikbooking', 'shortcodes');
1171 + $itemid = $model->best('booking');
1049 1172 if (!$itemid) {
1050 1173 VikError::raiseWarning('', 'No Shortcodes found, or no Shortcodes being used in Pages/Posts.');
1051 1174 }
1052 1175 }
@@ -1056,9 +1179,9 @@
1056 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>
1057 1180 </div>
1058 1181 <?php
1059 1182 }
1060 - 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))) {
1061 1184 ?>
1062 1185 <div class="vbo-bookingdet-command">
1063 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>
1064 1187 </div>
@@ -1083,9 +1206,9 @@
1083 1206 </div>
1084 1207 <?php
1085 1208 }
1086 1209 }
1087 - if ($row['status'] == "standby" || ($row['status'] == "cancelled" && $row['checkout'] >= time())) {
1210 + if ($row['status'] == "standby" || ($row['status'] == "cancelled" && $row['checkout'] >= $current_ts)) {
1088 1211 ?>
1089 1212 <div class="vbo-bookingdet-command">
1090 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>
1091 1214 </div>
@@ -1094,9 +1217,9 @@
1094 1217 if ($row['status'] == "cancelled" && !$row['closure']) {
1095 1218 ?>
1096 1219 <div class="vbo-bookingdet-command">
1097 1220 <button type="button" class="btn btn-warning vbo-context-menu-btn vbo-context-menu-actionscanc">
1098 - <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>
1099 1222 <span class="vbo-context-menu-ico"><?php VikBookingIcons::e('sort-down'); ?></span>
1100 1223 </button>
1101 1224 </div>
1102 1225
@@ -1197,11 +1320,84 @@
1197 1320 <div class="vbo-bookingdet-tab" data-vbotab="vbo-tab-admin"><?php echo JText::translate('VBOBOOKDETTABADMIN'); ?></div>
1198 1321 </div>
1199 1322 </div>
1200 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 +
1201 1341 <div class="vbo-bookingdet-tab-cont" id="vbo-tab-details" style="display: block;">
1202 1342 <div class="vbo-bookingdet-innercontainer">
1203 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 + ?>
1204 1400 <div class="vbo-bookingdet-detcont<?php echo $row['closure'] > 0 ? ' vbo-bookingdet-closure' : ''; ?>">
1205 1401 <?php
1206 1402 $custdata_parts = explode("\n", $row['custdata']);
1207 1403 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
@@ -1225,11 +1421,11 @@
1225 1421 }
1226 1422 unset($custdet_parts[0]);
1227 1423 if ((!strcasecmp(trim($custdet_parts[1]), 'http') || !strcasecmp(trim($custdet_parts[1]), 'https')) && !empty($custdet_parts[2]) && strpos($custdet_parts[2], '//') !== false) {
1228 1424 // this is a URI
1229 - $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>';
1230 1426 } else {
1231 - $custd_val = trim(implode(':', $custdet_parts));
1427 + $custd_val = htmlspecialchars(trim(implode(':', $custdet_parts)));
1232 1428 }
1233 1429 }
1234 1430 ?>
1235 1431 <div class="vbo-bookingdet-userdetail">
@@ -1287,13 +1483,20 @@
1287 1483
1288 1484 </div>
1289 1485
1290 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 +
1291 1494 // build list of invoice details (path/uri & label/button), one by default
1292 1495 $invoice_details = [
1293 1496 [
1294 - 'path' => implode(DIRECTORY_SEPARATOR, [VBO_SITE_PATH, 'helpers', 'invoices', 'generated', "{$row['id']}_{$row['sid']}.pdf"]),
1295 - '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",
1296 1499 'label' => JText::translate('VBOCOLORTAGRULEINVONE'),
1297 1500 'btn' => JText::translate('VBOINVDOWNLOAD'),
1298 1501 ]
1299 1502 ];
@@ -1323,14 +1526,30 @@
1323 1526 ?>
1324 1527 <div class="vbo-bookingdet-detcont vbo-bookingdet-detcont-labels-wrap vbo-hidein-print">
1325 1528 <?php
1326 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'];
1327 1536 ?>
1328 - <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])); ?>">
1329 1538 <span class="label label-info">
1330 - <span><?php echo $otachannel_name . ' ID'; ?></span>
1331 - <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>
1332 1550 </span>
1551 + <textarea class="vbo-textarea-copyable" data-copy="otabid"><?php echo htmlentities($displayOtaBid); ?></textarea>
1333 1552 </div>
1334 1553 <?php
1335 1554 }
1336 1555 if (strlen((string)$row['confirmnumber'])) {
@@ -1342,8 +1561,103 @@
1342 1561 </span>
1343 1562 </div>
1344 1563 <?php
1345 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 +
1346 1660 if ($invoiced) {
1347 1661 foreach ($invoice_details as $invoice_detail) {
1348 1662 ?>
1349 1663 <div class="vbo-bookingdet-detcont-label vbo-bookingdet-detcont-label-invoice">
@@ -1357,10 +1671,10 @@
1357 1671 }
1358 1672 if ($this->vcm_review) {
1359 1673 ?>
1360 1674 <div class="vbo-bookingdet-detcont-label vbo-bookingdet-detcont-label-review">
1361 - <span class="label label-warning">
1362 - <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>
1363 1677 </span>
1364 1678 </div>
1365 1679 <?php
1366 1680 // check if a notification was clicked
@@ -1365,13 +1679,15 @@
1365 1679 <?php
1366 1680 // check if a notification was clicked
1367 1681 if (VikRequest::getString('notif_action', '', 'request') == 'see_guest_review') {
1368 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'];
1369 1684 JFactory::getDocument()->addScriptDeclaration(
1370 1685 <<<JS
1371 1686 jQuery(function() {
1372 1687 setTimeout(() => {
1373 - 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'});
1374 1690 }, 200);
1375 1691 });
1376 1692 JS
1377 1693 );
@@ -1382,10 +1698,9 @@
1382 1698 <!-- Define role to detect the supported hook -->
1383 1699 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"invoice","type":"field"} -->
1384 1700
1385 1701 <?php
1386 - if (isset($bookingSidebarForms['invoice']))
1387 - {
1702 + if (isset($bookingSidebarForms['invoice'])) {
1388 1703 echo $bookingSidebarForms['invoice'];
1389 1704 }
1390 1705 ?>
1391 1706
@@ -1400,15 +1715,15 @@
1400 1715 </div>
1401 1716 <div class="vbo-bookingdet-inpwrap">
1402 1717 <div class="vbo-bookingdet-inpcont">
1403 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>
1404 1722 </div>
1405 - <div class="vbo-bookingdet-btncont vbo-bookingdet-save-email" style="display: none;">
1406 - <button type="button" class="btn btn-success" onclick="document.adminForm.submit();"><?php VikBookingIcons::e('save'); ?> <?php echo JText::translate('VBSAVE'); ?></button>
1407 - </div>
1408 1723 <?php if (!empty($row['custmail'])) : ?>
1409 1724 <div class="vbo-bookingdet-btncont">
1410 - <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>
1411 1726 </div>
1412 1727 <?php endif; ?>
1413 1728 </div>
1414 1729
@@ -1415,10 +1730,9 @@
1415 1730 <!-- Define role to detect the supported hook -->
1416 1731 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"email","type":"field"} -->
1417 1732
1418 1733 <?php
1419 - if (isset($bookingSidebarForms['email']))
1420 - {
1734 + if (isset($bookingSidebarForms['email'])) {
1421 1735 echo $bookingSidebarForms['email'];
1422 1736 }
1423 1737 ?>
1424 1738
@@ -1430,13 +1744,27 @@
1430 1744 <div class="vbo-bookingdet-inpwrap">
1431 1745 <div class="vbo-bookingdet-inpcont">
1432 1746 <?php echo $vbo_app->printPhoneInputField(array('name' => 'custphone', 'id' => 'custphone', 'value' => $this->escape($row['phone'])), array('nationalMode' => false, 'fullNumberOnBlur' => true)); ?>
1433 1747 </div>
1434 - <?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 + ?>
1435 1753 <div class="vbo-bookingdet-btncont">
1436 - <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>
1437 1755 </div>
1438 - <?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 + ?>
1439 1767 </div>
1440 1768
1441 1769 <!-- Define role to detect the supported hook -->
1442 1770 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"phone","type":"field"} -->
@@ -1441,24 +1769,48 @@
1441 1769 <!-- Define role to detect the supported hook -->
1442 1770 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","key":"phone","type":"field"} -->
1443 1771
1444 1772 <?php
1445 - if (isset($bookingSidebarForms['phone']))
1446 - {
1773 + if (isset($bookingSidebarForms['phone'])) {
1447 1774 echo $bookingSidebarForms['phone'];
1448 1775 }
1449 1776 ?>
1450 1777
1451 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>
1452 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 +
1453 1807 <!-- Define role to detect the supported hook -->
1454 1808 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderSidebar","type":"fieldset"} -->
1455 1809
1456 1810 <?php
1457 - foreach ($bookingSidebarForms as $legend => $form)
1458 - {
1459 - if (in_array($legend, ['userdetails', 'invoice', 'email', 'phone']))
1460 - {
1811 + foreach ($bookingSidebarForms as $legend => $form) {
1812 + if (in_array($legend, ['userdetails', 'invoice', 'email', 'phone'])) {
1461 1813 // skip default forms
1462 1814 continue;
1463 1815 }
1464 1816 ?>
@@ -1508,10 +1860,10 @@
1508 1860 <td class="vbo-bookingdet-summary-room-firstcell">
1509 1861 <div class="vbo-bookingdet-summary-room-wrap">
1510 1862 <div class="vbo-bookingdet-summary-roomnum"><?php VikBookingIcons::e($room_icon); ?> <?php echo JText::translate('VBEDITORDERTHREE').' '.$num; ?></div>
1511 1863 <?php
1512 - //Room Specific Unit
1513 - 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'])) {
1514 1866 $room_params = json_decode($or['params'], true);
1515 1867 $arr_features = array();
1516 1868 $unavailable_indexes = VikBooking::getRoomUnitNumsUnavailable($row, $or['idroom']);
1517 1869 if (is_array($room_params) && array_key_exists('features', $room_params) && !empty($room_params['features'])) {
@@ -1526,9 +1878,9 @@
1526 1878 }
1527 1879 }
1528 1880 }
1529 1881 }
1530 - if (count($arr_features)) {
1882 + if ($arr_features) {
1531 1883 // $or['id'] equals to the ID of each matching record in _ordersrooms
1532 1884 ?>
1533 1885 <div class="vbo-bookingdet-summary-roomnum-chunit">
1534 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']] : ''); ?>
@@ -1554,9 +1906,9 @@
1554 1906 if ($arrpeople[$num]['children'] > 0) {
1555 1907 $age_str = '';
1556 1908 if (!empty($arrpeople[$num]['children_age'])) {
1557 1909 $json_child = json_decode($arrpeople[$num]['children_age'], true);
1558 - 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']) {
1559 1911 $age_str = ' '.JText::sprintf('VBORDERCHILDAGES', implode(', ', $json_child['age']));
1560 1912 }
1561 1913 }
1562 1914 ?>
@@ -1645,9 +1997,9 @@
1645 1997 </div>
1646 1998 <?php
1647 1999 }
1648 2000
1649 - 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']) {
1650 2002 // print split stay information for this room
1651 2003 $room_stay_checkin = !empty($room_stay_dates[$ind]['checkin_ts']) ? $room_stay_dates[$ind]['checkin_ts'] : $room_stay_dates[$ind]['checkin'];
1652 2004 $room_stay_checkout = !empty($room_stay_dates[$ind]['checkout_ts']) ? $room_stay_dates[$ind]['checkout_ts'] : $room_stay_dates[$ind]['checkout'];
1653 2005 $room_stay_nights = $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout);
@@ -1659,9 +2011,9 @@
1659 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>
1660 2012 </div>
1661 2013 </div>
1662 2014 <?php
1663 - } 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']) {
1664 2016 // print modified stay dates information for this room
1665 2017 $room_stay_checkin = $room_stay_records[$ind]['checkin'];
1666 2018 $room_stay_checkout = $room_stay_records[$ind]['checkout'];
1667 2019 $room_stay_nights = $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout);
@@ -1703,14 +2055,22 @@
1703 2055 }
1704 2056 $stept = explode(":", $oo);
1705 2057 $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `id`=".(int)$stept[0].";";
1706 2058 $dbo->setQuery($q);
1707 - $dbo->execute();
1708 - if ($dbo->getNumRows() != 1) {
2059 + $actopt = $dbo->loadAssocList();
2060 + if (!$actopt) {
1709 2061 continue;
1710 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 +
1711 2072 $counter++;
1712 - $actopt = $dbo->loadAssocList();
1713 2073 $chvar = '';
1714 2074 if (!empty($actopt[0]['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
1715 2075 $optagenames = VikBooking::getOptionIntervalsAges($actopt[0]['ageintervals']);
1716 2076 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt[0]['ageintervals']);
@@ -1745,9 +2105,9 @@
1745 2105 }
1746 2106 $actopt[0]['chageintv'] = $chvar;
1747 2107 $actopt[0]['name'] .= ' ('.$optagenames[($chvar - 1)].')';
1748 2108 $realcost = (intval($actopt[0]['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $room_stay_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
1749 - } else {
2109 + } elseif (is_numeric($stept[1])) {
1750 2110 // VBO 1.11 - options percentage cost of the room total fee
1751 2111 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
1752 2112 $deftar_basecosts = $or['cust_cost'];
1753 2113 } else {
@@ -1756,8 +2116,10 @@
1756 2116 }
1757 2117 $actopt[0]['cost'] = (int)$actopt[0]['pcentroom'] ? ($deftar_basecosts * $actopt[0]['cost'] / 100) : $actopt[0]['cost'];
1758 2118 //
1759 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;
1760 2122 }
1761 2123 if ($actopt[0]['maxprice'] > 0 && $realcost > $actopt[0]['maxprice']) {
1762 2124 $realcost = $actopt[0]['maxprice'];
1763 2125 if (intval($actopt[0]['hmany']) == 1 && intval($stept[1]) > 1) {
@@ -1764,8 +2126,19 @@
1764 2126 $realcost = $actopt[0]['maxprice'] * $stept[1];
1765 2127 }
1766 2128 }
1767 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 +
1768 2141 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt[0]['idiva']);
1769 2142 ?>
1770 2143 <tr class="vbo-bookingdet-summary-options">
1771 2144 <td class="vbo-bookingdet-summary-options-title"><?php echo $counter == 1 ? JText::translate('VBEDITORDEREIGHT') : '&nbsp;'; ?></td>
@@ -1853,18 +2226,17 @@
1853 2226 //Reservation Total
1854 2227 //Taxes Breakdown (only if tot_taxes is greater than 0)
1855 2228 $tax_breakdown = array();
1856 2229 $base_aliq = 0;
1857 - if (count($all_id_prices) > 0 && $row['tot_taxes'] > 0) {
2230 + if ($all_id_prices && $row['tot_taxes'] > 0) {
1858 2231 //only last type of price assuming that the tax breakdown is equivalent in case of different rates
1859 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;";
1860 2233 $dbo->setQuery($q);
1861 - $dbo->execute();
1862 - if ($dbo->getNumRows() > 0) {
1863 - $breakdown_info = $dbo->loadAssoc();
2234 + $breakdown_info = $dbo->loadAssoc();
2235 + if ($breakdown_info) {
1864 2236 if (!empty($breakdown_info['breakdown']) && !empty($breakdown_info['aliq'])) {
1865 2237 $tax_breakdown = json_decode($breakdown_info['breakdown'], true);
1866 - $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();
1867 2239 $base_aliq = $breakdown_info['aliq'];
1868 2240 }
1869 2241 }
1870 2242 }
@@ -1905,9 +2277,9 @@
1905 2277 <div class="vbdiscenter-entry">
1906 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>
1907 2279 </div>
1908 2280 <?php
1909 - if (count($tax_breakdown)) {
2281 + if ($tax_breakdown) {
1910 2282 foreach ($tax_breakdown as $tbkk => $tbkv) {
1911 2283 $tax_break_cost = $row['tot_taxes'] * floatval($tbkv['aliq']) / $base_aliq;
1912 2284 ?>
1913 2285 <div class="vbdiscenter-entry vbdiscenter-entry-breakdown">
@@ -1925,9 +2297,19 @@
1925 2297 </div>
1926 2298 <div class="vbdiscenter-entry">
1927 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>
1928 2300 </div>
2301 + <?php
2302 + if ($booking_has_damage_dep) {
2303 + // allow to overwrite (or see) the amount of damage deposit
2304 + ?>
1929 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">
1930 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>
1931 2313 </div>
1932 2314 <div class="vbdiscenter-entrycentered">
1933 2315 <button type="submit" class="btn btn-success"><?php echo JText::translate('VBAPPLYDISCOUNTSAVE'); ?></button>
@@ -1934,9 +2316,26 @@
1934 2316 </div>
1935 2317 </div>
1936 2318 </td>
1937 2319 <td>
1938 - <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 + ?>
1939 2338 </td>
1940 2339 </tr>
1941 2340 <?php
1942 2341 if ($row['total'] > 0 && !$row['closure']) {
@@ -1941,8 +2340,12 @@
1941 2340 <?php
1942 2341 if ($row['total'] > 0 && !$row['closure']) {
1943 2342 $has_paid = ($row['totpaid'] > 0);
1944 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 + }
1945 2348 if ($has_paid || $row['status'] != 'standby') {
1946 2349 ?>
1947 2350 <tr class="vbo-bookingdet-summary-totpaid">
1948 2351 <td>&nbsp;</td>
@@ -2000,9 +2403,9 @@
2000 2403 </tr>
2001 2404 <?php
2002 2405 }
2003 2406 }
2004 - 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')) {
2005 2408 /**
2006 2409 * The amount payable can be modified by the admin.
2007 2410 *
2008 2411 * @since 1.14 (J) - 1.4.0 (WP)
@@ -2108,9 +2511,9 @@
2108 2511 <?php
2109 2512 if (!$row['closure']) {
2110 2513 ?>
2111 2514 <div class="vbo-bookingdet-admin-entry">
2112 - <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>
2113 2516 <span style="display: block;"><?php echo JText::translate('VBOSEARCHEXISTCUST'); ?></span>
2114 2517 <span class="vbo-eorder-assigncust" style="margin-bottom: 1px;">
2115 2518 <input type="text" id="vbo-searchcust" autocomplete="off" value="" placeholder="<?php echo JText::translate('VBOSEARCHCUSTBY'); ?>" size="30" style="margin-bottom: 0;" />
2116 2519 </span>
@@ -2146,11 +2549,11 @@
2146 2549 <?php
2147 2550 }
2148 2551 $chpayment = '';
2149 2552 if ($payments) {
2150 - $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>';
2151 2554 foreach ($payments as $pay) {
2152 - $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>';
2153 2556 }
2154 2557 $chpayment .= '</select></div>';
2155 2558 }
2156 2559 echo $chpayment;
@@ -2225,9 +2628,12 @@
2225 2628 $history = $history_obj->loadHistory();
2226 2629 ?>
2227 2630 <div class="vbo-bookingdet-noteslogs-btns">
2228 2631 <div class="vbo-bookingdet-noteslogs-btn vbo-bookingdet-noteslogs-btn-active" data-id="notes">
2229 - <a href="javascript: void(0);" id="vbo-trig-notes" onclick="javascript: vbToggleNotes(this);"><?php VikBookingIcons::e('user-lock'); ?> <?php echo JText::translate('VBADMINNOTESTOGGLE'); ?></a>
2632 + <a href="javascript: void(0);" id="vbo-trig-notes" onclick="javascript: vbToggleNotes(this);">
2633 + <?php VikBookingIcons::e('user-lock'); ?>
2634 + <span class="vbo-bookingdet-admintab-lbl"><?php echo JText::translate('VBADMINNOTESTOGGLE'); ?></span>
2635 + </a>
2230 2636 </div>
2231 2637 <?php
2232 2638 /**
2233 2639 * Initialize chat instance by getting the proper channel name
@@ -2240,9 +2646,9 @@
2240 2646 } else {
2241 2647 $channelparts = explode('_', $row['channel']);
2242 2648 // check if this is a meta search channel
2243 2649 $is_meta_search = false;
2244 - 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')) {
2245 2651 $is_meta_search = empty($row['idorderota']);
2246 2652 }
2247 2653 if ($is_meta_search) {
2248 2654 // customer of type sales channel should use front-end reservation chat handler
@@ -2256,24 +2662,51 @@
2256 2662 //
2257 2663 if (!$printreceipt && !is_null($messaging)) {
2258 2664 ?>
2259 2665 <div class="vbo-bookingdet-noteslogs-btn" data-id="messaging">
2260 - <a name="messaging" href="javascript: void(0);" id="vbo-trig-messaging" onclick="javascript: vbToggleMessaging(this);"><?php VikBookingIcons::e('comment-dots'); ?> <?php echo JText::translate('VBO_GUEST_MESSAGING'); ?></a>
2666 + <a name="messaging" href="javascript: void(0);" id="vbo-trig-messaging" onclick="javascript: vbToggleMessaging(this);">
2667 + <?php VikBookingIcons::e('comment-dots'); ?>
2668 + <span class="vbo-bookingdet-admintab-lbl"><?php echo JText::translate('VBO_GUEST_MESSAGING'); ?></span>
2669 + </a>
2261 2670 </div>
2262 2671 <?php
2263 2672 }
2264 - if (!empty($row['paymentlog'])) {
2265 - ?>
2266 - <div class="vbo-bookingdet-noteslogs-btn" data-id="paylogs">
2267 - <a href="javascript: void(0);" id="vbo-trig-paylogs" onclick="javascript: vbToggleLog(this);"><?php VikBookingIcons::e('credit-card'); ?> <?php echo JText::translate('VBPAYMENTLOGTOGGLE'); ?></a>
2268 - <a name="paymentlog" href="javascript: void(0);"></a>
2269 - </div>
2270 - <?php
2673 +
2674 + /**
2675 + * Build up a tab to summarize the "takings" for this sole booking in case of relevant events.
2676 + *
2677 + * @since 1.16.9 (J) - 1.6.9 (WP)
2678 + */
2679 + $takings = [];
2680 + if ($history) {
2681 + // payment related events
2682 + $h_payment_group = $history_obj->getTypeGroups($group = 'GPM');
2683 +
2684 + // merge default types with 'PU' for the manually set "new amount paid" and with 'RU' (refunded amount updated)
2685 + $takings_events = array_unique(array_merge(($h_payment_group['types'] ?? []), ['PU', 'RU']));
2686 +
2687 + // load the relevant events with data
2688 + $takings = $history_obj->getEventsWithData($takings_events, null, false);
2689 +
2690 + if ($takings) {
2691 + ?>
2692 + <div class="vbo-bookingdet-noteslogs-btn pro-feature" data-id="takings">
2693 + <a href="javascript: void(0);" id="vbo-trig-bookhistory" onclick="javascript: vbToggleDynamicPanel(this, '#vboextrapanel-takings');">
2694 + <?php VikBookingIcons::e('wallet'); ?>
2695 + <span class="vbo-bookingdet-admintab-lbl"><?php echo JText::translate('VBO_TAKINGS'); ?></span>
2696 + </a>
2697 + </div>
2698 + <?php
2699 + }
2271 2700 }
2272 - if (count($history)) {
2701 +
2702 + if ($history) {
2273 2703 ?>
2274 2704 <div class="vbo-bookingdet-noteslogs-btn" data-id="history">
2275 - <a href="javascript: void(0);" id="vbo-trig-bookhistory" onclick="javascript: vbToggleHistory(this);"><?php VikBookingIcons::e('history'); ?> <?php echo JText::translate('VBOBOOKHISTORYTAB'); ?></a>
2705 + <a href="javascript: void(0);" id="vbo-trig-bookhistory" onclick="javascript: vbToggleHistory(this);">
2706 + <?php VikBookingIcons::e('history'); ?>
2707 + <span class="vbo-bookingdet-admintab-lbl"><?php echo JText::translate('VBOBOOKHISTORYTAB'); ?></span>
2708 + </a>
2276 2709 </div>
2277 2710 <script type="text/javascript">
2278 2711 if (window.location.hash == '#bookhistory') {
2279 2712 setTimeout(function() {
@@ -2285,9 +2718,12 @@
2285 2718 <?php
2286 2719 }
2287 2720 ?>
2288 2721 <div class="vbo-bookingdet-noteslogs-btn pro-feature" data-id="invnotes">
2289 - <a href="javascript: void(0);" class="hasTooltip" id="vbo-trig-invnotes" onclick="javascript: vbToggleInvNotes(this);" title="<?php echo addslashes(JText::translate('VBBOOKINGINVNOTESHELP')); ?>"><?php VikBookingIcons::e('file-invoice'); ?> <?php echo JText::translate('VBBOOKINGINVNOTES'); ?></a>
2722 + <a href="javascript: void(0);" class="hasTooltip" id="vbo-trig-invnotes" onclick="javascript: vbToggleInvNotes(this);" title="<?php echo addslashes(JText::translate('VBBOOKINGINVNOTESHELP')); ?>">
2723 + <?php VikBookingIcons::e('file-invoice'); ?>
2724 + <span class="vbo-bookingdet-admintab-lbl"><?php echo JText::translate('VBBOOKINGINVNOTES'); ?></span>
2725 + </a>
2290 2726 </div>
2291 2727
2292 2728 <!-- Define role to detect the supported hook -->
2293 2729 <!-- {"rule":"customizer","event":"onDisplayViewVikBookingEditorderAdministration","type":"fieldset"} -->
@@ -2296,28 +2732,383 @@
2296 2732 foreach ($adminPanelForms as $legend => $form)
2297 2733 {
2298 2734 $legendId = preg_replace("/[^a-zA-Z0-9_\-]+/", '', $legend);
2299 2735 ?>
2300 - <div class="vbo-bookingdet-noteslogs-btn" data-id="<?php echo $legendId; ?>">
2301 - <a href="javascript: void(0);" class="hasTooltip" id="vbo-trig-<?php echo $legendId; ?>" onclick="javascript: vbToggleDynamicPanel(this, '#vboextrapanel-<?php echo $legendId; ?>');">
2302 - <?php
2303 - VikBookingIcons::e(!empty($adminFormsSetup->icons[$legend]) ? $adminFormsSetup->icons[$legend] : 'plug');
2304 - echo JText::translate($legend);
2305 - ?>
2306 - </a>
2307 - </div>
2736 + <div class="vbo-bookingdet-noteslogs-btn" data-id="<?php echo $legendId; ?>">
2737 + <a href="javascript: void(0);" class="hasTooltip" id="vbo-trig-<?php echo $legendId; ?>" onclick="javascript: vbToggleDynamicPanel(this, '#vboextrapanel-<?php echo $legendId; ?>');">
2738 + <?php VikBookingIcons::e(!empty($adminFormsSetup->icons[$legend]) ? $adminFormsSetup->icons[$legend] : 'plug'); ?>
2739 + <span class="vbo-bookingdet-admintab-lbl"><?php echo JText::translate($legend); ?></span>
2740 + </a>
2741 + </div>
2308 2742 <?php
2309 2743 }
2744 +
2745 + if (!empty($row['paymentlog'])) {
2746 + $paylogs_lbl = JText::translate('VBPAYMENTLOGTOGGLE');
2747 + if ($may_have_cc_details) {
2748 + $paylogs_lbl = JText::translate('VBO_CREDIT_CARD');
2749 + }
2750 + ?>
2751 + <div class="vbo-bookingdet-noteslogs-btn" data-id="paylogs">
2752 + <a href="javascript: void(0);" id="vbo-trig-paylogs" onclick="javascript: vbToggleLog(this);">
2753 + <?php VikBookingIcons::e('credit-card'); ?>
2754 + <span class="vbo-bookingdet-admintab-lbl"><?php echo $paylogs_lbl; ?></span>
2755 + </a>
2756 + <a name="paymentlog" href="javascript: void(0);"></a>
2757 + </div>
2758 + <?php
2759 + }
2310 2760 ?>
2311 2761 </div>
2312 2762 <div class="vbo-bookingdet-noteslogs-cont">
2313 2763 <div id="vbadminnotesdiv" class="vbo-extra-panel" style="display: block;">
2314 - <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>
2315 2765 <input type="submit" name="updadmnotes" value="<?php echo JText::translate('VBADMINNOTESUPD'); ?>" class="btn btn-success" />
2316 2766 </div>
2317 2767 <?php
2318 - if (count($history)) {
2768 + if ($takings) {
2319 2769 ?>
2770 + <div class="vbo-extra-panel" id="vboextrapanel-takings" style="display: none;">
2771 + <div class="vbo-booking-takings-container table-responsive">
2772 + <table class="table">
2773 + <thead>
2774 + <tr class="vbo-booking-takings-firstrow">
2775 + <td class="vbo-booking-takings-td-totpaid"><?php echo JText::translate('VBO_TAKINGS'); ?></td>
2776 + <td class="vbo-booking-takings-td-date"><?php echo JText::translate('VBOBOOKHISTORYLBLDATE'); ?></td>
2777 + <td class="vbo-booking-takings-td-type"><?php echo JText::translate('VBOBOOKHISTORYLBLTYPE'); ?></td>
2778 + <td class="vbo-booking-takings-td-fees"><?php echo JText::translate('VBO_PROCESSING_FEES'); ?></td>
2779 + <td class="vbo-booking-takings-td-descr"><?php echo JText::translate('VBOBOOKHISTORYLBLDESC'); ?></td>
2780 + </tr>
2781 + </thead>
2782 + <tbody>
2783 + <?php
2784 + $booking_total_taking = 0;
2785 + $booking_total_prfees = 0;
2786 + foreach (array_reverse($takings) as $hist) {
2787 + // calculate amount paid and description
2788 + $tn_amount_paid = 0;
2789 + $tn_amount_fees = 0;
2790 + $tn_description = strpos((string) $hist['descr'], '<') !== false ? $hist['descr'] : preg_replace("/\R/", ' - ', (string) $hist['descr']);
2791 + if (is_object($hist['data'])) {
2792 + // calculate amount paid or amount refunded
2793 + if (($hist['data']->amount_paid ?? 0)) {
2794 + $tn_amount_paid = $hist['data']->amount_paid;
2795 + if (in_array($hist['type'], ['RF', 'RU'])) {
2796 + // turn the amount paid to negative
2797 + $tn_amount_paid = $tn_amount_paid < 0 ? $tn_amount_paid : ($tn_amount_paid - ($tn_amount_paid * 2));
2798 + if ($hist['type'] === 'RU' && $hist['data']->amount_paid < 0) {
2799 + // this indicates a lower amount refunded, so we double it to make it positive
2800 + $tn_amount_paid = abs($hist['data']->amount_paid);
2801 + }
2802 + }
2803 + }
2804 +
2805 + // calculate processing fees, if set and if any
2806 + if (($hist['data']->processing_fees ?? 0)) {
2807 + $tn_amount_fees += $hist['data']->processing_fees;
2808 + }
2809 +
2810 + // build the event description
2811 + if (($hist['data']->payment_method ?? '')) {
2812 + $tn_description = ucwords($hist['data']->payment_method);
2813 + } elseif (($hist['data']->driver ?? '')) {
2814 + $tn_description = ucwords(str_replace('_', ' ', basename($hist['data']->driver, '.php')));
2815 + }
2816 + }
2817 +
2818 + // increase (or decrease) booking total takings and processing fees amounts
2819 + $booking_total_taking += $tn_amount_paid;
2820 + $booking_total_prfees += $tn_amount_fees;
2821 + ?>
2822 + <tr class="vbo-booking-takings-row">
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>
2841 + <td><?php echo JHtml::fetch('date', $hist['dt'], 'Y-m-d H:i:s'); ?></td>
2842 + <td><?php echo $history_obj->validType($hist['type'], true); ?></td>
2843 + <td><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($tn_amount_fees), $currencysymb); ?></td>
2844 + <td><?php echo $tn_description; ?></td>
2845 + </tr>
2846 + <?php
2847 + }
2848 + ?>
2849 + </tbody>
2850 + <tfoot>
2851 + <tr>
2852 + <td colspan="2">
2853 + <span><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($booking_total_taking), $currencysymb); ?></span>
2854 + </td>
2855 + <td></td>
2856 + <td>
2857 + <span><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($booking_total_prfees), $currencysymb); ?></span>
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>
2862 + </tr>
2863 + </tfoot>
2864 + </table>
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 +
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>
3107 + <?php
3108 + }
3109 + if ($history) {
3110 + ?>
2320 3111 <div id="vbhistorydiv" class="vbo-extra-panel" style="display: none;">
2321 3112 <div class="vbo-booking-history-container table-responsive">
2322 3113 <table class="table">
2323 3114 <thead>
@@ -2331,9 +3122,9 @@
2331 3122 </thead>
2332 3123 <tbody>
2333 3124 <?php
2334 3125 foreach ($history as $hist) {
2335 - $hdescr = strpos($hist['descr'], '<') !== false ? $hist['descr'] : nl2br($hist['descr']);
3126 + $hdescr = strpos((string) $hist['descr'], '<') !== false ? $hist['descr'] : nl2br((string) $hist['descr']);
2336 3127 ?>
2337 3128 <tr class="vbo-booking-history-row">
2338 3129 <td><?php echo $history_obj->validType($hist['type'], true); ?></td>
2339 3130 <td><?php echo JHtml::fetch('date', $hist['dt'], 'Y-m-d H:i:s'); ?></td>
@@ -2359,8 +3150,9 @@
2359 3150 <?php
2360 3151 if (!empty($row['paymentlog'])) {
2361 3152 ?>
2362 3153 <div id="vbpaymentlogdiv" class="vbo-extra-panel" style="display: none;">
3154 + <div class="vbo-eorder-paymentlog-buttons">
2363 3155 <?php
2364 3156 // PCI Data Retrieval
2365 3157 $cc_acl_denied = false;
2366 3158 if (!empty($row['idorderota']) && !empty($row['channel'])) {
@@ -2375,9 +3167,9 @@
2375 3167 if (!$canDo->authorise('core.admin', 'com_vikchannelmanager')) {
2376 3168 // permissions to access VCM are required
2377 3169 $cc_acl_denied = true;
2378 3170 ?>
2379 - <p class="err"><?php echo JText::translate('JERROR_ALERTNOAUTHOR'); ?></p>
3171 + <p class="err"><?php echo JText::translate('JERROR_ALERTNOAUTHOR'); ?></p>
2380 3172 <?php
2381 3173 } else {
2382 3174 /**
2383 3175 * Limit for accessing the credit card details has been changed to check-out
@@ -2385,31 +3177,462 @@
2385 3177 *
2386 3178 * @since 1.13
2387 3179 */
2388 3180 $cardlimit = mktime(23, 59, 59, $checkout_info['mon'], ($checkout_info['mday'] + 10), $checkout_info['year']);
2389 - if (time() < $cardlimit) {
2390 - $plain_log = htmlspecialchars($row['paymentlog']);
2391 - if (stripos($plain_log, 'card number') !== false && strpos($plain_log, '****') !== false) {
2392 - // log contains credit card details
2393 - // Prepare modal (Credit Card Details)
2394 - array_push($vbo_modals_html, $vbo_app->getJmodalHtml('vbo-vcm-pcid', JText::translate('GETFULLCARDDETAILS'), '', 'width: 80%; height: 60%; margin-left: -40%; top: 20% !important;'));
2395 - // 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;'));
2396 3185 ?>
2397 - <div class="vcm-notif-pcidrq-container">
2398 - <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>
2399 - </div>
3186 + <div class="vcm-notif-pcidrq-container">
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>
3188 + </div>
2400 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
2401 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
2402 3592 }
2403 3593 }
2404 3594 } elseif (empty($row['idorderota']) && !empty($row['paymentlog']) && $row['status'] == 'standby') {
2405 3595 ?>
2406 - <div class="vbo-eorder-emptyccdata">
2407 - <a class="btn btn-warning" href="index.php?option=com_vikbooking&task=editorder&cid[]=<?php echo $row['id']; ?>&unset_cc=1" onclick="return confirm(Joomla.JText._('VBDELCONFIRM'));"><?php VikBookingIcons::e('broom'); ?> <?php echo JText::translate('VBO_EMPTY_DATA'); ?></a>
2408 - </div>
3596 + <div class="vbo-eorder-emptyccdata">
3597 + <a class="btn btn-warning" href="index.php?option=com_vikbooking&task=editorder&cid[]=<?php echo $row['id']; ?>&unset_cc=1" onclick="return confirm(Joomla.JText._('VBDELCONFIRM'));"><?php VikBookingIcons::e('broom'); ?> <?php echo JText::translate('VBO_EMPTY_DATA'); ?></a>
3598 + </div>
2409 3599 <?php
2410 3600 }
2411 3601
3602 + /**
3603 + * Attempt to display the reporting invalid credit card layout from VCM.
3604 + *
3605 + * @requires VCM >= 1.8.27
3606 + *
3607 + * @since 1.16.9 (J) - 1.6.9 (WP)
3608 + */
3609 + try {
3610 + // prepare the layout data array
3611 + $layout_data = [
3612 + 'caller' => 'vikbooking',
3613 + 'booking' => $row,
3614 + ];
3615 +
3616 + ?>
3617 + <div class="vbo-eorder-vcm-reporting-invalidcc">
3618 + <?php
3619 + // render the reporting-invalidcc layout
3620 + echo JLayoutHelper::render('reporting.invalidcc', $layout_data, null, [
3621 + 'component' => 'com_vikchannelmanager',
3622 + 'client' => 'admin',
3623 + ]);
3624 + ?>
3625 + </div>
3626 + <?php
3627 + } catch(Throwable $e) {
3628 + // do nothing
3629 + }
3630 +
3631 + ?>
3632 + </div>
3633 + <?php
3634 +
2412 3635 if (!$cc_acl_denied) {
2413 3636 ?>
2414 3637 <pre><?php echo htmlspecialchars($row['paymentlog']); ?></pre>
2415 3638 <?php
@@ -2488,92 +3711,94 @@
2488 3711 <input type="hidden" name="cid[]" value="<?php echo $row['id']; ?>" />
2489 3712 <input type="hidden" name="invnotes" id="invnotes-hid" value="" />
2490 3713 </form>
2491 3714
2492 -<div class="vbo-modal-overlay-block vbo-modal-overlay-block-sms-email">
2493 - <a class="vbo-modal-overlay-close" href="javascript: void(0);"></a>
2494 - <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;">
2495 3716
2496 - <div class="vbo-modal-overlay-content-head vbo-modal-overlay-content-sms-email-head">
2497 - <h3>
2498 - <span></span>
2499 - <span class="vbo-modal-overlay-close-times" onclick="vboToggleSendEmail();">&times;</span>
2500 - </h3>
2501 - </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">
2502 3722
2503 - <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>
2504 3731
2505 - <div id="vbo-overlay-sms-cont" style="display: none;">
2506 - <h4 style="display: none;"><?php echo JText::translate('VBSENDSMSACTION'); ?>: <span id="smstophone-lbl"><?php echo $row['phone']; ?></span></h4>
2507 - <form action="index.php?option=com_vikbooking" method="post" id="vbo-modal-form-sms">
2508 - <div class="vbo-calendar-cfield-entry">
2509 - <label for="smscont"><?php echo JText::translate('VBSENDSMSCUSTCONT'); ?></label>
2510 - <span><textarea name="smscont" id="smscont" style="width: 99%; min-width: 99%;max-width: 99%; height: 120px;"></textarea></span>
2511 3732 </div>
2512 - <input type="hidden" name="phone" id="smstophone" value="<?php echo $row['phone']; ?>" />
2513 - <input type="hidden" name="goto" value="<?php echo urlencode('index.php?option=com_vikbooking&task=editorder&cid[]='.$row['id']); ?>" />
2514 - <input type="hidden" name="task" value="sendcustomsms" />
2515 - </form>
3733 + </div>
2516 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>
2517 3740
2518 - <div id="vbo-overlay-email-cont" style="display: none;">
2519 - <h4 style="display: none;"><?php echo JText::translate('VBSENDEMAILACTION'); ?>: <span id="emailto-lbl"><?php echo $row['custmail']; ?></span></h4>
2520 - <form action="index.php?option=com_vikbooking" method="post" enctype="multipart/form-data" id="vbo-modal-form-email">
2521 - <input type="hidden" name="bid" value="<?php echo $row['id']; ?>" />
2522 - <?php
2523 - $cur_emtpl = array();
2524 - $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
2525 - $dbo->setQuery($q);
2526 - $dbo->execute();
2527 - if ($dbo->getNumRows() > 0) {
2528 - $cur_emtpl = $dbo->loadResult();
2529 - $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
2530 - $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
2531 - }
2532 - if (count($cur_emtpl) > 0) {
2533 - ?>
2534 - <div class="vbo-calendar-custmail-tpls-wrap">
2535 - <select id="emtpl-customemail" onchange="vboLoadEmailTpl(this.value);">
2536 - <option value=""><?php echo JText::translate('VBEMAILCUSTFROMTPL'); ?></option>
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">
3746 +
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>
2537 3768 <?php
2538 - foreach ($cur_emtpl as $emk => $emv) {
2539 - ?>
2540 - <optgroup label="<?php echo $emv['emailsubj']; ?>">
2541 - <option value="<?php echo $emk; ?>"><?php echo JText::translate('VBEMAILCUSTFROMTPLUSE'); ?></option>
2542 - <option value="rm<?php echo $emk; ?>"><?php echo JText::translate('VBEMAILCUSTFROMTPLRM'); ?></option>
2543 - </optgroup>
2544 - <?php
3769 + }
3770 +
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>');
2545 3782 }
2546 - ?>
2547 - </select>
2548 - </div>
2549 - <?php
2550 - }
2551 -
2552 - /**
2553 - * Load all conditional text special tags.
2554 - *
2555 - * @since 1.14 (J) - 1.4.0 (WP)
2556 - */
2557 - $extra_btns = array();
2558 - $condtext_tags = VikBooking::getConditionalRulesInstance()->getSpecialTags();
2559 - if (count($condtext_tags)) {
2560 - $condtext_tags = array_keys($condtext_tags);
2561 - foreach ($condtext_tags as $tag) {
2562 - array_push($extra_btns, '<button type="button" class="btn btn-secondary btn-small vbo-condtext-specialtag-btn" onclick="setSpecialTplTag(\'emailcont\', \'' . $tag . '\');">' . $tag . '</button>');
2563 3783 }
2564 - }
2565 - //
2566 - ?>
2567 - <div class="vbo-calendar-cfields-wrap">
2568 - <div class="vbo-calendar-cfield-entry">
2569 - <label for="emailsubj"><?php echo JText::translate('VBSENDEMAILCUSTSUBJ'); ?></label>
2570 - <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>
2571 3792 </div>
2572 - <div class="vbo-calendar-cfield-entry">
2573 - <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">
2574 3799 <?php
2575 - $special_tags_base = array(
3800 + $special_tags_base = [
2576 3801 '{customer_name}',
2577 3802 '{booking_id}',
2578 3803 '{checkin_date}',
2579 3804 '{checkout_date}',
@@ -2586,9 +3811,9 @@
2586 3811 '{total}',
2587 3812 '{total_paid}',
2588 3813 '{remaining_balance}',
2589 3814 '{booking_link}',
2590 - );
3815 + ];
2591 3816
2592 3817 $special_tags_base_html = '';
2593 3818 foreach ($special_tags_base as $sp_tag) {
2594 3819 $special_tags_base_html .= '<button type="button" class="btn btn-secondary btn-small" onclick="setSpecialTplTag(\'emailcont\', \'' . $sp_tag . '\');">' . $sp_tag . '</button>' . "\n";
@@ -2598,110 +3823,211 @@
2598 3823 * Use the rich text editor (visual editor) to build custom email messages.
2599 3824 *
2600 3825 * @since 1.15.0 (J) - 1.5.0 (WP)
2601 3826 */
2602 - $tarea_attr = array(
3827 + $tarea_attr = [
2603 3828 'id' => 'emailcont',
2604 3829 'rows' => '7',
2605 3830 'cols' => '170',
2606 3831 'style' => 'width: 99%; min-width: 99%; max-width: 99%; height: 120px; margin-bottom: 1px;',
2607 - );
2608 - $editor_opts = array(
2609 - 'modes' => array(
3832 + ];
3833 + $editor_opts = [
3834 + 'modes' => [
3835 + 'visual',
2610 3836 'text',
2611 - 'visual',
2612 - ),
2613 - );
3837 + ],
3838 + 'gen_ai' => [
3839 + 'booking' => $row,
3840 + 'customer' => $customer,
3841 + 'environment' => 'booking',
3842 + ],
3843 + ];
2614 3844 $editor_btns = $special_tags_base;
2615 - if (count($condtext_tags)) {
3845 + if ($condtext_tags) {
2616 3846 $editor_btns = array_merge($editor_btns, $condtext_tags);
2617 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
2618 3864 echo $vbo_app->renderVisualEditor('emailcont', '', $tarea_attr, $editor_opts, $editor_btns);
2619 3865 ?>
2620 - <div class="btn-group pull-left vbo-smstpl-bgroup vbo-custmail-bgroup vik-contentbuilder-textmode-sptags">
2621 - <?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>
2622 3869 </div>
2623 3870 </div>
2624 - <div class="vbo-calendar-cfield-entry">
2625 - <label for="emailattch"><?php echo JText::translate('VBSENDEMAILCUSTATTCH'); ?></label>
2626 - <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>
2627 3879 </div>
2628 - <div class="vbo-calendar-cfield-entry">
2629 - <label for="emailfrom"><?php echo JText::translate('VBSENDEMAILCUSTFROM'); ?></label>
2630 - <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>
2631 3888 </div>
3889 +
2632 3890 </div>
2633 - <input type="hidden" name="email" id="emailto" value="<?php echo $row['custmail']; ?>" />
2634 - <input type="hidden" name="goto" value="<?php echo urlencode('index.php?option=com_vikbooking&task=editorder&cid[]='.$row['id']); ?>" />
2635 - <input type="hidden" name="task" value="sendcustomemail" />
2636 - </form>
3891 + </div>
2637 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>
2638 3899
2639 - </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>
2640 3907
2641 - <div class="vbo-modal-overlay-content-footer">
2642 - <div class="vbo-modal-overlay-content-footer-right">
2643 - <div id="vbo-modal-footer-sms" style="display: none;">
2644 - <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>
2645 3954 </div>
2646 - <div id="vbo-modal-footer-email" style="display: none;">
2647 - <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>
2648 - </div>
2649 3955 </div>
2650 3956 </div>
2651 -
2652 3957 </div>
2653 3958 </div>
2654 3959
2655 3960 <script type="text/javascript">
2656 -var vbo_overlay_on = false;
2657 3961 var vbo_print_only = false;
2658 -if (jQuery.isFunction(jQuery.fn.tooltip)) {
3962 +if (typeof jQuery.fn.tooltip === 'function') {
2659 3963 jQuery(".hasTooltip").tooltip();
2660 3964 }
2661 -function vboToggleSendSMS() {
3965 +function vboDisplaySendSMS() {
2662 3966 var cur_phone = jQuery("#smstophone").val();
2663 3967 var phone_set = jQuery("#custphone").trigger('vboupdatephonenumber').val();
2664 3968 if (phone_set.length && phone_set != cur_phone) {
2665 3969 jQuery("#smstophone").val(phone_set);
2666 - jQuery("#smstophone-lbl").text(phone_set);
2667 3970 }
2668 3971
2669 - // toggle email/sms contents
2670 - jQuery("#vbo-overlay-email-cont, #vbo-modal-footer-email").hide();
2671 - jQuery("#vbo-overlay-sms-cont, #vbo-modal-footer-sms").show();
2672 - var use_modal_title = jQuery("#vbo-overlay-sms-cont").find('h4').first().html();
2673 - 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 + });
2674 3980
2675 - jQuery(".vbo-modal-overlay-block-sms-email").fadeToggle(400, function() {
2676 - if (jQuery(".vbo-modal-overlay-block-sms-email").is(":visible")) {
2677 - vbo_overlay_on = true;
2678 - } else {
2679 - vbo_overlay_on = false;
2680 - }
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 + },
2681 3994 });
3995 +
3996 + jQuery('.vbo-editorder-sms-send-wrap').appendTo(modal_body);
2682 3997 }
2683 -function vboToggleSendEmail() {
3998 +function vboDisplaySendEmail() {
2684 3999 var cur_email = jQuery("#emailto").val();
2685 4000 var email_set = jQuery("#custmail").val();
2686 4001 if (email_set.length && email_set != cur_email) {
2687 4002 jQuery("#emailto").val(email_set);
2688 - jQuery("#emailto-lbl").text(email_set);
2689 4003 }
2690 4004
2691 - // toggle email/sms contents
2692 - jQuery("#vbo-overlay-sms-cont, #vbo-modal-footer-sms").hide();
2693 - jQuery("#vbo-overlay-email-cont, #vbo-modal-footer-email").show();
2694 - var use_modal_title = jQuery("#vbo-overlay-email-cont").find('h4').first().html();
2695 - 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 + });
2696 4013
2697 - jQuery(".vbo-modal-overlay-block-sms-email").fadeToggle(400, function() {
2698 - if (jQuery(".vbo-modal-overlay-block-sms-email").is(":visible")) {
2699 - vbo_overlay_on = true;
2700 - } else {
2701 - vbo_overlay_on = false;
2702 - }
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 + },
2703 4027 });
4028 +
4029 + jQuery('.vbo-editorder-email-send-wrap').appendTo(modal_body);
2704 4030 }
2705 4031 function vboKeyupEmail(event) {
2706 4032 if (event.key && event.key == 'Enter') {
2707 4033 event.preventDefault();
@@ -2738,8 +4064,12 @@
2738 4064 }
2739 4065 // reload the page to display the new information about the booking just modified
2740 4066 location.reload();
2741 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 + });
2742 4072 // remove overbooking flag
2743 4073 jQuery('.vbo-label-overbooking').on('click', function() {
2744 4074 if (confirm(Joomla.JText._('VBO_CONF_RM_OVERBOOKING_FLAG'))) {
2745 4075 VBOCore.doAjax(
@@ -2835,24 +4165,8 @@
2835 4165 return;
2836 4166 }
2837 4167 });
2838 4168 // Search customer - End
2839 - jQuery(document).mouseup(function(e) {
2840 - if (!vbo_overlay_on) {
2841 - return false;
2842 - }
2843 - var vbo_overlay_cont = jQuery(".vbo-modal-overlay-content-sms-email");
2844 - if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0 && !jQuery(e.target).is('svg')) {
2845 - jQuery(".vbo-modal-overlay-block-sms-email").fadeOut();
2846 - vbo_overlay_on = false;
2847 - }
2848 - });
2849 - jQuery(document).keyup(function(e) {
2850 - if (e.keyCode == 27 && vbo_overlay_on) {
2851 - jQuery(".vbo-modal-overlay-block-sms-email").fadeOut();
2852 - vbo_overlay_on = false;
2853 - }
2854 - });
2855 4169 jQuery(".vbo-bookingdet-tab").click(function() {
2856 4170 var newtabrel = jQuery(this).attr('data-vbotab');
2857 4171 var oldtabrel = jQuery(".vbo-bookingdet-tab-active").attr('data-vbotab');
2858 4172 if (newtabrel == oldtabrel) {
@@ -2975,8 +4289,250 @@
2975 4289 jQuery('body').append(zoom_modal);
2976 4290 });
2977 4291 });
2978 4292
4293 + // emit the event to read all notifications in the current context
4294 + VBOCore.emitEvent('vbo-nc-read-notifications', {
4295 + criteria: {
4296 + group: '<?php echo !empty($row['idorderota']) && !empty($row['channel']) ? 'otas' : 'website'; ?>',
4297 + idorder: '<?php echo $row['id']; ?>',
4298 + }
4299 + });
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 +
2979 4535 });
2980 4536 var cur_emtpl = <?php echo json_encode($cur_emtpl); ?>;
2981 4537 function vboLoadEmailTpl(tplind) {
2982 4538 if (!(tplind.length > 0)) {
@@ -3003,9 +4559,9 @@
3003 4559 <?php
3004 4560 $pcustomemail = VikRequest::getInt('customemail', '', 'request');
3005 4561 if ($pcustomemail > 0) {
3006 4562 ?>
3007 - vboToggleSendEmail();
4563 + vboDisplaySendEmail();
3008 4564 <?php
3009 4565 }
3010 4566 if ($printreceipt) {
3011 4567 ?>