| 1 |
<?php |
| 2 |
/** |
| 3 |
* @package VikBooking |
| 4 |
* @subpackage core |
| 5 |
* @author E4J s.r.l. |
| 6 |
* @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved. |
| 7 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL |
| 8 |
* @link https://vikwp.com |
| 9 |
*/ |
| 10 |
|
| 11 |
// No direct access |
| 12 |
defined('ABSPATH') or die('No script kiddies please!'); |
| 13 |
|
| 14 |
/** |
| 15 |
* VikBooking reservation model. |
| 16 |
* |
| 17 |
* @since 1.16.0 (J) - 1.6.0 (WP) |
| 18 |
*/ |
| 19 |
class VBOModelReservation extends JObject |
| 20 |
{ |
| 21 |
/** |
| 22 |
* The singleton instance of the class. |
| 23 |
* |
| 24 |
* @var VBOModelReservation |
| 25 |
*/ |
| 26 |
private static $instance = null; |
| 27 |
|
| 28 |
/** |
| 29 |
* Proxy for immediately getting the object and bind data. |
| 30 |
* |
| 31 |
* @param array|object $data optional data to bind. |
| 32 |
* @param boolean $anew true for forcing a new instance. |
| 33 |
* |
| 34 |
* @return self |
| 35 |
*/ |
| 36 |
public static function getInstance($data = [], $anew = false) |
| 37 |
{ |
| 38 |
if (is_null(static::$instance) || $anew) { |
| 39 |
static::$instance = new static($data); |
| 40 |
} |
| 41 |
|
| 42 |
return static::$instance; |
| 43 |
} |
| 44 |
|
| 45 |
/** |
| 46 |
* Sets the customer information. |
| 47 |
* |
| 48 |
* @param array $customer the customer array. |
| 49 |
* |
| 50 |
* @return self |
| 51 |
*/ |
| 52 |
public function setCustomer(array $customer = []) |
| 53 |
{ |
| 54 |
$this->set('_customer', $customer); |
| 55 |
|
| 56 |
return $this; |
| 57 |
} |
| 58 |
|
| 59 |
/** |
| 60 |
* Returns the customer information. |
| 61 |
* |
| 62 |
* @return array |
| 63 |
*/ |
| 64 |
public function getCustomer() |
| 65 |
{ |
| 66 |
return (array)$this->get('_customer', []); |
| 67 |
} |
| 68 |
|
| 69 |
/** |
| 70 |
* Sets the room information. |
| 71 |
* |
| 72 |
* @param array $room the room array. |
| 73 |
* |
| 74 |
* @return self |
| 75 |
*/ |
| 76 |
public function setRoom(array $room = []) |
| 77 |
{ |
| 78 |
$this->set('_room', $room); |
| 79 |
|
| 80 |
return $this; |
| 81 |
} |
| 82 |
|
| 83 |
/** |
| 84 |
* Returns the room information. |
| 85 |
* |
| 86 |
* @return array |
| 87 |
*/ |
| 88 |
public function getRoom() |
| 89 |
{ |
| 90 |
return (array)$this->get('_room', []); |
| 91 |
} |
| 92 |
|
| 93 |
/** |
| 94 |
* Sets the new booking ID created. |
| 95 |
* |
| 96 |
* @param int $bid the newly added record ID. |
| 97 |
* |
| 98 |
* @return self |
| 99 |
*/ |
| 100 |
protected function setNewBookingID($bid = 0) |
| 101 |
{ |
| 102 |
$this->set('_newBookingID', $bid); |
| 103 |
|
| 104 |
return $this; |
| 105 |
} |
| 106 |
|
| 107 |
/** |
| 108 |
* Returns the new booking ID created, or 0. |
| 109 |
* |
| 110 |
* @return int |
| 111 |
*/ |
| 112 |
public function getNewBookingID() |
| 113 |
{ |
| 114 |
return (int)$this->get('_newBookingID', 0); |
| 115 |
} |
| 116 |
|
| 117 |
/** |
| 118 |
* Sets the VCM action to be performed in order to sync the availability. |
| 119 |
* |
| 120 |
* @param string $action the VCM action, usually an HTML link. |
| 121 |
* |
| 122 |
* @return self |
| 123 |
*/ |
| 124 |
protected function setChannelManagerAction($action = '') |
| 125 |
{ |
| 126 |
$this->set('_vcmAction', $action); |
| 127 |
|
| 128 |
return $this; |
| 129 |
} |
| 130 |
|
| 131 |
/** |
| 132 |
* Returns the VCM action (if any) to sync the availability. |
| 133 |
* |
| 134 |
* @return string |
| 135 |
*/ |
| 136 |
public function getChannelManagerAction() |
| 137 |
{ |
| 138 |
return $this->get('_vcmAction', ''); |
| 139 |
} |
| 140 |
|
| 141 |
/** |
| 142 |
* Sets the check-in and check-out times with hours and minutes. |
| 143 |
* |
| 144 |
* @return array list of check-in and check-out hours and minutes. |
| 145 |
*/ |
| 146 |
public function loadCheckinOutTimes() |
| 147 |
{ |
| 148 |
static $times_loaded = null; |
| 149 |
|
| 150 |
if ($times_loaded) { |
| 151 |
return [ |
| 152 |
$this->get('checkin_h'), |
| 153 |
$this->get('checkin_m'), |
| 154 |
$this->get('checkout_h'), |
| 155 |
$this->get('checkout_m'), |
| 156 |
]; |
| 157 |
} |
| 158 |
|
| 159 |
$timeopst = VikBooking::getTimeOpenStore(); |
| 160 |
if (is_array($timeopst) && $timeopst) { |
| 161 |
$opent = VikBooking::getHoursMinutes($timeopst[0]); |
| 162 |
$closet = VikBooking::getHoursMinutes($timeopst[1]); |
| 163 |
$hcheckin = $opent[0]; |
| 164 |
$mcheckin = $opent[1]; |
| 165 |
$hcheckout = $closet[0]; |
| 166 |
$mcheckout = $closet[1]; |
| 167 |
} else { |
| 168 |
$hcheckin = 0; |
| 169 |
$mcheckin = 0; |
| 170 |
$hcheckout = 0; |
| 171 |
$mcheckout = 0; |
| 172 |
} |
| 173 |
|
| 174 |
$this->set('checkin_h', $hcheckin); |
| 175 |
$this->set('checkin_m', $mcheckin); |
| 176 |
$this->set('checkout_h', $hcheckout); |
| 177 |
$this->set('checkout_m', $mcheckout); |
| 178 |
|
| 179 |
$times_loaded = 1; |
| 180 |
|
| 181 |
return [ |
| 182 |
$hcheckin, |
| 183 |
$mcheckin, |
| 184 |
$hcheckout, |
| 185 |
$mcheckout, |
| 186 |
]; |
| 187 |
} |
| 188 |
|
| 189 |
/** |
| 190 |
* Creates a new reservation record after having constructed the |
| 191 |
* object by properly injecting all the necessary booking information. |
| 192 |
* |
| 193 |
* @return bool |
| 194 |
*/ |
| 195 |
public function create() |
| 196 |
{ |
| 197 |
if (!$this->canCreate()) { |
| 198 |
$this->setError('Forbidden'); |
| 199 |
return false; |
| 200 |
} |
| 201 |
|
| 202 |
// availability helper |
| 203 |
$av_helper = VikBooking::getAvailabilityInstance(); |
| 204 |
|
| 205 |
// validate mandatory fields |
| 206 |
$room = $this->getRoom(); |
| 207 |
if (!$this->get('checkin') || !$this->get('checkout') || empty($room['id'])) { |
| 208 |
$this->setError('Missing mandatory fields'); |
| 209 |
return false; |
| 210 |
} |
| 211 |
|
| 212 |
if ($this->get('checkin') >= $this->get('checkout')) { |
| 213 |
$this->setError('Invalid dates'); |
| 214 |
return false; |
| 215 |
} |
| 216 |
|
| 217 |
// make sure we have the time for check-in and check-out |
| 218 |
if (!$this->get('checkin_h') || !$this->get('checkout_h')) { |
| 219 |
// make sure to set the times (hours/minutes) for check-in and check-out |
| 220 |
$this->loadCheckinOutTimes(); |
| 221 |
} |
| 222 |
|
| 223 |
// number of nights of stay |
| 224 |
if (!$this->get('nights')) { |
| 225 |
$this->set('nights', $av_helper->countNightsOfStay($this->get('checkin'), $this->get('checkout'))); |
| 226 |
} |
| 227 |
|
| 228 |
// fetch and apply turnover time before doing anything else |
| 229 |
$this->applyTurnover(); |
| 230 |
|
| 231 |
// if rate plan selected, get the tariff ID |
| 232 |
$this->loadTariffID(); |
| 233 |
|
| 234 |
// get pool of rooms involved |
| 235 |
$rooms_pool = $this->getRoomsPool(); |
| 236 |
if (!$rooms_pool) { |
| 237 |
if ($this->getError() === false) { |
| 238 |
// set generic error if not set already |
| 239 |
$this->setError('No rooms involved in the reservation'); |
| 240 |
} |
| 241 |
return false; |
| 242 |
} |
| 243 |
|
| 244 |
// check if the room is available |
| 245 |
$room_available = $this->isRoomAvailable(); |
| 246 |
if (!$this->get('force_booking', 0) && !$this->get('set_closed', 0) && !$room_available) { |
| 247 |
// no forcing, no closure and room fully booked results into an error message |
| 248 |
$this->setError(JText::translate('VBBOOKNOTMADE')); |
| 249 |
return false; |
| 250 |
} |
| 251 |
|
| 252 |
// detect if we are forcing the reservation |
| 253 |
$this->detectForcedReason($room_available); |
| 254 |
|
| 255 |
// store the customer information |
| 256 |
$this->storeCustomer(); |
| 257 |
|
| 258 |
// calculate total amount and total tax |
| 259 |
$this->calculateTotal(); |
| 260 |
|
| 261 |
// store booking and room-booking records |
| 262 |
if (!$this->storeReservationRecords($rooms_pool)) { |
| 263 |
if ($this->getError() === false) { |
| 264 |
// set generic error if not set already |
| 265 |
$this->setError('Could not create the reservation'); |
| 266 |
} |
| 267 |
return false; |
| 268 |
} |
| 269 |
|
| 270 |
return true; |
| 271 |
} |
| 272 |
|
| 273 |
/** |
| 274 |
* Tells whether the operation is authenticated. By default this |
| 275 |
* model is only available in the administrator section of the site. |
| 276 |
* |
| 277 |
* @return bool |
| 278 |
*/ |
| 279 |
protected function canCreate() |
| 280 |
{ |
| 281 |
if (!JFactory::getApplication()->isClient('administrator') && !$this->get('_isAdministrator')) { |
| 282 |
return false; |
| 283 |
} |
| 284 |
|
| 285 |
return true; |
| 286 |
} |
| 287 |
|
| 288 |
/** |
| 289 |
* Gets and sets the tariff ID if a rate plan was set. |
| 290 |
* |
| 291 |
* @return int the tariff ID or 0. |
| 292 |
*/ |
| 293 |
protected function loadTariffID() |
| 294 |
{ |
| 295 |
$dbo = JFactory::getDbo(); |
| 296 |
|
| 297 |
$id_tariff = 0; |
| 298 |
$room = $this->getRoom(); |
| 299 |
$daysdiff = (int)$this->get('nights', 1); |
| 300 |
|
| 301 |
if (!empty($room['id']) && !empty($room['id_price']) && !empty($room['room_cost']) && $room['room_cost'] > 0 && !(int)$this->get('set_closed') && !$this->get('split_stay', [])) { |
| 302 |
$room['id_price'] = (int)$room['id_price']; |
| 303 |
|
| 304 |
$q = "SELECT `id` FROM `#__vikbooking_dispcost` WHERE `idroom`={$room['id']} AND `days`={$daysdiff} AND `idprice`={$room['id_price']};"; |
| 305 |
$dbo->setQuery($q); |
| 306 |
$id_tariff = $dbo->loadResult(); |
| 307 |
} |
| 308 |
|
| 309 |
$this->set('id_tariff', (int)$id_tariff); |
| 310 |
|
| 311 |
return (int)$id_tariff; |
| 312 |
} |
| 313 |
|
| 314 |
/** |
| 315 |
* Applies the turnover time to the checkout timestamp and sets its value. |
| 316 |
* |
| 317 |
* @return int the turnover seconds applied. |
| 318 |
*/ |
| 319 |
protected function applyTurnover() |
| 320 |
{ |
| 321 |
$turnover_secs = 0; |
| 322 |
$checkout = $this->get('checkout', 0); |
| 323 |
|
| 324 |
if ($checkout) { |
| 325 |
// turnover time |
| 326 |
$turnover_secs = VikBooking::getHoursRoomAvail() * 3600; |
| 327 |
|
| 328 |
$this->set('checkout_real', ($checkout + $turnover_secs)); |
| 329 |
} |
| 330 |
|
| 331 |
$this->set('turnover_secs', $turnover_secs); |
| 332 |
|
| 333 |
return $turnover_secs; |
| 334 |
} |
| 335 |
|
| 336 |
/** |
| 337 |
* Returns the details of a specific room ID. |
| 338 |
* |
| 339 |
* @param int $rid the room ID. |
| 340 |
* |
| 341 |
* @return array the record found or empty array. |
| 342 |
*/ |
| 343 |
protected function getRoomDetails($rid = null) |
| 344 |
{ |
| 345 |
$all_rooms = VikBooking::getAvailabilityInstance()->loadRooms(); |
| 346 |
|
| 347 |
if (!$rid) { |
| 348 |
$inj_room = $this->getRoom(); |
| 349 |
if (!empty($inj_room['id'])) { |
| 350 |
$rid = $inj_room['id']; |
| 351 |
} |
| 352 |
} |
| 353 |
|
| 354 |
if ($rid && isset($all_rooms[$rid])) { |
| 355 |
return $all_rooms[$rid]; |
| 356 |
} |
| 357 |
|
| 358 |
return []; |
| 359 |
} |
| 360 |
|
| 361 |
/** |
| 362 |
* Gets the list of rooms involved in the reservation in case of closures. |
| 363 |
* |
| 364 |
* @return array the list of rooms involved |
| 365 |
*/ |
| 366 |
protected function getRoomsPool() |
| 367 |
{ |
| 368 |
$room = $this->getRoom(); |
| 369 |
if (empty($room['id'])) { |
| 370 |
return []; |
| 371 |
} |
| 372 |
$room = $this->getRoomDetails($room['id']); |
| 373 |
|
| 374 |
// gather values |
| 375 |
$set_close_others = (array)$this->get('close_others', []); |
| 376 |
$split_stay_data = $this->get('split_stay', []); |
| 377 |
$set_closed = (int)$this->get('set_closed'); |
| 378 |
$turnover_secs = $this->get('turnover_secs', 0); |
| 379 |
$hcheckin = $this->get('checkin_h', 12); |
| 380 |
$mcheckin = $this->get('checkin_m', 0); |
| 381 |
$hcheckout = $this->get('checkout_h', 10); |
| 382 |
$mcheckout = $this->get('checkout_m', 0); |
| 383 |
|
| 384 |
$av_helper = VikBooking::getAvailabilityInstance(); |
| 385 |
$all_rooms = $av_helper->loadRooms(); |
| 386 |
$rooms_pool = []; |
| 387 |
$closeothers = []; |
| 388 |
|
| 389 |
if ($set_close_others && $set_closed) { |
| 390 |
// prepend current room for closing |
| 391 |
array_unshift($set_close_others, $room['id']); |
| 392 |
} |
| 393 |
$set_close_others = array_unique($set_close_others); |
| 394 |
|
| 395 |
foreach ($set_close_others as $closeid) { |
| 396 |
if (empty($closeid)) { |
| 397 |
continue; |
| 398 |
} |
| 399 |
if ((int)$closeid === -1) { |
| 400 |
// close all rooms |
| 401 |
$closeothers = []; |
| 402 |
foreach ($all_rooms as $cr) { |
| 403 |
array_push($closeothers, $cr); |
| 404 |
} |
| 405 |
break; |
| 406 |
} |
| 407 |
foreach ($all_rooms as $cr) { |
| 408 |
if ((int)$cr['id'] == (int)$closeid) { |
| 409 |
// push the main room or one of the other rooms requested for closure |
| 410 |
array_push($closeothers, $cr); |
| 411 |
break; |
| 412 |
} |
| 413 |
} |
| 414 |
} |
| 415 |
|
| 416 |
if (!$closeothers || !$set_closed) { |
| 417 |
$rooms_pool = [$room]; |
| 418 |
} else { |
| 419 |
$rooms_pool = $closeothers; |
| 420 |
} |
| 421 |
|
| 422 |
// check split stay rooms booking |
| 423 |
if (!empty($split_stay_data)) { |
| 424 |
// reset pool and set it with the split stay rooms |
| 425 |
$rooms_pool = []; |
| 426 |
foreach ($split_stay_data as $sps_k => $split_stay) { |
| 427 |
if (!isset($all_rooms[$split_stay['idroom']])) { |
| 428 |
continue; |
| 429 |
} |
| 430 |
// calculate and set the exact check-in and check-out timestamps for this split-room |
| 431 |
$split_stay['checkin_ts'] = VikBooking::getDateTimestamp($split_stay['checkin'], $hcheckin, $mcheckin); |
| 432 |
$split_stay['checkout_ts'] = VikBooking::getDateTimestamp($split_stay['checkout'], $hcheckout, $mcheckout); |
| 433 |
$split_stay['realback_ts'] = $turnover_secs + $split_stay['checkout_ts']; |
| 434 |
$split_stay['nights'] = $av_helper->countNightsOfStay($split_stay['checkin_ts'], $split_stay['checkout_ts']); |
| 435 |
$split_stay_data[$sps_k] = $split_stay; |
| 436 |
// push room data to pool after storing additional information |
| 437 |
$room_data = $all_rooms[$split_stay['idroom']]; |
| 438 |
$room_data['checkin_ts'] = $split_stay['checkin_ts']; |
| 439 |
$room_data['checkout_ts'] = $split_stay['checkout_ts']; |
| 440 |
$rooms_pool[] = $room_data; |
| 441 |
} |
| 442 |
if (!$rooms_pool) { |
| 443 |
$this->setError('No valid rooms for the split stay booking'); |
| 444 |
return []; |
| 445 |
} |
| 446 |
// update split stay data manipulated |
| 447 |
$this->set('split_stay', $split_stay_data); |
| 448 |
} |
| 449 |
|
| 450 |
return $rooms_pool; |
| 451 |
} |
| 452 |
|
| 453 |
/** |
| 454 |
* Checks that the room is available on the requested dates. |
| 455 |
* Call this method only after getting the rooms pool. |
| 456 |
* |
| 457 |
* @return bool |
| 458 |
*/ |
| 459 |
protected function isRoomAvailable() |
| 460 |
{ |
| 461 |
$inj_room = $this->getRoom(); |
| 462 |
if (empty($inj_room['id'])) { |
| 463 |
return false; |
| 464 |
} |
| 465 |
$room = $this->getRoomDetails($inj_room['id']); |
| 466 |
if (!$room) { |
| 467 |
return false; |
| 468 |
} |
| 469 |
|
| 470 |
$split_stay_data = $this->get('split_stay', []); |
| 471 |
$set_closed = $this->get('set_closed', 0); |
| 472 |
$num_rooms = $this->get('num_rooms', 1); |
| 473 |
|
| 474 |
$room_available = true; |
| 475 |
$all_rooms = VikBooking::getAvailabilityInstance()->loadRooms(); |
| 476 |
|
| 477 |
if (empty($split_stay_data)) { |
| 478 |
// make sure the rooms are available |
| 479 |
$check_units = $room['units']; |
| 480 |
if ($num_rooms > 1 && $num_rooms <= $room['units'] && !$set_closed) { |
| 481 |
// only when non closing the room we check the availability for the units requested for booking |
| 482 |
$check_units = $room['units'] - $num_rooms + 1; |
| 483 |
} |
| 484 |
$room_available = VikBooking::roomBookable($room['id'], $check_units, $this->get('checkin', 0), $this->get('checkout', 0)); |
| 485 |
} else { |
| 486 |
// make sure the rooms for the split stay are available |
| 487 |
foreach ($split_stay_data as $split_stay) { |
| 488 |
if (!isset($all_rooms[$split_stay['idroom']])) { |
| 489 |
$room_available = false; |
| 490 |
break; |
| 491 |
} |
| 492 |
$room_available = $room_available && VikBooking::roomBookable($split_stay['idroom'], $all_rooms[$split_stay['idroom']]['units'], $split_stay['checkin_ts'], $split_stay['checkout_ts']); |
| 493 |
} |
| 494 |
} |
| 495 |
|
| 496 |
return $room_available; |
| 497 |
} |
| 498 |
|
| 499 |
/** |
| 500 |
* In case the reservation is forced or is a closure, we detect the |
| 501 |
* forced reason to eventually attach it to the booking history. |
| 502 |
* |
| 503 |
* @param bool $room_available whether the room is available. |
| 504 |
* |
| 505 |
* @return void |
| 506 |
*/ |
| 507 |
protected function detectForcedReason($room_available = true) |
| 508 |
{ |
| 509 |
$split_stay_data = $this->get('split_stay', []); |
| 510 |
$force_booking = $this->get('force_booking', 0); |
| 511 |
$set_closed = $this->get('set_closed', 0); |
| 512 |
|
| 513 |
$forced_reason = ''; |
| 514 |
$all_rooms = VikBooking::getAvailabilityInstance()->loadRooms(); |
| 515 |
|
| 516 |
if (empty($split_stay_data)) { |
| 517 |
// eventually build string for the description of the history event |
| 518 |
if (($force_booking || $set_closed) && !$room_available) { |
| 519 |
$forced_reason = JText::translate('VBO_FORCED_BOOKDATES'); |
| 520 |
} |
| 521 |
} else { |
| 522 |
// set "split stay" as the description of the history event |
| 523 |
$forced_reason = JText::translate('VBO_SPLIT_STAY') . "\n"; |
| 524 |
foreach ($split_stay_data as $sps_k => $split_stay) { |
| 525 |
// describe the split stay for each room |
| 526 |
if (!isset($all_rooms[$split_stay['idroom']])) { |
| 527 |
continue; |
| 528 |
} |
| 529 |
$room_stay_nights = $split_stay['nights']; |
| 530 |
$forced_reason .= $all_rooms[$split_stay['idroom']]['name'] . ': ' . $room_stay_nights . ' ' . ($room_stay_nights > 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')) . ', '; |
| 531 |
$forced_reason .= $split_stay['checkin'] . ' - ' . $split_stay['checkout'] . "\n"; |
| 532 |
} |
| 533 |
$forced_reason = rtrim($forced_reason, "\n"); |
| 534 |
} |
| 535 |
|
| 536 |
$this->set('forced_reason', $forced_reason); |
| 537 |
} |
| 538 |
|
| 539 |
/** |
| 540 |
* Stores the customer information to a new or existing record. |
| 541 |
* In case of success, the customer ID property is updated. |
| 542 |
* The customer shall be stored before the reservation records. |
| 543 |
* |
| 544 |
* @return bool |
| 545 |
*/ |
| 546 |
protected function storeCustomer() |
| 547 |
{ |
| 548 |
$dbo = JFactory::getDbo(); |
| 549 |
|
| 550 |
$inj_customer = $this->getCustomer(); |
| 551 |
$first_name = !empty($inj_customer['first_name']) ? $inj_customer['first_name'] : ''; |
| 552 |
$last_name = !empty($inj_customer['last_name']) ? $inj_customer['last_name'] : ''; |
| 553 |
$custdata = !empty($inj_customer['data']) ? $inj_customer['data'] : ''; |
| 554 |
$email = !empty($inj_customer['email']) ? $inj_customer['email'] : ''; |
| 555 |
$country = !empty($inj_customer['country']) ? $inj_customer['country'] : ''; |
| 556 |
$phone = !empty($inj_customer['phone']) ? $inj_customer['phone'] : ''; |
| 557 |
|
| 558 |
// custom fields |
| 559 |
$q = "SELECT * FROM `#__vikbooking_custfields` ORDER BY `ordering` ASC;"; |
| 560 |
$dbo->setQuery($q); |
| 561 |
$all_cfields = $dbo->loadAssocList(); |
| 562 |
|
| 563 |
$customer_cfields = []; |
| 564 |
$customer_extrainfo = []; |
| 565 |
$custdata_parts = explode("\n", $custdata); |
| 566 |
foreach ($custdata_parts as $cdataline) { |
| 567 |
if (!strlen(trim($cdataline))) { |
| 568 |
continue; |
| 569 |
} |
| 570 |
$cdata_parts = explode(':', $cdataline); |
| 571 |
if (count($cdata_parts) < 2 || !strlen(trim($cdata_parts[0])) || !strlen(trim($cdata_parts[1]))) { |
| 572 |
continue; |
| 573 |
} |
| 574 |
foreach ($all_cfields as $cf) { |
| 575 |
$needle = JText::translate($cf['name']); |
| 576 |
if (!empty($needle) && strpos($cdata_parts[0], $needle) !== false && !array_key_exists($cf['id'], $customer_cfields) && $cf['type'] != 'country') { |
| 577 |
$user_input_val = trim($cdata_parts[1]); |
| 578 |
$customer_cfields[$cf['id']] = $user_input_val; |
| 579 |
if (!empty($cf['flag'])) { |
| 580 |
$customer_extrainfo[$cf['flag']] = $user_input_val; |
| 581 |
} elseif ($cf['type'] == 'state') { |
| 582 |
$customer_extrainfo['state'] = $user_input_val; |
| 583 |
} |
| 584 |
break; |
| 585 |
} |
| 586 |
} |
| 587 |
} |
| 588 |
|
| 589 |
$cpin = VikBooking::getCPinInstance(); |
| 590 |
$cpin->is_admin = true; |
| 591 |
$cpin->setCustomerExtraInfo($customer_extrainfo); |
| 592 |
$cpin->saveCustomerDetails($first_name, $last_name, $email, $phone, $country, $customer_cfields); |
| 593 |
|
| 594 |
$customer_id = $cpin->getNewCustomerId(); |
| 595 |
if (!$customer_id) { |
| 596 |
return false; |
| 597 |
} |
| 598 |
|
| 599 |
$inj_customer['id'] = $customer_id; |
| 600 |
$this->setCustomer($inj_customer); |
| 601 |
|
| 602 |
return true; |
| 603 |
} |
| 604 |
|
| 605 |
/** |
| 606 |
* Returns the calculated total booking amount and total taxes. |
| 607 |
* Sets the necessary properties with the calculated amounts. |
| 608 |
* |
| 609 |
* @return array list of booking total amount and total taxes. |
| 610 |
*/ |
| 611 |
protected function calculateTotal() |
| 612 |
{ |
| 613 |
// the values to calculate |
| 614 |
$set_total = 0; |
| 615 |
$set_taxes = 0; |
| 616 |
|
| 617 |
$dbo = JFactory::getDbo(); |
| 618 |
|
| 619 |
// get data |
| 620 |
$inj_room = $this->getRoom(); |
| 621 |
$set_closed = $this->get('set_closed', 0); |
| 622 |
$daysdiff = (int)$this->get('nights', 1); |
| 623 |
$num_rooms = (int)$this->get('num_rooms', 1); |
| 624 |
$totalpnight = !empty($inj_room['total_or_pnight']) ? $inj_room['total_or_pnight'] : 'total'; |
| 625 |
$cust_cost = !empty($inj_room['cust_cost']) ? (float)$inj_room['cust_cost'] : 0; |
| 626 |
$room_cost = !empty($inj_room['room_cost']) ? (float)$inj_room['room_cost'] : 0; |
| 627 |
$id_price = !empty($inj_room['id_price']) ? (int)$inj_room['id_price'] : 0; |
| 628 |
$id_tax = !empty($inj_room['id_tax']) ? (int)$inj_room['id_tax'] : 0; |
| 629 |
|
| 630 |
$split_stay_data = $this->get('split_stay', []); |
| 631 |
|
| 632 |
$room = $this->getRoomDetails(); |
| 633 |
if (!$room) { |
| 634 |
return [$set_total, $set_taxes]; |
| 635 |
} |
| 636 |
|
| 637 |
if ($cust_cost > 0 && !$set_closed) { |
| 638 |
// custom cost can be per night |
| 639 |
if ($totalpnight == 'pnight') { |
| 640 |
$cust_cost = $cust_cost * $daysdiff; |
| 641 |
} |
| 642 |
$set_total = $cust_cost; |
| 643 |
|
| 644 |
// apply taxes, if necessary |
| 645 |
if ($id_tax) { |
| 646 |
$q = "SELECT `i`.`aliq`,`i`.`taxcap` FROM `#__vikbooking_iva` AS `i` WHERE `i`.`id`=" . $id_tax . ";"; |
| 647 |
$dbo->setQuery($q); |
| 648 |
$taxdata = $dbo->loadAssoc(); |
| 649 |
if ($taxdata) { |
| 650 |
$aliq = $taxdata['aliq']; |
| 651 |
if (floatval($aliq) > 0.00) { |
| 652 |
if (!VikBooking::ivaInclusa()) { |
| 653 |
// add tax to the total amount |
| 654 |
$subt = 100 + (float)$aliq; |
| 655 |
$set_total = ($set_total * $subt / 100); |
| 656 |
/** |
| 657 |
* Tax Cap implementation for prices tax excluded (most common). |
| 658 |
* |
| 659 |
* @since 1.12 (J) - 1.2 (WP) |
| 660 |
*/ |
| 661 |
if ($taxdata['taxcap'] > 0 && ($set_total - $cust_cost) > $taxdata['taxcap']) { |
| 662 |
$set_total = ($cust_cost + $taxdata['taxcap']); |
| 663 |
} |
| 664 |
// calculate tax |
| 665 |
$set_taxes = $set_total - $cust_cost; |
| 666 |
} else { |
| 667 |
// calculate tax |
| 668 |
$cost_minus_tax = VikBooking::sayPackageMinusIva($cust_cost, $id_tax); |
| 669 |
$set_taxes += ($cust_cost - $cost_minus_tax); |
| 670 |
} |
| 671 |
} |
| 672 |
} |
| 673 |
} |
| 674 |
} elseif (!empty($id_price) && $room_cost > 0 && !$set_closed && empty($split_stay_data)) { |
| 675 |
// one website rate plan was selected, so we calculate total and taxes |
| 676 |
$set_total = $room_cost; |
| 677 |
|
| 678 |
// find tax rate assigned to this rate plan |
| 679 |
$q = "SELECT `p`.`id`,`p`.`idiva`,`i`.`aliq`,`i`.`taxcap` FROM `#__vikbooking_prices` AS `p` LEFT JOIN `#__vikbooking_iva` AS `i` ON `p`.`idiva`=`i`.`id` WHERE `p`.`id`=" . $id_price . ";"; |
| 680 |
$dbo->setQuery($q); |
| 681 |
$taxdata = $dbo->loadAssoc(); |
| 682 |
if ($taxdata) { |
| 683 |
$aliq = $taxdata['aliq']; |
| 684 |
if (floatval($aliq) > 0.00) { |
| 685 |
if (!VikBooking::ivaInclusa()) { |
| 686 |
// add tax to the total amount |
| 687 |
$subt = 100 + (float)$aliq; |
| 688 |
$set_total = ($set_total * $subt / 100); |
| 689 |
/** |
| 690 |
* Tax Cap implementation for prices tax excluded (most common). |
| 691 |
* |
| 692 |
* @since 1.12 (J) - 1.2 (WP) |
| 693 |
*/ |
| 694 |
if ($taxdata['taxcap'] > 0 && ($set_total - $room_cost) > $taxdata['taxcap']) { |
| 695 |
$set_total = ($room_cost + $taxdata['taxcap']); |
| 696 |
} |
| 697 |
// calculate tax |
| 698 |
$set_taxes = $set_total - $room_cost; |
| 699 |
} else { |
| 700 |
// calculate tax |
| 701 |
$cost_minus_tax = VikBooking::sayPackageMinusIva($room_cost, $taxdata['idiva']); |
| 702 |
$set_taxes += ($room_cost - $cost_minus_tax); |
| 703 |
} |
| 704 |
} |
| 705 |
} |
| 706 |
|
| 707 |
// total and taxes should be multiplied by the number of rooms booked when using a website rate plan |
| 708 |
if ($set_closed) { |
| 709 |
$set_total *= $room['units']; |
| 710 |
$set_taxes *= $room['units']; |
| 711 |
} elseif ($num_rooms > 1 && $num_rooms <= $room['units']) { |
| 712 |
$set_total *= $num_rooms; |
| 713 |
$set_taxes *= $num_rooms; |
| 714 |
} |
| 715 |
} |
| 716 |
|
| 717 |
// set values |
| 718 |
$this->set('_total', $set_total); |
| 719 |
$this->set('_total_tax', $set_taxes); |
| 720 |
|
| 721 |
return [$set_total, $set_taxes]; |
| 722 |
} |
| 723 |
|
| 724 |
/** |
| 725 |
* Stores the booking and room-booking records. |
| 726 |
* If no errors, the newly generated booking id is set. |
| 727 |
* |
| 728 |
* @param array $rooms_pool list of rooms involved. |
| 729 |
* |
| 730 |
* @return bool |
| 731 |
*/ |
| 732 |
protected function storeReservationRecords(array $rooms_pool) |
| 733 |
{ |
| 734 |
$dbo = JFactory::getDbo(); |
| 735 |
|
| 736 |
$set_closed = $this->get('set_closed', 0); |
| 737 |
$daysdiff = (int)$this->get('nights', 1); |
| 738 |
$num_rooms = (int)$this->get('num_rooms', 1); |
| 739 |
$adults = (int)$this->get('adults', 1); |
| 740 |
$children = (int)$this->get('children', 0); |
| 741 |
$status = $this->get('status', 'confirmed'); |
| 742 |
|
| 743 |
$split_stay_data = $this->get('split_stay', []); |
| 744 |
$room = $this->getRoomDetails(); |
| 745 |
if (!$room || !$rooms_pool) { |
| 746 |
return false; |
| 747 |
} |
| 748 |
|
| 749 |
// get current Joomla/WordPress User ID |
| 750 |
$now_user = JFactory::getUser(); |
| 751 |
$store_ujid = property_exists($now_user, 'id') ? (int)$now_user->id : 0; |
| 752 |
|
| 753 |
// forced booking reason, status validation and additional data |
| 754 |
$forced_reason = $this->get('forced_reason', ''); |
| 755 |
$valid_statuses = ['confirmed', 'standby']; |
| 756 |
$status = in_array($status, $valid_statuses) ? $status : 'confirmed'; |
| 757 |
$paymentmeth = $this->get('id_payment', ''); |
| 758 |
$set_total = (float)$this->get('_total', 0); |
| 759 |
$set_taxes = (float)$this->get('_total_tax', 0); |
| 760 |
|
| 761 |
// stay dates |
| 762 |
$now_ts = time(); |
| 763 |
$checkin_ts = $this->get('checkin'); |
| 764 |
$checkout_ts = $this->get('checkout'); |
| 765 |
$realback_ts = $this->get('checkout_real', $checkout_ts); |
| 766 |
|
| 767 |
// room |
| 768 |
$inj_room = $this->getRoom(); |
| 769 |
$cust_cost = !empty($inj_room['cust_cost']) ? (float)$inj_room['cust_cost'] : 0; |
| 770 |
$room_cost = !empty($inj_room['room_cost']) ? (float)$inj_room['room_cost'] : 0; |
| 771 |
$id_price = !empty($inj_room['id_price']) ? (int)$inj_room['id_price'] : 0; |
| 772 |
$id_tax = !empty($inj_room['id_tax']) ? (int)$inj_room['id_tax'] : 0; |
| 773 |
$id_tariff = $this->get('id_tariff', 0); |
| 774 |
|
| 775 |
// custom rate modifier per night |
| 776 |
$totalpnight = !empty($inj_room['total_or_pnight']) ? $inj_room['total_or_pnight'] : 'total'; |
| 777 |
if ($cust_cost > 0.00 && !$set_closed && $totalpnight == 'pnight') { |
| 778 |
$cust_cost = $cust_cost * $daysdiff; |
| 779 |
} |
| 780 |
|
| 781 |
// customer |
| 782 |
$cpin = VikBooking::getCPinInstance(); |
| 783 |
$inj_customer = $this->getCustomer(); |
| 784 |
$customer_id = !empty($inj_customer['id']) ? $inj_customer['id'] : 0; |
| 785 |
$customer_pin = !empty($inj_customer['pin']) ? $inj_customer['pin'] : ''; |
| 786 |
$t_first_name = !empty($inj_customer['first_name']) ? $inj_customer['first_name'] : ''; |
| 787 |
$t_last_name = !empty($inj_customer['last_name']) ? $inj_customer['last_name'] : ''; |
| 788 |
$customer_data = !empty($inj_customer['data']) ? $inj_customer['data'] : ''; |
| 789 |
$customer_email = !empty($inj_customer['email']) ? $inj_customer['email'] : ''; |
| 790 |
$country_code = !empty($inj_customer['country']) ? $inj_customer['country'] : ''; |
| 791 |
$phone_number = !empty($inj_customer['phone']) ? $inj_customer['phone'] : ''; |
| 792 |
|
| 793 |
if ($set_closed) { |
| 794 |
$customer_data = JText::translate('VBDBTEXTROOMCLOSED'); |
| 795 |
} |
| 796 |
|
| 797 |
// generate booking SID |
| 798 |
$sid = VikBooking::getSecretLink(); |
| 799 |
|
| 800 |
// assign room specific unit |
| 801 |
$set_room_indexes = !$set_closed ? VikBooking::autoRoomUnit() : false; |
| 802 |
$num_rooms = $num_rooms > 0 ? $num_rooms : 1; |
| 803 |
|
| 804 |
// occupancy and loop limits |
| 805 |
$forend = 1; |
| 806 |
$or_forend = 1; |
| 807 |
$adults_map = []; |
| 808 |
$children_map = []; |
| 809 |
if ($set_closed && empty($split_stay_data)) { |
| 810 |
$forend = $room['units']; |
| 811 |
} elseif ($num_rooms > 1 && $num_rooms <= $room['units'] && empty($split_stay_data)) { |
| 812 |
$forend = $num_rooms; |
| 813 |
$or_forend = $num_rooms; |
| 814 |
// assign adults/children proportionally |
| 815 |
if (($adults + $children) < $num_rooms) { |
| 816 |
// the number of guests does not make much sense but we build the maps anyway |
| 817 |
for ($r = 1; $r <= $or_forend; $r++) { |
| 818 |
$adults_map[$r] = $adults; |
| 819 |
$children_map[$r] = $children; |
| 820 |
} |
| 821 |
} else { |
| 822 |
$adults_per_room = floor(($adults / $num_rooms)); |
| 823 |
$adults_left = ($adults % $num_rooms); |
| 824 |
$children_per_room = floor(($children / $num_rooms)); |
| 825 |
$children_left = ($children % $num_rooms); |
| 826 |
for ($r = 1; $r <= $or_forend; $r++) { |
| 827 |
$adults_map[$r] = $adults_per_room; |
| 828 |
$children_map[$r] = $children_per_room; |
| 829 |
if ($r == $or_forend) { |
| 830 |
$adults_map[$r] += $adults_left; |
| 831 |
$children_map[$r] += $children_left; |
| 832 |
} |
| 833 |
} |
| 834 |
} |
| 835 |
} |
| 836 |
|
| 837 |
// count total rooms booked |
| 838 |
$totalrooms = ($set_closed && $status == 'confirmed' ? count($rooms_pool) : ($num_rooms > 1 && $num_rooms <= $room['units'] ? $num_rooms : 1)); |
| 839 |
$totalrooms = !empty($split_stay_data) ? count($split_stay_data) : $totalrooms; |
| 840 |
|
| 841 |
// prepare booking record |
| 842 |
$booking = new stdClass; |
| 843 |
$booking->custdata = $customer_data; |
| 844 |
$booking->ts = $now_ts; |
| 845 |
$booking->status = $status; |
| 846 |
$booking->days = $daysdiff; |
| 847 |
$booking->checkin = $checkin_ts; |
| 848 |
$booking->checkout = $checkout_ts; |
| 849 |
$booking->custmail = $customer_email; |
| 850 |
$booking->sid = $sid; |
| 851 |
$booking->idpayment = $paymentmeth; |
| 852 |
$booking->ujid = (int)$store_ujid; |
| 853 |
$booking->roomsnum = $totalrooms; |
| 854 |
$booking->total = $set_total > 0 ? $set_total : null; |
| 855 |
$booking->lang = VikBooking::guessBookingLangFromCountry($country_code); |
| 856 |
$booking->country = $country_code; |
| 857 |
$booking->tot_taxes = $set_taxes > 0 ? $set_taxes : null; |
| 858 |
$booking->phone = $phone_number; |
| 859 |
$booking->closure = ($status == 'standby' ? 0 : ($set_closed ? 1 : 0)); |
| 860 |
$booking->split_stay = !empty($split_stay_data) ? 1 : 0; |
| 861 |
|
| 862 |
// store reservation |
| 863 |
if ($status == 'confirmed') { |
| 864 |
// occupy the rooms |
| 865 |
$insertedbusy = []; |
| 866 |
if (empty($split_stay_data)) { |
| 867 |
// only when closing other rooms we have an array containing multiple rooms info |
| 868 |
foreach ($rooms_pool as $nowroom) { |
| 869 |
$nowforend = $set_closed ? $nowroom['units'] : $forend; |
| 870 |
for ($b = 1; $b <= $nowforend; $b++) { |
| 871 |
$busy_record = new stdClass; |
| 872 |
$busy_record->idroom = (int)$nowroom['id']; |
| 873 |
$busy_record->checkin = (int)$checkin_ts; |
| 874 |
$busy_record->checkout = (int)$checkout_ts; |
| 875 |
$busy_record->realback = (int)$realback_ts; |
| 876 |
|
| 877 |
// store busy record |
| 878 |
$dbo->insertObject('#__vikbooking_busy', $busy_record, 'id'); |
| 879 |
|
| 880 |
if (isset($busy_record->id)) { |
| 881 |
$insertedbusy[] = $busy_record->id; |
| 882 |
} |
| 883 |
} |
| 884 |
} |
| 885 |
} else { |
| 886 |
// for split stay bookings we occupy the rooms on the individual stay dates |
| 887 |
foreach ($split_stay_data as $split_stay) { |
| 888 |
$busy_record = new stdClass; |
| 889 |
$busy_record->idroom = (int)$split_stay['idroom']; |
| 890 |
$busy_record->checkin = (int)$split_stay['checkin_ts']; |
| 891 |
$busy_record->checkout = (int)$split_stay['checkout_ts']; |
| 892 |
$busy_record->realback = (int)$split_stay['realback_ts']; |
| 893 |
|
| 894 |
// store busy record |
| 895 |
$dbo->insertObject('#__vikbooking_busy', $busy_record, 'id'); |
| 896 |
|
| 897 |
if (isset($busy_record->id)) { |
| 898 |
$insertedbusy[] = $busy_record->id; |
| 899 |
} |
| 900 |
} |
| 901 |
} |
| 902 |
|
| 903 |
if (!$insertedbusy) { |
| 904 |
$this->setError('No records were occupied'); |
| 905 |
return false; |
| 906 |
} |
| 907 |
|
| 908 |
// store booking record |
| 909 |
$dbo->insertObject('#__vikbooking_orders', $booking, 'id'); |
| 910 |
|
| 911 |
if (!isset($booking->id)) { |
| 912 |
$this->setError('Could not store the reservation record'); |
| 913 |
return false; |
| 914 |
} |
| 915 |
|
| 916 |
// get the newly generated booking ID |
| 917 |
$newoid = $booking->id; |
| 918 |
|
| 919 |
// set the new booking ID |
| 920 |
$this->setNewBookingID($newoid); |
| 921 |
|
| 922 |
if (!empty($split_stay_data)) { |
| 923 |
// save transient on db for split stay information |
| 924 |
VBOFactory::getConfig()->set('split_stay_' . $newoid, json_encode($split_stay_data)); |
| 925 |
} |
| 926 |
|
| 927 |
// check if some of the rooms booked have shared calendars |
| 928 |
VikBooking::updateSharedCalendars($newoid, [$room['id']], $checkin_ts, $checkout_ts); |
| 929 |
|
| 930 |
// confirmation number |
| 931 |
$confirmnumber = VikBooking::generateConfirmNumber($newoid, true); |
| 932 |
|
| 933 |
// store busy records/booking relations |
| 934 |
foreach ($insertedbusy as $lid) { |
| 935 |
$obusy_record = new stdClass; |
| 936 |
$obusy_record->idorder = (int)$newoid; |
| 937 |
$obusy_record->idbusy = (int)$lid; |
| 938 |
|
| 939 |
// store busy relation record |
| 940 |
$dbo->insertObject('#__vikbooking_ordersbusy', $obusy_record, 'id'); |
| 941 |
} |
| 942 |
|
| 943 |
// write room records |
| 944 |
foreach ($rooms_pool as $rind => $nowroom) { |
| 945 |
$room_indexes_usemap = []; |
| 946 |
for ($r = 1; $r <= $or_forend; $r++) { |
| 947 |
// Assign room specific unit |
| 948 |
$info_room_avail = [ |
| 949 |
'id' => $newoid, |
| 950 |
'checkin' => (!empty($nowroom['checkin_ts']) ? $nowroom['checkin_ts'] : $checkin_ts), |
| 951 |
'checkout' => (!empty($nowroom['checkout_ts']) ? $nowroom['checkout_ts'] : $checkout_ts), |
| 952 |
]; |
| 953 |
$room_indexes = $set_room_indexes === true ? VikBooking::getRoomUnitNumsAvailable($info_room_avail, $nowroom['id']) : []; |
| 954 |
$use_ind_key = 0; |
| 955 |
if ($room_indexes) { |
| 956 |
if (!array_key_exists($nowroom['id'], $room_indexes_usemap)) { |
| 957 |
$room_indexes_usemap[$nowroom['id']] = $use_ind_key; |
| 958 |
} else { |
| 959 |
$use_ind_key = $room_indexes_usemap[$nowroom['id']]; |
| 960 |
} |
| 961 |
} |
| 962 |
|
| 963 |
// room custom cost |
| 964 |
$or_cust_cost = $cust_cost > 0.00 ? $cust_cost : 0; |
| 965 |
$or_cust_cost = $or_forend > 1 && $or_cust_cost > 0 ? round(($or_cust_cost / $or_forend), 2) : $or_cust_cost; |
| 966 |
// room cost from website rate plan is always based on one room |
| 967 |
$or_room_cost = $room_cost > 0.00 ? $room_cost : 0; |
| 968 |
if (!empty($split_stay_data) && $cust_cost > 0) { |
| 969 |
// set the average cost per room in case of split stay |
| 970 |
$cost_per_room = ($cust_cost / count($split_stay_data)); |
| 971 |
$or_cust_cost = round($cost_per_room, 2); |
| 972 |
if (isset($split_stay_data[$rind]) && isset($split_stay_data[$rind]['nights'])) { |
| 973 |
// count the average cost per room depending on the number of nights of stay |
| 974 |
$cost_per_room = $cust_cost / $daysdiff * $split_stay_data[$rind]['nights']; |
| 975 |
$or_cust_cost = round($cost_per_room, 2); |
| 976 |
} |
| 977 |
} |
| 978 |
|
| 979 |
// room guests |
| 980 |
$room_adults = isset($adults_map[$r]) && empty($split_stay_data) ? $adults_map[$r] : $adults; |
| 981 |
$room_children = isset($children_map[$r]) && empty($split_stay_data) ? $children_map[$r] : $children; |
| 982 |
|
| 983 |
// store room record |
| 984 |
$room_record = new stdClass; |
| 985 |
$room_record->idorder = (int)$newoid; |
| 986 |
$room_record->idroom = (int)$nowroom['id']; |
| 987 |
$room_record->adults = $room_adults; |
| 988 |
$room_record->children = $room_children; |
| 989 |
$room_record->idtar = !empty($id_tariff) ? $id_tariff : null; |
| 990 |
$room_record->t_first_name = $t_first_name; |
| 991 |
$room_record->t_last_name = $t_last_name; |
| 992 |
$room_record->roomindex = count($room_indexes) ? (int)$room_indexes[$use_ind_key] : null; |
| 993 |
$room_record->cust_cost = $cust_cost > 0.00 ? $or_cust_cost : null; |
| 994 |
$room_record->cust_idiva = $cust_cost > 0.00 && !empty($id_tax) ? $id_tax : null; |
| 995 |
$room_record->room_cost = $room_cost > 0.00 ? $or_room_cost : null; |
| 996 |
|
| 997 |
$dbo->insertObject('#__vikbooking_ordersrooms', $room_record, 'id'); |
| 998 |
|
| 999 |
if (!isset($room_record->id)) { |
| 1000 |
$this->setError('Could not store room reservation record for booking ID ' . $room_record->idorder); |
| 1001 |
continue; |
| 1002 |
} |
| 1003 |
|
| 1004 |
// Assign room specific unit |
| 1005 |
if ($room_indexes) { |
| 1006 |
$room_indexes_usemap[$nowroom['id']]++; |
| 1007 |
} |
| 1008 |
} |
| 1009 |
} |
| 1010 |
} elseif ($status == 'standby') { |
| 1011 |
// store booking record |
| 1012 |
$dbo->insertObject('#__vikbooking_orders', $booking, 'id'); |
| 1013 |
|
| 1014 |
if (!isset($booking->id)) { |
| 1015 |
$this->setError('Could not store the reservation record'); |
| 1016 |
return false; |
| 1017 |
} |
| 1018 |
|
| 1019 |
// get the newly generated booking ID |
| 1020 |
$newoid = $booking->id; |
| 1021 |
|
| 1022 |
// set the new booking ID |
| 1023 |
$this->setNewBookingID($newoid); |
| 1024 |
|
| 1025 |
if (!empty($split_stay_data)) { |
| 1026 |
// save transient on db for split stay information |
| 1027 |
VBOFactory::getConfig()->set('split_stay_' . $newoid, json_encode($split_stay_data)); |
| 1028 |
} |
| 1029 |
|
| 1030 |
// write room records |
| 1031 |
foreach ($rooms_pool as $rind => $nowroom) { |
| 1032 |
for ($r = 1; $r <= $or_forend; $r++) { |
| 1033 |
// room custom cost |
| 1034 |
$or_cust_cost = $cust_cost > 0.00 ? $cust_cost : 0; |
| 1035 |
$or_cust_cost = $or_forend > 1 && $or_cust_cost > 0 ? round(($or_cust_cost / $or_forend), 2) : $or_cust_cost; |
| 1036 |
// room cost from website rate plan is always based on one room |
| 1037 |
$or_room_cost = $room_cost > 0.00 ? $room_cost : 0; |
| 1038 |
if (!empty($split_stay_data) && $cust_cost > 0) { |
| 1039 |
// set the average cost per room in case of split stay |
| 1040 |
$cost_per_room = ($cust_cost / count($split_stay_data)); |
| 1041 |
$or_cust_cost = round($cost_per_room, 2); |
| 1042 |
if (isset($split_stay_data[$rind]) && isset($split_stay_data[$rind]['nights'])) { |
| 1043 |
// count the average cost per room depending on the number of nights of stay |
| 1044 |
$cost_per_room = $cust_cost / $daysdiff * $split_stay_data[$rind]['nights']; |
| 1045 |
$or_cust_cost = round($cost_per_room, 2); |
| 1046 |
} |
| 1047 |
} |
| 1048 |
|
| 1049 |
// room guests |
| 1050 |
$room_adults = isset($adults_map[$r]) && empty($split_stay_data) ? $adults_map[$r] : $adults; |
| 1051 |
$room_children = isset($children_map[$r]) && empty($split_stay_data) ? $children_map[$r] : $children; |
| 1052 |
|
| 1053 |
// store room record |
| 1054 |
$room_record = new stdClass; |
| 1055 |
$room_record->idorder = (int)$newoid; |
| 1056 |
$room_record->idroom = (int)$nowroom['id']; |
| 1057 |
$room_record->adults = $room_adults; |
| 1058 |
$room_record->children = $room_children; |
| 1059 |
$room_record->idtar = !empty($id_tariff) ? $id_tariff : null; |
| 1060 |
$room_record->t_first_name = $t_first_name; |
| 1061 |
$room_record->t_last_name = $t_last_name; |
| 1062 |
$room_record->cust_cost = $cust_cost > 0.00 ? $or_cust_cost : null; |
| 1063 |
$room_record->cust_idiva = $cust_cost > 0.00 && !empty($id_tax) ? $id_tax : null; |
| 1064 |
$room_record->room_cost = $room_cost > 0.00 ? $or_room_cost : null; |
| 1065 |
|
| 1066 |
$dbo->insertObject('#__vikbooking_ordersrooms', $room_record, 'id'); |
| 1067 |
|
| 1068 |
if (!isset($room_record->id)) { |
| 1069 |
$this->setError('Could not store room reservation record for booking ID ' . $room_record->idorder); |
| 1070 |
continue; |
| 1071 |
} |
| 1072 |
|
| 1073 |
if (empty($split_stay_data)) { |
| 1074 |
// lock room for pending status |
| 1075 |
$tmplock_record = new stdClass; |
| 1076 |
$tmplock_record->idroom = (int)$room['id']; |
| 1077 |
$tmplock_record->checkin = $checkin_ts; |
| 1078 |
$tmplock_record->checkout = $checkout_ts; |
| 1079 |
$tmplock_record->until = VikBooking::getMinutesLock(true); |
| 1080 |
$tmplock_record->realback = $realback_ts; |
| 1081 |
$tmplock_record->idorder = (int)$newoid; |
| 1082 |
|
| 1083 |
$dbo->insertObject('#__vikbooking_tmplock', $tmplock_record, 'id'); |
| 1084 |
} |
| 1085 |
} |
| 1086 |
} |
| 1087 |
|
| 1088 |
if (!empty($split_stay_data)) { |
| 1089 |
// lock rooms for pending status on proper stay dates |
| 1090 |
foreach ($split_stay_data as $split_stay) { |
| 1091 |
$tmplock_record = new stdClass; |
| 1092 |
$tmplock_record->idroom = (int)$split_stay['idroom']; |
| 1093 |
$tmplock_record->checkin = (int)$split_stay['checkin_ts']; |
| 1094 |
$tmplock_record->checkout = (int)$split_stay['checkout_ts']; |
| 1095 |
$tmplock_record->until = VikBooking::getMinutesLock(true); |
| 1096 |
$tmplock_record->realback = (int)$split_stay['realback_ts']; |
| 1097 |
$tmplock_record->idorder = (int)$newoid; |
| 1098 |
|
| 1099 |
$dbo->insertObject('#__vikbooking_tmplock', $tmplock_record, 'id'); |
| 1100 |
} |
| 1101 |
} |
| 1102 |
} |
| 1103 |
|
| 1104 |
$newoid = $this->getNewBookingID(); |
| 1105 |
if (!$newoid) { |
| 1106 |
return false; |
| 1107 |
} |
| 1108 |
|
| 1109 |
// assign booking to customer |
| 1110 |
if (!$cpin->getNewCustomerId() && !empty($customer_id)) { |
| 1111 |
$cpin->setNewPin($customer_pin); |
| 1112 |
$cpin->setNewCustomerId($customer_id); |
| 1113 |
} |
| 1114 |
$cpin->saveCustomerBooking($newoid); |
| 1115 |
|
| 1116 |
// Booking History |
| 1117 |
$forced_reason = !empty($forced_reason) ? " {$forced_reason}" : $forced_reason; |
| 1118 |
VikBooking::getBookingHistoryInstance()->setBid($newoid)->store('NB', "({$now_user->name}){$forced_reason}"); |
| 1119 |
|
| 1120 |
if ($status == 'confirmed') { |
| 1121 |
// Invoke Channel Manager |
| 1122 |
$vcm_autosync = VikBooking::vcmAutoUpdate(); |
| 1123 |
if ($vcm_autosync > 0) { |
| 1124 |
$vcm_obj = VikBooking::getVcmInvoker(); |
| 1125 |
$vcm_obj->setOids([$newoid])->setSyncType('new'); |
| 1126 |
$sync_result = $vcm_obj->doSync(); |
| 1127 |
if ($sync_result === false) { |
| 1128 |
// set error message |
| 1129 |
$vcm_err = $vcm_obj->getError(); |
| 1130 |
$this->setError(JText::translate('VBCHANNELMANAGERRESULTKO') . (!empty($vcm_err) ? ' - ' . $vcm_err : '')); |
| 1131 |
|
| 1132 |
// return true because the booking was actually stored |
| 1133 |
return true; |
| 1134 |
} |
| 1135 |
} elseif (is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'synch.vikbooking.php')) { |
| 1136 |
// set the necessary action to invoke VCM |
| 1137 |
$vcm_sync_url = 'index.php?option=com_vikbooking&task=invoke_vcm&stype=new&cid[]=' . $newoid . '&returl=' . urlencode('index.php?option=com_vikbooking&task=calendar&cid[]=' . $room['id']); |
| 1138 |
|
| 1139 |
$this->setChannelManagerAction(JText::translate('VBCHANNELMANAGERINVOKEASK') . ' <button type="button" class="btn btn-primary" onclick="document.location.href=\'' . $vcm_sync_url . '\';">' . JText::translate('VBCHANNELMANAGERSENDRQ') . '</button>'); |
| 1140 |
} |
| 1141 |
} |
| 1142 |
|
| 1143 |
return true; |
| 1144 |
} |
| 1145 |
} |
| 1146 |
|