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/helpers/src/booking/registry.php +371 -1 1.8.7 → 1.8.16 View file →
@@ -440,8 +440,127 @@
440 440 return $this->countTotalAdults($roomLevel) + $this->countTotalChildren($roomLevel);
441 441 }
442 442
443 443 /**
444 + * Returns the booking or customer country code, if any.
445 + *
446 + * @return ?string
447 + *
448 + * @since 1.18.8 (J) - 1.8.8 (WP)
449 + */
450 + public function getCountry()
451 + {
452 + // give higher priority to booking-level country
453 + $countryCode = $this->getProperty('country');
454 +
455 + if (!$countryCode) {
456 + // fetch booking customer details
457 + $customer = $this->getCustomer();
458 +
459 + // check customer-level country
460 + $countryCode = ($customer['country'] ?? '') ?: $countryCode;
461 + }
462 +
463 + return $countryCode ?: null;
464 + }
465 +
466 + /**
467 + * Returns the booking or customer phone number, if any.
468 + * Note that the + prefix may not be included.
469 + *
470 + * @param ?string $forcedPhone Optional phone number to sanitize.
471 + *
472 + * @return ?string
473 + *
474 + * @since 1.18.8 (J) - 1.8.8 (WP)
475 + * @since 1.18.16 (J) - 1.8.16 (WP) added argument $forcedPhone.
476 + */
477 + public function getPhoneNumber(?string $forcedPhone = null)
478 + {
479 + // give higher priority to passed or booking-level phone number
480 + $phoneNumber = $forcedPhone ?: $this->getProperty('phone');
481 +
482 + if (!$phoneNumber && $forcedPhone !== null) {
483 + // fetch booking customer details
484 + $customer = $this->getCustomer();
485 +
486 + // check customer-level phone number
487 + $phoneNumber = ($customer['phone'] ?? '') ?: $phoneNumber;
488 + }
489 +
490 + // trim phone number
491 + $phoneNumber = (string) $phoneNumber;
492 +
493 + // sanitize number by keeping the + char only if at the beginning of the string
494 + $phoneNumber = preg_replace('/(?!^)\+|[^0-9+]/', '', $phoneNumber);
495 +
496 + if (substr($phoneNumber, 0, 2) === '00') {
497 + // safely convert 00 to +
498 + $phoneNumber = substr_replace($phoneNumber, '+', 0, 2);
499 + }
500 +
501 + return $phoneNumber ?: null;
502 + }
503 +
504 + /**
505 + * Returns the booking alias phone number (masked phone), if any.
506 + *
507 + * @return ?string
508 + *
509 + * @since 1.18.16 (J) - 1.8.16 (WP)
510 + */
511 + public function getAliasPhoneNumber()
512 + {
513 + $phoneAlias = (string) $this->getProperty('phone_alias');
514 +
515 + return $phoneAlias ? $this->getPhoneNumber($phoneAlias) : null;
516 + }
517 +
518 + /**
519 + * Returns the booking or customer email address, if any.
520 + *
521 + * @return ?string
522 + *
523 + * @since 1.18.8 (J) - 1.8.8 (WP)
524 + */
525 + public function getEmailAddress()
526 + {
527 + // give higher priority to booking-level email address
528 + $emailAddr = $this->getProperty('custmail');
529 +
530 + if (!$emailAddr) {
531 + // fetch booking customer details
532 + $customer = $this->getCustomer();
533 +
534 + // check customer-level email address
535 + $emailAddr = ($customer['email'] ?? '') ?: $emailAddr;
536 + }
537 +
538 + // trim email address
539 + $emailAddr = (string) $emailAddr;
540 +
541 + return $emailAddr ?: null;
542 + }
543 +
544 + /**
545 + * Returns the booking OTA type-data information, if any.
546 + *
547 + * @return array Empty or associative array.
548 + *
549 + * @since 1.18.8 (J) - 1.8.8 (WP)
550 + */
551 + public function getOTATypeData()
552 + {
553 + $typeData = $this->getProperty('ota_type_data');
554 +
555 + if ($typeData && is_scalar($typeData)) {
556 + $typeData = (array) json_encode($typeData, true);
557 + }
558 +
559 + return $typeData ?: [];
560 + }
561 +
562 + /**
444 563 * Returns the previous booking data.
445 564 *
446 565 * @return array
447 566 */
@@ -450,8 +569,24 @@
450 569 return $this->previousBooking;
451 570 }
452 571
453 572 /**
573 + * Sets the previous booking data.
574 + *
575 + * @param array $previous Previous booking assoc data.
576 + *
577 + * @return self
578 + *
579 + * @since 1.18.11 (J) - 1.8.11 (WP)
580 + */
581 + public function setPrevious(array $previous)
582 + {
583 + $this->previousBooking = $previous;
584 +
585 + return $this;
586 + }
587 +
588 + /**
454 589 * Gets the current room index.
455 590 *
456 591 * @return int
457 592 */
@@ -502,9 +637,9 @@
502 637 * Gets the room ID from the current room index set.
503 638 *
504 639 * @return int
505 640 *
506 - * @since 1.18.7 (J) - 1.8.7 (WP) added argument $roomLevel.
641 + * @since 1.18.7 (J) - 1.8.7 (WP)
507 642 */
508 643 public function getCurrentRoomID()
509 644 {
510 645 foreach ($this->getRooms() as $index => $bookingRoom) {
@@ -944,6 +1079,241 @@
944 1079 $this->registry['_precheckin'] = $pre_checkin;
945 1080 }
946 1081
947 1082 return (bool) $pre_checkin;
1083 + }
1084 +
1085 + /**
1086 + * Tells if the booking comes from an OTA.
1087 + *
1088 + * @return bool
1089 + *
1090 + * @since 1.18.8 (J) - 1.8.8 (WP)
1091 + */
1092 + public function isFromOTA()
1093 + {
1094 + $otaId = $this->getProperty('idorderota', null);
1095 + $channel = $this->getProperty('channel', null);
1096 +
1097 + return !empty($otaId) && !empty($channel);
1098 + }
1099 +
1100 + /**
1101 + * Tells if the booking is fully paid as of now.
1102 + * Note that OTA payouts may still need to be received,
1103 + * or an outstanding payment may still be due in case of upselling events.
1104 + * Do NOT rely on this method alone to determine if payments are needed.
1105 + *
1106 + * @return bool Even if true, there could be pending payments.
1107 + *
1108 + * @see getOutstandingBalance()
1109 + *
1110 + * @since 1.18.8 (J) - 1.8.8 (WP)
1111 + */
1112 + public function isFullyPaid()
1113 + {
1114 + // get current totals
1115 + $bookingTotal = (float) $this->getProperty('total', 0);
1116 + $otaCompensation = (float) $this->getProperty('cmms', 0);
1117 + $amountPaid = (float) $this->getProperty('totpaid', 0);
1118 +
1119 + if ($this->isFromOTA()) {
1120 + return ($bookingTotal - $amountPaid - $otaCompensation) < 1;
1121 + }
1122 +
1123 + return $amountPaid >= $bookingTotal;
1124 + }
1125 +
1126 + /**
1127 + * Calculates the outstanding balance as of now, if any.
1128 + *
1129 + * @return ?float
1130 + *
1131 + * @since 1.18.8 (J) - 1.8.8 (WP)
1132 + */
1133 + public function getOutstandingBalance()
1134 + {
1135 + // get current totals
1136 + $bookingTotal = (float) $this->getProperty('total', 0);
1137 + $otaCompensation = (float) $this->getProperty('cmms', 0);
1138 + $amountPaid = (float) $this->getProperty('totpaid', 0);
1139 + $amountPayable = (float) $this->getProperty('payable', 0);
1140 + $paymentCount = abs((int) $this->getProperty('paymcount', 0));
1141 + $bookingDamDep = (float) $this->getProperty('tot_damage_dep', 0);
1142 +
1143 + // check if the amounts look as fully paid
1144 + $looksFullyPaid = $this->isFullyPaid();
1145 +
1146 + if ($looksFullyPaid && !$amountPayable) {
1147 + // no upselling events involved, nothing is due
1148 + return null;
1149 + }
1150 +
1151 + // obtain damage deposit details
1152 + $damage_deposit_payment = VBORoomHelper::getInstance()->getDamageDepositSplitPayment($this->getData(), $this->getRooms());
1153 +
1154 + // obtain previous damage deposit payments, if any
1155 + $prev_dd_payments = [];
1156 + if ($damage_deposit_payment['damagedep_gross'] ?? 0) {
1157 + $prev_dd_payments = VikBooking::getBookingHistoryInstance($this->getID())
1158 + ->getEventsWithData('PN', function($data) {
1159 + return (is_object($data) && !empty($data->damage_deposit));
1160 + });
1161 + if (!$prev_dd_payments) {
1162 + // check also the first payment event in case of OTA bookings
1163 + $prev_dd_payments = VikBooking::getBookingHistoryInstance($this->getID())
1164 + ->getEventsWithData('P0', function($data) {
1165 + return (is_object($data) && !empty($data->damage_deposit));
1166 + });
1167 + }
1168 + }
1169 +
1170 + // outstanding balance
1171 + $outstanding = $bookingTotal - $amountPaid;
1172 + if ($prev_dd_payments && $bookingDamDep) {
1173 + // deduct previously paid damage deposit
1174 + $outstanding -= $bookingDamDep;
1175 + }
1176 +
1177 + // tell if the booking looks payable
1178 + $isPayable = (($amountPaid > 0 && ($amountPaid + ($damage_deposit_payment['damagedep_gross'] ?? 0)) < $bookingTotal && $paymentCount) || $amountPayable > 0);
1179 +
1180 + // additional payment flags
1181 + $otaWillPay = false;
1182 + $payableLater = false;
1183 +
1184 + // determine booking pay-ability
1185 + if ($isPayable && $this->isConfirmed() && $this->isFromOTA() && $amountPayable > 0 && !$prev_dd_payments) {
1186 + if (($damage_deposit_payment['damagedep_gross'] ?? 0) == $amountPayable && ($damage_deposit_payment['payment_window']['pay_id'] ?? 0)) {
1187 + // upselling event must have added the damage deposit to an OTA booking, and this is the only outstanding amount that will be paid separately
1188 + $isPayable = false;
1189 + // check if the damage deposit is payable later
1190 + if (!($damage_deposit_payment['payment_window']['payable'] ?? 0) && ($damage_deposit_payment['payment_window']['payment_from_dt'] ?? null)) {
1191 + // will be payable on a date in the future
1192 + $payableLater = true;
1193 + }
1194 + }
1195 + }
1196 + if ($isPayable && $this->isFromOTA() && $otaCompensation && $looksFullyPaid) {
1197 + // the difference of the amount paid is equal to the OTA commissions amount
1198 + $isPayable = false;
1199 + if ($amountPayable > 0 && $bookingTotal > $amountPaid && round(($bookingTotal - $amountPaid), 0) == round($amountPayable, 0)) {
1200 + // there must have been an upselling event or a payment request with an amount equal to the OTA commissions
1201 + $isPayable = true;
1202 + }
1203 + }
1204 + if ($isPayable && $this->isFromOTA() && !$amountPayable) {
1205 + // access OTA payout events
1206 + $prev_ota_payments = VikBooking::getBookingHistoryInstance($this->getID())
1207 + ->getEventsWithData('PO', null, false);
1208 + if (!$prev_ota_payments) {
1209 + // the OTA will pay the remaining balance
1210 + $isPayable = false;
1211 + $otaWillPay = true;
1212 + }
1213 + }
1214 +
1215 + if ($outstanding && !$isPayable && $payableLater) {
1216 + // deduct the damage deposit amount that will be payable in a future date
1217 + $outstanding -= $bookingDamDep;
1218 + }
1219 +
1220 + if ($outstanding && !$isPayable && $otaWillPay) {
1221 + // deduct OTA commissions that has not been paid yet
1222 + $outstanding -= $otaCompensation;
1223 + }
1224 +
1225 + return $outstanding > 0 ? $outstanding : null;
1226 + }
1227 +
1228 + /**
1229 + * Returns a unique list of booked listing names.
1230 + *
1231 + * @return array Linear array of strings, if any.
1232 + *
1233 + * @since 1.18.8 (J) - 1.8.8 (WP)
1234 + */
1235 + public function getListingNames()
1236 + {
1237 + $listingIds = $this->getBookedListingIds();
1238 + $roomNames = VikBooking::getAvailabilityInstance(true)->loadRooms($listingIds);
1239 +
1240 + return array_map(function($id) use ($roomNames) {
1241 + return $roomNames[$id]['name'] ?? $id;
1242 + }, $listingIds);
1243 + }
1244 +
1245 + /**
1246 + * Returns a unique list of booked listing addresses.
1247 + *
1248 + * @return array Linear array of strings, if any.
1249 + *
1250 + * @since 1.18.8 (J) - 1.8.8 (WP)
1251 + */
1252 + public function getListingAddresses()
1253 + {
1254 + if (!class_exists('VCMOtaListing')) {
1255 + // prevent errors when the CM is not installed
1256 + return [];
1257 + }
1258 +
1259 + $addresses = [];
1260 +
1261 + foreach ($this->getBookedListingIds() as $listingId) {
1262 + $addresses[] = VCMOtaListing::getInstance()->getLocation($listingId)['address'] ?? null;
1263 + }
1264 +
1265 + return array_values(array_unique($addresses));
1266 + }
1267 +
1268 + /**
1269 + * Returns the booking routed URI, either in its regular or shorten forms.
1270 + *
1271 + * @return string
1272 + *
1273 + * @since 1.18.13 (J) - 1.8.13 (WP)
1274 + */
1275 + public function getBookingLink(bool $shorten = false)
1276 + {
1277 + // construct booking link
1278 + $useSid = !$this->getProperty('sid') && $this->getProperty('idorderota') ? $this->getProperty('idorderota') : $this->getProperty('sid');
1279 + $bestItemid = VikBooking::findProperItemIdType(['booking'], ($this->getProperty('lang') ?: null));
1280 + $langSuffix = $bestItemid && $this->getProperty('lang') ? '&lang=' . $this->getProperty('lang') : '';
1281 +
1282 + $bookingLink = VikBooking::externalroute("index.php?option=com_vikbooking&view=booking&sid=" . $useSid . "&ts=" . $this->getProperty('ts') . $langSuffix, false, ($bestItemid ?: null));
1283 +
1284 + if (!$shorten) {
1285 + return $bookingLink;
1286 + }
1287 +
1288 + // access the model for shortening URLs
1289 + $model = VBOModelShortenurl::getInstance($onlyRouted = false)->setBooking($this->getData());
1290 +
1291 + return $model->getShortUrl($bookingLink);
1292 + }
1293 +
1294 + /**
1295 + * Returns the booking pre-check-in routed URI, either in its regular or shorten forms.
1296 + *
1297 + * @return string
1298 + *
1299 + * @since 1.18.13 (J) - 1.8.13 (WP)
1300 + */
1301 + public function getPrecheckinLink(bool $shorten = false)
1302 + {
1303 + // obtain booking link
1304 + $useSid = !$this->getProperty('sid') && $this->getProperty('idorderota') ? $this->getProperty('idorderota') : $this->getProperty('sid');
1305 + $bestItemid = VikBooking::findProperItemIdType(['booking'], ($this->getProperty('lang') ?: null));
1306 + $langSuffix = $bestItemid && $this->getProperty('lang') ? '&lang=' . $this->getProperty('lang') : '';
1307 +
1308 + $precheckinLink = VikBooking::externalroute("index.php?option=com_vikbooking&view=precheckin&sid=" . $useSid . "&ts=" . $this->getProperty('ts') . $langSuffix, false, ($bestItemid ?: null));
1309 +
1310 + if (!$shorten) {
1311 + return $precheckinLink;
1312 + }
1313 +
1314 + // access the model for shortening URLs
1315 + $model = VBOModelShortenurl::getInstance($onlyRouted = false)->setBooking($this->getData());
1316 +
1317 + return $model->getShortUrl($precheckinLink);
948 1318 }
949 1319 }