| @@ -755,12 +755,21 @@ | ||
| 755 | 755 | $q->where($dbo->qn('o.custmail') . ' = ' . $dbo->q($filters['email'])); |
| 756 | 756 | } |
| 757 | 757 | |
| 758 | 758 | if (($filters['phone'] ?? null)) { |
| 759 | - $q->where(sprintf('REPLACE(%s, \' \', \'\') LIKE REPLACE(%s, \' \', \'\')', | |
| 760 | - $dbo->qn('o.phone'), | |
| 761 | - $dbo->q('%' . $filters['phone']) | |
| 762 | - )); | |
| 759 | + $q->where( | |
| 760 | + sprintf( | |
| 761 | + '(%s OR %s)', | |
| 762 | + sprintf('REPLACE(%s, \' \', \'\') LIKE REPLACE(%s, \' \', \'\')', | |
| 763 | + $dbo->qn('o.phone'), | |
| 764 | + $dbo->q('%' . $filters['phone']) | |
| 765 | + ), | |
| 766 | + sprintf('REPLACE(%s, \' \', \'\') LIKE REPLACE(%s, \' \', \'\')', | |
| 767 | + $dbo->qn('o.phone_alias'), | |
| 768 | + $dbo->q('%' . $filters['phone_alias']) | |
| 769 | + ) | |
| 770 | + ) | |
| 771 | + ); | |
| 763 | 772 | } |
| 764 | 773 | |
| 765 | 774 | if (($filters['date_range']['type'] ?? null) && (($filters['date_range']['start'] ?? null) || ($filters['date_range']['end'] ?? null))) { |
| 766 | 775 | // search by date range |
| @@ -1064,8 +1073,90 @@ | ||
| 1064 | 1073 | return (bool) $dbo->getAffectedRows(); |
| 1065 | 1074 | } |
| 1066 | 1075 | |
| 1067 | 1076 | /** |
| 1077 | + * Updates the registration status for the provided booking. | |
| 1078 | + * | |
| 1079 | + * @param int $status Registration status to set. | |
| 1080 | + * @param ?int $bookingId Optional booking ID to update. | |
| 1081 | + * | |
| 1082 | + * @return bool | |
| 1083 | + * | |
| 1084 | + * @throws Exception | |
| 1085 | + * | |
| 1086 | + * @since 1.18.15 (J) - 1.8.15 (WP) | |
| 1087 | + */ | |
| 1088 | + public function updateRegistration(int $status, ?int $bookingId = null) | |
| 1089 | + { | |
| 1090 | + $dbo = JFactory::getDbo(); | |
| 1091 | + | |
| 1092 | + if (!$bookingId) { | |
| 1093 | + // get the internal booking record | |
| 1094 | + $booking = $this->getBooking(); | |
| 1095 | + $bookingId = $booking['id'] ?? null; | |
| 1096 | + } | |
| 1097 | + | |
| 1098 | + if (empty($bookingId)) { | |
| 1099 | + throw new Exception('Missing booking ID.', 400); | |
| 1100 | + } | |
| 1101 | + | |
| 1102 | + $knownStatuses = [-1, 0, 1, 2]; | |
| 1103 | + | |
| 1104 | + if (!in_array($status, $knownStatuses, true)) { | |
| 1105 | + throw new InvalidArgumentException('Invalid registration status.', 400); | |
| 1106 | + } | |
| 1107 | + | |
| 1108 | + // update booking record | |
| 1109 | + $dbo->setQuery( | |
| 1110 | + $dbo->getQuery(true) | |
| 1111 | + ->update($dbo->qn('#__vikbooking_orders')) | |
| 1112 | + ->set($dbo->qn('checked') . ' = ' . $status) | |
| 1113 | + ->where($dbo->qn('id') . ' = ' . $bookingId) | |
| 1114 | + ); | |
| 1115 | + $dbo->execute(); | |
| 1116 | + | |
| 1117 | + return (bool) $dbo->getAffectedRows(); | |
| 1118 | + } | |
| 1119 | + | |
| 1120 | + /** | |
| 1121 | + * Updates the booking-level phone number or alias phone number (masked phone). | |
| 1122 | + * | |
| 1123 | + * @param ?string $phone The phone number value to set, even null to unset. | |
| 1124 | + * @param bool $isAlias Whether to update the real or alias phone number. | |
| 1125 | + * @param ?int $bookingId Optional booking ID to update. | |
| 1126 | + * | |
| 1127 | + * @return bool | |
| 1128 | + * | |
| 1129 | + * @throws Exception | |
| 1130 | + * | |
| 1131 | + * @since 1.18.16 (J) - 1.8.16 (WP) | |
| 1132 | + */ | |
| 1133 | + public function updatePhoneNumber(?string $phone, bool $isAlias = false, ?int $bookingId = null) | |
| 1134 | + { | |
| 1135 | + $dbo = JFactory::getDbo(); | |
| 1136 | + | |
| 1137 | + if (!$bookingId) { | |
| 1138 | + // get the internal booking record | |
| 1139 | + $booking = $this->getBooking(); | |
| 1140 | + $bookingId = $booking['id'] ?? null; | |
| 1141 | + } | |
| 1142 | + | |
| 1143 | + if (empty($bookingId)) { | |
| 1144 | + throw new Exception('Missing booking ID.', 400); | |
| 1145 | + } | |
| 1146 | + | |
| 1147 | + $dbo->setQuery( | |
| 1148 | + $dbo->getQuery(true) | |
| 1149 | + ->update('#__vikbooking_orders') | |
| 1150 | + ->set($dbo->qn($isAlias ? 'phone_alias' : 'phone') . ' = ' . ($phone ? $dbo->q($phone) : 'NULL')) | |
| 1151 | + ->where($dbo->qn('id') . ' = ' . (int) $bookingId) | |
| 1152 | + ); | |
| 1153 | + $dbo->execute(); | |
| 1154 | + | |
| 1155 | + return (bool) $dbo->getAffectedRows(); | |
| 1156 | + } | |
| 1157 | + | |
| 1158 | + /** | |
| 1068 | 1159 | * Modifies the requested booking ID according to the provided options. |
| 1069 | 1160 | * This method does not support all rate plan options like for the creation of a |
| 1070 | 1161 | * new booking. This is a method for making quick updates concerning a room switch, |
| 1071 | 1162 | * a change of stay dates, new booking total amount, guests, add extra services etc.. |
| @@ -1805,8 +1896,11 @@ | ||
| 1805 | 1896 | $units_minus_oper[$or['idroom']] = 0; |
| 1806 | 1897 | } |
| 1807 | 1898 | } |
| 1808 | 1899 | |
| 1900 | + // list of room-units pool | |
| 1901 | + $roomUnits = []; | |
| 1902 | + | |
| 1809 | 1903 | // check availability for each room involved |
| 1810 | 1904 | foreach ($roomBooking as $ind => $or) { |
| 1811 | 1905 | // determine proper values for this room |
| 1812 | 1906 | $room_stay_checkin = $booking['checkin']; |
| @@ -1822,8 +1916,11 @@ | ||
| 1822 | 1916 | |
| 1823 | 1917 | // get room record |
| 1824 | 1918 | $room_record = VikBooking::getRoomInfo($or['idroom']); |
| 1825 | 1919 | |
| 1920 | + // set room (total) units | |
| 1921 | + $roomUnits[$or['idroom']] = (int) ($room_record['units'] ?? 1); | |
| 1922 | + | |
| 1826 | 1923 | // check if the room is available |
| 1827 | 1924 | if (!VikBooking::roomBookable($or['idroom'], (($room_record['units'] ?? 0) - $units_minus_oper[$or['idroom']]), $room_stay_checkin, $room_stay_checkout)) { |
| 1828 | 1925 | $allbook = false; |
| 1829 | 1926 | $notavail[] = $room_record['name'] ?? '?'; |
| @@ -1847,26 +1944,31 @@ | ||
| 1847 | 1944 | $room_stay_checkout = $room_stay_dates[$ind]['checkout_ts'] ?: $room_stay_dates[$ind]['checkout']; |
| 1848 | 1945 | $room_stay_realback = $turnover_secs + $room_stay_checkout; |
| 1849 | 1946 | } |
| 1850 | 1947 | |
| 1851 | - // build busy record | |
| 1852 | - $busy_record = new stdClass; | |
| 1853 | - $busy_record->idroom = (int) $or['idroom']; | |
| 1854 | - $busy_record->checkin = (int) $room_stay_checkin; | |
| 1855 | - $busy_record->checkout = (int) $room_stay_checkout; | |
| 1856 | - $busy_record->realback = (int) $room_stay_realback; | |
| 1948 | + // determine the room units to occupy (to support closure re-confirmations) | |
| 1949 | + $bookUnitsCount = (int) (intval($booking['closure'] ?? 0) ? ($roomUnits[$or['idroom']] ?? 1) : 1); | |
| 1857 | 1950 | |
| 1858 | - // store busy record and obtain the newly created ID | |
| 1859 | - $dbo->insertObject('#__vikbooking_busy', $busy_record, 'id'); | |
| 1860 | - $lid = $busy_record->id ?? 0; | |
| 1951 | + for ($bi = 1; $bi <= $bookUnitsCount; $bi++) { | |
| 1952 | + // build busy record | |
| 1953 | + $busy_record = new stdClass; | |
| 1954 | + $busy_record->idroom = (int) $or['idroom']; | |
| 1955 | + $busy_record->checkin = (int) $room_stay_checkin; | |
| 1956 | + $busy_record->checkout = (int) $room_stay_checkout; | |
| 1957 | + $busy_record->realback = (int) $room_stay_realback; | |
| 1861 | 1958 | |
| 1862 | - // build busy relation record | |
| 1863 | - $obusy_record = new stdClass; | |
| 1864 | - $obusy_record->idorder = (int) $booking['id']; | |
| 1865 | - $obusy_record->idbusy = (int) $lid; | |
| 1959 | + // store busy record and obtain the newly created ID | |
| 1960 | + $dbo->insertObject('#__vikbooking_busy', $busy_record, 'id'); | |
| 1961 | + $lid = $busy_record->id ?? 0; | |
| 1866 | 1962 | |
| 1867 | - // store busy relation record | |
| 1868 | - $dbo->insertObject('#__vikbooking_ordersbusy', $obusy_record, 'id'); | |
| 1963 | + // build busy relation record | |
| 1964 | + $obusy_record = new stdClass; | |
| 1965 | + $obusy_record->idorder = (int) $booking['id']; | |
| 1966 | + $obusy_record->idbusy = (int) $lid; | |
| 1967 | + | |
| 1968 | + // store busy relation record | |
| 1969 | + $dbo->insertObject('#__vikbooking_ordersbusy', $obusy_record, 'id'); | |
| 1970 | + } | |
| 1869 | 1971 | } |
| 1870 | 1972 | |
| 1871 | 1973 | // delete temporarily locked records, if any |
| 1872 | 1974 | $dbo->setQuery( |
| @@ -2150,8 +2252,36 @@ | ||
| 2150 | 2252 | foreach ($booking_rooms as $booking_room) { |
| 2151 | 2253 | $rooms_units_counter[$booking_room['idroom']] = ($rooms_counter[$booking_room['idroom']] ?? -1) + 1; |
| 2152 | 2254 | } |
| 2153 | 2255 | |
| 2256 | + // check if the booking modification involves other rooms | |
| 2257 | + $roomsPool = $this->getRoomsPool(); | |
| 2258 | + $oldRoomIds = array_map('intval', array_column($booking_rooms, 'idroom')); | |
| 2259 | + $newRoomIds = array_values(array_unique(array_map('intval', array_column($roomsPool, 'id')))); | |
| 2260 | + $diffIds = array_diff($newRoomIds, $oldRoomIds); | |
| 2261 | + foreach ($diffIds as $newId) { | |
| 2262 | + $unitsRequested = 0; | |
| 2263 | + $totUnits = 0; | |
| 2264 | + foreach ($roomsPool as $roomData) { | |
| 2265 | + if ($newId == $roomData['id']) { | |
| 2266 | + $unitsRequested++; | |
| 2267 | + $totUnits = (int) ($roomData['units'] ?? 1); | |
| 2268 | + } | |
| 2269 | + } | |
| 2270 | + if (!$unitsRequested) { | |
| 2271 | + continue; | |
| 2272 | + } | |
| 2273 | + | |
| 2274 | + // update units counter map for multiple units requested | |
| 2275 | + $rooms_units_counter[$newId] = $unitsRequested - 1; | |
| 2276 | + | |
| 2277 | + // push additional room for availability validation | |
| 2278 | + $booking_rooms[] = [ | |
| 2279 | + 'idroom' => $newId, | |
| 2280 | + 'tot_units' => $totUnits, | |
| 2281 | + ]; | |
| 2282 | + } | |
| 2283 | + | |
| 2154 | 2284 | // check the availability for each room booked |
| 2155 | 2285 | $rooms_parsed = []; |
| 2156 | 2286 | foreach ($booking_rooms as $booking_room) { |
| 2157 | 2287 | if (in_array($booking_room['idroom'], $rooms_parsed)) { |
| @@ -2879,8 +3009,11 @@ | ||
| 2879 | 3009 | $email = !empty($inj_customer['email']) ? $inj_customer['email'] : ''; |
| 2880 | 3010 | $country = !empty($inj_customer['country']) ? $inj_customer['country'] : ''; |
| 2881 | 3011 | $phone = !empty($inj_customer['phone']) ? $inj_customer['phone'] : ''; |
| 2882 | 3012 | $gender = !empty($inj_customer['gender']) ? $inj_customer['gender'] : ''; |
| 3013 | + $address = !empty($inj_customer['address']) ? $inj_customer['address'] : ''; | |
| 3014 | + $city = !empty($inj_customer['city']) ? $inj_customer['city'] : ''; | |
| 3015 | + $zip = !empty($inj_customer['zip']) ? $inj_customer['zip'] : ''; | |
| 2883 | 3016 | |
| 2884 | 3017 | // custom fields |
| 2885 | 3018 | $q = "SELECT * FROM `#__vikbooking_custfields` ORDER BY `ordering` ASC;"; |
| 2886 | 3019 | $dbo->setQuery($q); |
| @@ -2916,8 +3049,23 @@ | ||
| 2916 | 3049 | // inject the customer gender value |
| 2917 | 3050 | $customer_extrainfo['gender'] = strtoupper($gender); |
| 2918 | 3051 | } |
| 2919 | 3052 | |
| 3053 | + if (!empty($address)) { | |
| 3054 | + // inject extra info | |
| 3055 | + $customer_extrainfo['address'] = strlen((string) $address) > 200 ? substr($address, 0, 200) : $address; | |
| 3056 | + } | |
| 3057 | + | |
| 3058 | + if (!empty($city)) { | |
| 3059 | + // inject extra info | |
| 3060 | + $customer_extrainfo['city'] = strlen((string) $city) > 50 ? substr($city, 0, 50) : $city; | |
| 3061 | + } | |
| 3062 | + | |
| 3063 | + if (!empty($zip)) { | |
| 3064 | + // inject extra info | |
| 3065 | + $customer_extrainfo['zip'] = strlen((string) $zip) > 10 ? substr($zip, 0, 10) : $zip; | |
| 3066 | + } | |
| 3067 | + | |
| 2920 | 3068 | $cpin = VikBooking::getCPinInstance(); |
| 2921 | 3069 | $cpin->is_admin = true; |
| 2922 | 3070 | $cpin->setCustomerExtraInfo($customer_extrainfo); |
| 2923 | 3071 | $cpin->saveCustomerDetails($first_name, $last_name, $email, $phone, $country, $customer_cfields); |
| @@ -3066,9 +3214,9 @@ | ||
| 3066 | 3214 | // total and taxes should be multiplied by the number of rooms booked when using a website rate plan |
| 3067 | 3215 | if ($set_closed) { |
| 3068 | 3216 | $roomTotalValue *= $roomDetails['units']; |
| 3069 | 3217 | $roomTotalTax *= $roomDetails['units']; |
| 3070 | - } elseif ($num_rooms > 1 && $num_rooms <= $roomDetails['units']) { | |
| 3218 | + } elseif (!$this->isMultiRoom() && $num_rooms > 1 && $num_rooms <= $roomDetails['units']) { | |
| 3071 | 3219 | $roomTotalValue *= $num_rooms; |
| 3072 | 3220 | $roomTotalTax *= $num_rooms; |
| 3073 | 3221 | } |
| 3074 | 3222 | } |
| @@ -3267,8 +3415,12 @@ | ||
| 3267 | 3415 | $customer_data = ($inj_customer['data'] ?? null) ?: ''; |
| 3268 | 3416 | $customer_email = ($inj_customer['email'] ?? null) ?: ''; |
| 3269 | 3417 | $country_code = ($inj_customer['country'] ?? null) ?: ''; |
| 3270 | 3418 | $phone_number = ($inj_customer['phone'] ?? null) ?: ''; |
| 3419 | + $phone_alias = ($inj_customer['phone_alias'] ?? null) ?: ''; | |
| 3420 | + $address = ($inj_customer['address'] ?? null) ?: ''; | |
| 3421 | + $city = ($inj_customer['city'] ?? null) ?: ''; | |
| 3422 | + $zip = ($inj_customer['zip'] ?? null) ?: ''; | |
| 3271 | 3423 | |
| 3272 | 3424 | if ($set_closed) { |
| 3273 | 3425 | // get the possibly custom "customer notes" |
| 3274 | 3426 | $customer_notes = $customer_data; |
| @@ -3290,14 +3442,25 @@ | ||
| 3290 | 3442 | } |
| 3291 | 3443 | if ($customer_email) { |
| 3292 | 3444 | $customer_data .= "eMail: {$customer_email}\n"; |
| 3293 | 3445 | } |
| 3446 | + if ($phone_number) { | |
| 3447 | + $customer_data .= "Phone: {$phone_number}\n"; | |
| 3448 | + } elseif ($phone_alias) { | |
| 3449 | + $customer_data .= "Phone-alias: {$phone_alias}\n"; | |
| 3450 | + } | |
| 3294 | 3451 | if ($country_code) { |
| 3295 | 3452 | $customer_data .= "Country: {$country_code}\n"; |
| 3296 | 3453 | } |
| 3297 | - if ($phone_number) { | |
| 3298 | - $customer_data .= "Phone: {$phone_number}\n"; | |
| 3454 | + if ($address) { | |
| 3455 | + $customer_data .= "Address: {$address}\n"; | |
| 3299 | 3456 | } |
| 3457 | + if ($city) { | |
| 3458 | + $customer_data .= "City: {$city}\n"; | |
| 3459 | + } | |
| 3460 | + if ($zip) { | |
| 3461 | + $customer_data .= "Zip: {$zip}\n"; | |
| 3462 | + } | |
| 3300 | 3463 | $customer_data = rtrim($customer_data, "\n"); |
| 3301 | 3464 | } |
| 3302 | 3465 | |
| 3303 | 3466 | // ensure the country code is in the right format |
| @@ -3364,8 +3527,11 @@ | ||
| 3364 | 3527 | // attempt to get the default payment method |
| 3365 | 3528 | if (!$paymentmeth && ($auto_paymeth || $status == 'standby')) { |
| 3366 | 3529 | // get the default payment method, if any |
| 3367 | 3530 | $paymentmeth = $this->getDefaultPaymentMethod($auto_paymeth); |
| 3531 | + } elseif ($paymentmeth && is_int($paymentmeth)) { | |
| 3532 | + // convert the payment integer ID into a string with the actual record name | |
| 3533 | + $paymentmeth = $this->getPaymentMethodValue($paymentmeth); | |
| 3368 | 3534 | } |
| 3369 | 3535 | |
| 3370 | 3536 | if ($bookingId) { |
| 3371 | 3537 | // when updating, delete all room booking records involved |
| @@ -3403,8 +3569,9 @@ | ||
| 3403 | 3569 | $booking->tot_city_taxes = $set_city_tax ?: ($this->get('tot_city_taxes') ? ((float) $this->get('tot_city_taxes')) : null); |
| 3404 | 3570 | $booking->tot_fees = $set_fees ?: ($this->get('tot_fees') ? ((float) $this->get('tot_fees')) : null); |
| 3405 | 3571 | $booking->tot_damage_dep = $this->get('tot_damage_dep') ? ((float) $this->get('tot_damage_dep')) : null; |
| 3406 | 3572 | $booking->phone = $phone_number; |
| 3573 | + $booking->phone_alias = $phone_alias ?: null; | |
| 3407 | 3574 | $booking->cmms = $this->get('cmms') ? ((float) $this->get('cmms')) : null; |
| 3408 | 3575 | $booking->closure = ($status == 'standby' ? 0 : ($set_closed || $units_closed ? 1 : 0)); |
| 3409 | 3576 | $booking->payable = $this->get('payable') ? ((float) $this->get('payable')) : null; |
| 3410 | 3577 | $booking->refund = $this->get('refund') ? ((float) $this->get('refund')) : null; |
| @@ -3511,10 +3678,10 @@ | ||
| 3511 | 3678 | } |
| 3512 | 3679 | } |
| 3513 | 3680 | |
| 3514 | 3681 | // store room booking records |
| 3682 | + $room_indexes_usemap = []; | |
| 3515 | 3683 | foreach ($rooms_pool as $rind => $nowroom) { |
| 3516 | - $room_indexes_usemap = []; | |
| 3517 | 3684 | for ($r = 1; $r <= $or_forend; $r++) { |
| 3518 | 3685 | // determine room-level stay dates |
| 3519 | 3686 | $room_stay_checkin = $nowroom['checkin_ts'] ?? $roomsData[$rind]['checkin'] ?? $checkin_ts; |
| 3520 | 3687 | $room_stay_checkout = $nowroom['checkout_ts'] ?? $roomsData[$rind]['checkout'] ?? $checkout_ts; |
| @@ -3569,10 +3736,10 @@ | ||
| 3569 | 3736 | } |
| 3570 | 3737 | } |
| 3571 | 3738 | |
| 3572 | 3739 | // room guests |
| 3573 | - $room_adults = (int) (isset($adults_map[$r]) && empty($split_stay_data) ? $adults_map[$r] : ($roomsData[$rind]['adults'] ?? $adults)); | |
| 3574 | - $room_children = (int) (isset($children_map[$r]) && empty($split_stay_data) ? $children_map[$r] : ($roomsData[$rind]['children'] ?? $children)); | |
| 3740 | + $room_adults = (int) ($roomsData[$rind]['adults'] ?? (isset($adults_map[$r]) && empty($split_stay_data) ? $adults_map[$r] : $adults)); | |
| 3741 | + $room_children = (int) ($roomsData[$rind]['children'] ?? (isset($children_map[$r]) && empty($split_stay_data) ? $children_map[$r] : $children)); | |
| 3575 | 3742 | $use_children_age = (array) ($roomsData[$rind]['children_age'] ?? $children_age); |
| 3576 | 3743 | |
| 3577 | 3744 | // number of pets per room |
| 3578 | 3745 | $room_pets = (int) ($roomsData[$rind]['pets'] ?? 0); |
| @@ -3852,6 +4019,27 @@ | ||
| 3852 | 4019 | } |
| 3853 | 4020 | |
| 3854 | 4021 | // nothing was found |
| 3855 | 4022 | return ''; |
| 4023 | + } | |
| 4024 | + | |
| 4025 | + /** | |
| 4026 | + * Given a payment method record ID, returns the string | |
| 4027 | + * in the format for saving it along the reservation. | |
| 4028 | + * | |
| 4029 | + * @param int $id The record ID. | |
| 4030 | + * | |
| 4031 | + * @return ?string ID=Name calculated value, or null. | |
| 4032 | + * | |
| 4033 | + * @since 1.18.15 (J) - 1.8.15 (WP) | |
| 4034 | + */ | |
| 4035 | + protected function getPaymentMethodValue(int $id) | |
| 4036 | + { | |
| 4037 | + $record = VBOMvcModel::getInstance('payment')->getItem($id); | |
| 4038 | + | |
| 4039 | + if ($record) { | |
| 4040 | + return sprintf('%s=%s', $record->id, $record->name); | |
| 4041 | + } | |
| 4042 | + | |
| 4043 | + return null; | |
| 3856 | 4044 | } |
| 3857 | 4045 | } |