PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_virtuemart/classes/ps_booking.php

https://bitbucket.org/dgough/annamaria-daneswood-25102012
PHP | 1414 lines | 876 code | 281 blank | 257 comment | 194 complexity | f0002ad286d3694e5c19b8bf84821c36 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. if( ! defined( '_VALID_MOS' ) && ! defined( '_JEXEC' ) )
  3. die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ) ;
  4. require_once( CLASSPATH . "ps_discount.php" );
  5. require_once( CLASSPATH . "ps_season.php" );
  6. require_once( CLASSPATH . "ps_stage_payments.php" );
  7. class ps_booking {
  8. var $status = 0;
  9. var $total = 0;
  10. var $subtotal = 0;
  11. var $discount = 0;
  12. var $extras = 0;
  13. var $pets = 0;
  14. var $original = 0;
  15. var $tax = 0;
  16. var $tax_rate = 0;
  17. var $tax_state = 0;
  18. var $tax_resort = 0;
  19. var $dateFrom;
  20. var $dateTo;
  21. var $discounts = array();
  22. var $extras_details = array('extras_total'=>0);
  23. var $nights;
  24. var $nightPrices = array();
  25. var $seasons = array();
  26. var $maxPrice = 0;
  27. var $minNights = 0;
  28. var $securityDeposit = 48;
  29. /*** Booking info ***/
  30. var $booking_id;
  31. var $dateBooked;
  32. var $unixdateBooked;
  33. var $unixdateFrom;
  34. var $unixdateTo;
  35. var $people;
  36. var $arrivalAirport;
  37. var $departureAirport;
  38. var $arrivalFlightNo;
  39. var $departureFlightNo;
  40. var $hasPets;
  41. var $cleaning;
  42. /** Property Info **/
  43. var $property_id;
  44. var $property_data;
  45. /** Extra flags **/
  46. var $owner = 0;
  47. var $admin = 0;
  48. var $free = 0;
  49. var $payment_id = 0;
  50. var $display_errors = 1;
  51. /** Object holders **/
  52. var $discountObject;
  53. var $extrasObjects;
  54. var $ps_season;
  55. var $validation = array();
  56. /**
  57. *
  58. *
  59. */
  60. function __construct() {
  61. $this->status = 0;
  62. //Setup discounts class
  63. $this->discountObject = new ps_discount($this);
  64. }
  65. function ps_booking(){
  66. $this->__construct();
  67. }
  68. /**
  69. * Calculate the base value of the booking
  70. * and store the booking data in the object
  71. * then update the session to store the new values
  72. *
  73. */
  74. function process($saveSession = 1, $display_errors = 1){
  75. global $VM_LANG;
  76. //Always get the property ID
  77. $this->status = 0;
  78. $this->property_id = vmget($_REQUEST,'property_id',$this->property_id);
  79. $this->display_errors = $display_errors;
  80. $this->validationReset();
  81. //Setup discounts class
  82. $this->discountObject = new ps_discount($this);
  83. if(!$this->property_id){
  84. //Throw error
  85. $GLOBALS['vmLogger']->err($VM_LANG->_("VM_BOOKING_PROCESS_ERR"));
  86. $this->status = 0;
  87. return false;
  88. }else{
  89. //Calculate booking
  90. if(!$this->getPropertyData()) return false;
  91. $this->owner = $this->isOwner();
  92. $this->admin = $this->isAdmin();
  93. $this->free = $this->isFree();
  94. $this->dateBooked = date('Y-m-d');
  95. $this->unixdateBooked = strtotime($this->dateBooked);
  96. if(!$this->setDates()) return false;
  97. $this->people = vmget($_REQUEST,'people', $this->people);
  98. $this->arrivalAirport = vmget($_REQUEST,'arrivalAirport');
  99. $this->departureAirport = vmget($_REQUEST,'departureAirport');
  100. $this->extras_details = $this->setExtras(vmget($_REQUEST,'quantity'));
  101. $this->arrivalFlightNo = vmget($_REQUEST,'arrivalFlightNo');
  102. $this->departureFlightNo = vmget($_REQUEST,'departureFlightNo');
  103. $this->hasPets = vmget($_REQUEST,'pets');
  104. $this->payInFull = (bool)vmget($_REQUEST,'pay_in_full');
  105. //print_r($this);
  106. //exit;
  107. if(!$this->setPrice()){
  108. $this->original =
  109. $this->total =
  110. $this->subtotal =
  111. $this->discount =
  112. $this->tax =
  113. $this->tax_rate =
  114. $this->tax_state =
  115. $this->tax_resort =
  116. $this->maxPrice =
  117. $this->discount = 0;
  118. $this->discounts = array();
  119. $this->status = 0;
  120. return false;
  121. }
  122. $this->setPaymentMethod();
  123. //Commit the populated variables to the session
  124. if($saveSession) $this->updateSession();
  125. return $this->status;
  126. }
  127. }
  128. /**
  129. * Updates the session and stores
  130. * this object in a session var
  131. *
  132. */
  133. function updateSession(){
  134. @session_start();
  135. //Set the session var ['booking']['object'] equal to $this;
  136. $_SESSION['booking']['object'] = serialize($this);
  137. $_SESSION['booking']['cart'] = serialize(vmget($_SESSION,'cart',''));
  138. }
  139. function restoreSession(){
  140. @session_start();
  141. //Check if booking array is set
  142. if(!isset($_SESSION['booking'])){
  143. $_SESSION['booking'] = array();
  144. }
  145. //Restore booking session data
  146. if(isset($_SESSION['booking']['object'])) {
  147. $object = unserialize($_SESSION['booking']['object']);
  148. foreach(get_object_vars($this) as $key => $val ){
  149. if($key != 'discountObject') $this->$key = $object->$key;
  150. }
  151. }
  152. //Reset the status
  153. //$this->status = 0;
  154. }
  155. /**
  156. * Purges the session and resets all of $this vars to default
  157. *
  158. */
  159. function removeSession(){
  160. unset($_SESSION['booking']['object']);
  161. $this->reset();
  162. }
  163. /**
  164. * Reset to defaults
  165. *
  166. */
  167. function reset(){
  168. $tmp_bk = new ps_booking();
  169. foreach(get_class_vars((get_class($this))) as $key => $value){
  170. unset($this->$key);
  171. $this->$key = $tmp_bk->$key;
  172. }
  173. }
  174. /**
  175. * Core routing for calculating the base value of a booking
  176. *
  177. */
  178. function setPrice() {
  179. global $perm, $VM_LANG;
  180. //Clear the seasons and nightly prices array first
  181. $this->seasons = array();
  182. $this->nightPrices = array();
  183. $this->getNights();
  184. $this->unixdateFrom = strtotime($this->dateFrom);
  185. $this->unixdateTo = strtotime($this->dateTo);
  186. //Validate the date ranges
  187. if(!$this->validateDates()){
  188. return false;
  189. }
  190. if(vmGet($_REQUEST,'security_deposit',0) !== false && $perm->check( "admin,storeadmin")){
  191. $this->securityDeposit = vmGet($_REQUEST,'security_deposit',0);
  192. }
  193. //read in cleaning charge from property
  194. $this->cleaning = $this->getPropertyField('cleaning_fee') ? $this->getPropertyField('cleaning_fee') : (defined('VB_CLEANING_CHARGE') ? VB_CLEANING_CHARGE : 0);
  195. //Loop through each day and get its associated price
  196. $current_total = $this->total;
  197. $total = 0;
  198. $original = 0;
  199. $discount = 0;
  200. $discounts = array();
  201. for($i = 0; $i < $this->nights; $i++){
  202. //Get the current day in yyyy-mm-dd format
  203. $currentDay = date('Y-m-d', ($this->unixdateFrom) + ($i * 86400));
  204. //Get the nightly price to $currentDay
  205. $nightPrice = $this->getNightlyPrice($currentDay, $i);
  206. if($nightPrice === false){
  207. return false;
  208. }
  209. //Loop through the returned advanced discounts
  210. $nightly_discount = 0;
  211. foreach($nightPrice['discounts'] as $k => $d){
  212. if(!isset($discounts[$k])){
  213. $discounts[$k] = new stdClass;
  214. $discounts[$k]->id = $d->id;
  215. $discounts[$k]->name = $d->name;
  216. $discounts[$k]->value = 0;
  217. }
  218. //Store todays advanced discount
  219. $discounts[$k]->value += $d->value;
  220. $nightly_discount += $d->value;
  221. }
  222. $discount += $nightly_discount;
  223. //Add the returned values to the global counters
  224. $nightPrice['discount'] = $nightly_discount;
  225. $nightPrice['total'] = $nightPrice['total'] - $nightly_discount;
  226. $original += $nightPrice['original'];
  227. $total += $nightPrice['total'];
  228. //Store night prices
  229. $this->nightPrices[$currentDay] = $nightPrice;
  230. }
  231. //Run the total discount routines
  232. $total_discount = 0;
  233. foreach($this->getTotalDiscounts($total) as $k => $d){
  234. if(!isset($discounts[$k])){
  235. $discounts[$k] = new stdClass;
  236. $discounts[$k]->id = $d->id;
  237. $discounts[$k]->name = $d->name;
  238. $discounts[$k]->value = 0;
  239. }
  240. //Store todays advanced discount
  241. $discounts[$k]->value += $d->value;
  242. $total_discount += $d->value;
  243. }
  244. $discount += $total_discount;
  245. ksort($discounts);
  246. //Create the total price, subtract off discounts and save all
  247. $this->discount = $discount;
  248. $this->discounts = $discounts;
  249. $this->extras = $this->extras_details['extras_total'];
  250. /*Organic Mod: This is what you need to change to calculate pets VB_PET_RATE * $this->nights*/
  251. $this->pets = ($this->hasPets && defined('VB_PET_RATE') && VB_PET_RATE) ? ((VB_PET_RATE * $this->nights)*$this->hasPets) : 0;
  252. $this->original = $original;
  253. $this->subtotal = ($original - $this->discount + $this->pets + $this->cleaning);
  254. //Check if the an admin has overridden the price, if so, set the new subtotal and zero the discounts as they no longer apply
  255. if(vmGet($_REQUEST,'use_new_price',0) && $perm->check( "admin,storeadmin")){
  256. //Get the tax rate for this property
  257. $taxes = $this->getTaxRate($this->property_id);
  258. //Set the subtotal
  259. $this->subtotal = vmGet($_REQUEST,'new_total',0) / (1+$taxes->tax_total);
  260. //Zero discounts as they no longer apply
  261. $this->discount = 0;
  262. $this->discounts = array();
  263. }
  264. //Set the tax rates due for this booking
  265. $this->setTaxRates($this->property_id);
  266. //Check that total and subtotal and tax are not negative
  267. if($this->tax < 0) $this->tax = 0;
  268. if($this->subtotal < 0) $this->subtotal = 0;
  269. if($this->total < 0) $this->total = 0;
  270. if($this->free){
  271. $this->tax = 0;
  272. $this->tax_state = 0;
  273. $this->tax_resort = 0;
  274. $this->subtotal = 0;
  275. $this->total = 0;
  276. }
  277. //Get the highest price it could have ever been
  278. $this->setMaxPrice();
  279. $this->status = 1;
  280. //Check if we are updating an order and wether we are displaying errors, if not, show success msg
  281. $order_id = vmGet($_REQUEST,'order_id');
  282. if($this->total != $current_total && $this->display_errors && !$order_id){
  283. $GLOBALS['vmLogger']->info( $VM_LANG->_('VM_BOOKING_STORED') );
  284. }
  285. return true;
  286. }
  287. /**
  288. * This function provides a quick easy way to get a price of a booking
  289. * pass the property id to this funciton
  290. *
  291. * @param int $id = property_id
  292. * @return array()
  293. */
  294. function getPrice($id = 0, $returnTemplate = 1, $displayErrors = 0){
  295. global $CURRENCY_DISPLAY, $CURRENCY, $auth, $VM_LANG;
  296. $VM_LANG->load('booking');
  297. //Always get the property ID
  298. if($id) $this->property_id = $id;
  299. else if(vmGet($_REQUEST,'property_id',0)) $this->property_id = vmGet($_REQUEST,'property_id',0);
  300. $dateFrom = vmGet($_REQUEST,'dateFrom',array());
  301. $dateTo = vmGet($_REQUEST,'dateTo',array());
  302. $GLOBALS['cache_id'] = isset($GLOBALS['cache_id']) ? $GLOBALS['cache_id'] : 'vm_' . @md5( 'bookingGetPrice'. $id. $dateFrom .$dateTo. @$auth["shopper_group_id"]);
  303. $tpl = vmTemplate::getInstance();
  304. //Check the arrival and departure dates
  305. if(!defined('parsedDates')){
  306. if($this->parseDate($dateFrom) == -1){
  307. $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_DATE_ERR_ARR_INV') );
  308. }
  309. if($this->parseDate($dateTo) == -1){
  310. $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_DATE_ERR_DEPT_INV') );
  311. }
  312. define('parsedDates',1);
  313. }
  314. //Check if we have supplied a valid arrival and departure date
  315. if($this->property_id){
  316. //Check the arrival and departure dates are valid
  317. if($this->getDate('dateFrom') && $this->getDate('dateTo')){
  318. //Do the processing to retrieve a price
  319. $this->process(0,$displayErrors);
  320. $values = array('total'=> $this->total,
  321. 'subtotal'=>$this->subtotal,
  322. 'original'=>$this->original,
  323. 'tax'=>$this->tax,
  324. 'discount'=>$this->discount,
  325. 'max'=>$this->maxPrice,
  326. 'total_str'=>$CURRENCY_DISPLAY->getFullValue($this->total),
  327. 'subtotal_str'=>$CURRENCY_DISPLAY->getFullValue($this->subtotal),
  328. 'tax_str'=>$CURRENCY_DISPLAY->getFullValue($this->tax),
  329. 'original_str'=>$CURRENCY_DISPLAY->getFullValue($this->original),
  330. 'discount_str'=>$CURRENCY_DISPLAY->getFullValue($this->discount),
  331. 'max_str'=>$CURRENCY_DISPLAY->getFullValue($this->maxPrice),
  332. );
  333. if(!$returnTemplate){
  334. return $values;
  335. }else{
  336. $tpl->set_vars($values);
  337. return $tpl->fetch( 'booking/booking.quick_price_total.tpl.php' );
  338. }
  339. }else{
  340. //Store the season object in $this season var
  341. if(is_null($this->ps_season)) $this->ps_season = new ps_season();
  342. $bounds = $this->ps_season->getSeasonBounds($this->property_id);
  343. $min = vmGet($bounds,'minimum');
  344. $max = vmGet($bounds,'maximum');
  345. //If we found a price return
  346. if($min && $max && ($min->rate && $max->rate)){
  347. //Get the tax rate for this property
  348. $taxes = $this->getTaxRate();
  349. $tax_rate = 1+($taxes ? $taxes->tax_total : 0);
  350. //Calculate inclusive of tax price
  351. $min->rate_nightly = $min->rate * ($min->per_person_pricing ? $min->min_people : 1);
  352. $max->rate_nightly = $max->rate * ($max->per_person_pricing ? $max->min_people : 1);
  353. $min->rate_weekly = $min->rate_nightly * 7;
  354. $max->rate_weekly = $max->rate_nightly * 7;
  355. $min->rate_gross = $min->rate * $tax_rate;
  356. $min->rate_weekly_gross = round($min->rate_weekly * $tax_rate);
  357. $max->rate_gross = $max->rate * $tax_rate;
  358. $max->rate_weekly_gross = round($max->rate_weekly * $tax_rate);
  359. $min->currency = $CURRENCY_DISPLAY->getFullValue($CURRENCY->convert($min->rate_gross));
  360. $max->currency = $CURRENCY_DISPLAY->getFullValue($CURRENCY->convert($max->rate_gross));
  361. $min->currency_weekly = $CURRENCY_DISPLAY->getFullValue($CURRENCY->convert($min->rate_weekly_gross), 0);
  362. $max->currency_weekly = $CURRENCY_DISPLAY->getFullValue($CURRENCY->convert($max->rate_weekly_gross), 0);
  363. $return = array('min'=>$min,'max'=>$max);
  364. if(!$returnTemplate){
  365. return $return;
  366. }else{
  367. $tpl->set_vars($return);
  368. return $tpl->fetch( 'booking/booking.quick_price_bounds.tpl.php' );
  369. }
  370. }else{
  371. return false;
  372. }
  373. }
  374. }else{
  375. $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_NO_PROP_ERR') );
  376. return false;
  377. }
  378. }
  379. /**
  380. * Sets the maximum price for the date range, use for showing out of season discounts
  381. */
  382. function setMaxPrice(){
  383. //Get the highest price it could have ever been
  384. if(is_null($this->ps_season)) $this->ps_season = new ps_season();
  385. $bounds = $this->ps_season->getSeasonBounds($this->property_id);
  386. if(isset($bounds['maximum'])){
  387. $max = $bounds['maximum'];
  388. $people = $max->min_people > $this->people ? $max->min_people : $this->people;
  389. $this->maxPrice = $max->rate * ($max->per_person_pricing ? $people : 1) * $this->nights;
  390. }
  391. }
  392. function getMaxPrice(){
  393. }
  394. /**
  395. * This function retrieves the tax rate for a particular property
  396. *
  397. */
  398. function getTaxRate(){
  399. $db = new ps_DB();
  400. $db->query("SELECT t.tax_rate, t.tax_resort, (t.tax_rate + t.tax_resort) AS tax_total FROM #__{vm}_property_tax as p, #__{vm}_tax_rate as t
  401. WHERE p.tax_rate_id = t.tax_rate_id
  402. AND p.property_id = $this->property_id");
  403. $db->next_record();
  404. if($db->num_rows()){
  405. return $db->get_row();
  406. }else{
  407. return false;
  408. }
  409. }
  410. /**
  411. * This functions sets the tax rates for $this
  412. *
  413. */
  414. function setTaxRates(){
  415. $taxes = $this->getTaxRate($this->property_id);
  416. if($taxes){
  417. //Calculate the state and resort tax
  418. $this->tax_state = $taxes->tax_rate * $this->subtotal;
  419. $this->tax_resort = $taxes->tax_resort * $this->subtotal;
  420. $this->tax = $this->tax_state + $this->tax_resort;
  421. $this->tax_rate = $taxes->tax_total;
  422. $this->total = (float) $this->subtotal + (float) $this->tax;
  423. if($this->total > 0) {
  424. $this->total += (float) $this->securityDeposit;
  425. }
  426. }else{
  427. //No tax to be applied
  428. $this->total = (float) $this->subtotal;
  429. if($this->total > 0) {
  430. $this->total += (float) $this->securityDeposit;
  431. }
  432. }
  433. }
  434. /**
  435. * This function retrieves the nightly price for a property if one is set,
  436. * if not, it retrieves the global nightly price
  437. *
  438. * @param unknown_type $date
  439. * @return unknown
  440. */
  441. function getNightlyPrice($date, $day_no){
  442. global $VM_LANG;
  443. //Get rate specific to this property if any
  444. $season = $this->getSeason($date, $this->property_id);
  445. $rate = null;
  446. //Get the default pricing calculation
  447. $default_pricing = defined('VB_PER_PERSON_PRICING') ? VB_PER_PERSON_PRICING : 1;
  448. //Set the return variable
  449. $return = array();
  450. //Check if we have a property specific rate
  451. if($season){
  452. $seasonid = $season->f('rate_id');
  453. $rate = $season->f('rate');
  454. }else{
  455. //Get global rate
  456. $season = $this->getSeason($date);
  457. if($season){
  458. $seasonid = $season->f('rate_id');
  459. $rate = $season->f('rate');
  460. }
  461. }
  462. if($rate !== null){
  463. $min_ppl = $season->f('min_people');
  464. $season_discount_value = $season->f('discount');
  465. //Check how we are calculating pricing
  466. $per_person_pricing = $season->f('per_person_pricing') !== null ? $season->f('per_person_pricing') : $default_pricing;
  467. //Check if we are charging per person or per property (0|1)
  468. if($per_person_pricing){
  469. //Work out how many people we are charging
  470. if($this->people < $min_ppl){
  471. $people = $min_ppl;
  472. }else{
  473. $people = $this->people;
  474. }
  475. }else{
  476. $people = 1;
  477. }
  478. //return the rate multiplied by the number of guests
  479. $base_price = $rate * $people;
  480. $total = $base_price;
  481. //Run the advanced discount routines
  482. $discounts = $this->getNightlyDiscounts($seasonid, $season, $date, $day_no, $total);
  483. if($discounts === false){
  484. return false;
  485. }
  486. //Validate that the booking conforms to the season settings
  487. if(!$this->validate_season($season)){
  488. return false;
  489. }
  490. //Store season (simple) discount
  491. if($season_discount_value > 0){
  492. $season_discount = new stdClass();
  493. $season_discount->name = 'Season Discount';
  494. $season_discount->value = ($total * $season_discount_value) / 100; //Calculate the discount
  495. //Add the season discount first
  496. $discounts[-999] = $season_discount;
  497. }
  498. ksort($discounts);
  499. //Store the totals and calculate the overall total
  500. $return['total'] = $total;
  501. $return['original'] = $base_price;
  502. $return['discounts'] = $discounts;
  503. return $return;
  504. }else{
  505. //Error, no seasonal pricing available
  506. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_NO_RPICING_ERR') );
  507. return false;
  508. }
  509. }
  510. /**
  511. * This function will retrieve the season information for a specific date and property
  512. *
  513. * @param unknown_type $date
  514. * @param unknown_type $property_id
  515. * @return unknown
  516. */
  517. function getSeason($date, $property_id = 0){
  518. //Store the season object in $this season var
  519. if(is_null($this->ps_season)) $this->ps_season = new ps_season();
  520. //Get the season from ps_booking_seaons
  521. $season = $this->ps_season->getSeason('', $property_id, $date, '', '', '', 0);
  522. if($season){
  523. if(!array_key_exists($season->f('rate_id'), $this->seasons)) $this->seasons[$season->f('rate_id')] = $season;
  524. }
  525. return $season;
  526. }
  527. /**
  528. * Validates that the information submitted by the user
  529. * meets the minimum and maximum requirements for the given
  530. * season.
  531. *
  532. * @param unknown_type $season
  533. * @return unknown
  534. */
  535. function validate_season($season){
  536. global $perm, $VM_LANG;
  537. $min_ppl_force = $season->f('min_people_force');
  538. $min_ppl = $season->f('min_people');
  539. $max_ppl = $season->f('max_people');
  540. $min_stay = $this->minNights && $this->minNights < $season->f('min_stay') ? $this->minNights : $season->f('min_stay');
  541. $max_stay = $season->f('max_stay');
  542. $changeover = $season->f('changeover_day');
  543. $changeforce = $season->f('changeover_force');
  544. if(vmGet($_REQUEST,'ignore',0) && $perm->check('admin,storeadmin')) $this->validation = array();
  545. //Check if the booking exceeds the min/max values for this season
  546. if(($this->people == 0) && $this->validateCheck('PEOPLE')){
  547. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_MIN_PERSON'));
  548. return false;
  549. }
  550. if(($this->people < $min_ppl && $min_ppl != 0 && $min_ppl_force) && $this->validateCheck('MIN_PEOPLE')){
  551. if($this->display_errors) $GLOBALS['vmLogger']->err( sprintf($VM_LANG->_('VM_BOOKING_MIN_GUESTS'),$min_ppl));
  552. return false;
  553. }
  554. if(($this->people > $max_ppl && $max_ppl != 0) && $this->validateCheck('MAX_PEOPLE')){
  555. if($this->display_errors) $GLOBALS['vmLogger']->err( sprintf($VM_LANG->_('VM_BOOKING_MAX_GUESTS'),$max_ppl));
  556. return false;
  557. }
  558. if(($this->nights < $min_stay && $min_stay != 0) && $this->validateCheck('MIN_NIGHTS')){
  559. if($this->display_errors) $GLOBALS['vmLogger']->err( sprintf($VM_LANG->_('VM_BOOKING_MIN_NIGHTS'),$min_stay));
  560. return false;
  561. }
  562. if(($this->nights > $max_stay && $max_stay != 0) && $this->validateCheck('MAX_NIGHTS')){
  563. if($this->display_errors) $GLOBALS['vmLogger']->err( sprintf($VM_LANG->_('VM_BOOKING_MAX_NIGHTS'),$max_stay));
  564. return false;
  565. }
  566. if($changeover && $changeforce && ($changeover != date('N',$this->unixdateFrom)) && $this->validateCheck('CHANGEOVER')){
  567. $days = array(_DATE_MONDAY, _DATE_TUESDAY, _DATE_WEDNESDAY, _DATE_THURSDAY, _DATE_FRIDAY, _DATE_SATURDAY, _DATE_SUNDAY);
  568. if($this->display_errors) $GLOBALS['vmLogger']->err( sprintf($VM_LANG->_('VM_BOOKING_CHANGEOVER'),$days[$changeover-1]));
  569. return false;
  570. }
  571. if(!$this->property_data->allows_pets && $this->hasPets){
  572. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_PETS') );
  573. return false;
  574. }
  575. return true;
  576. }
  577. /**
  578. * Validation checking function, used in validate_season()
  579. */
  580. function validateCheck($perm){
  581. $perm = strtoupper($perm);
  582. if(isset($this->validation[$perm])) return $this->validation[$perm];
  583. else return false;
  584. }
  585. /**
  586. * Resets the validation data
  587. */
  588. function validationReset(){
  589. $this->validation = array(
  590. 'PEOPLE'=>1,
  591. 'MIN_PEOPLE'=>1,
  592. 'MAX_PEOPLE'=>1,
  593. 'MIN_NIGHTS'=>1,
  594. 'MAX_NIGHTS'=>1,
  595. 'CHANGEOVER'=>1
  596. );
  597. }
  598. /**
  599. * This function will validate that the entered dates are valid and
  600. * that there are no booking between the arrival or departure date *
  601. */
  602. function validateDates(){
  603. global $VM_LANG;
  604. if($this->dateFrom && $this->dateTo){
  605. if($this->unixdateFrom > $this->unixdateTo){
  606. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_DATE_ERR1') );
  607. return false;
  608. }else if($this->unixdateFrom == $this->unixdateTo){
  609. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_DATE_ERR2') );
  610. return false;
  611. }
  612. if(!$this->people){
  613. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_DATE_ERR3') );
  614. return false;
  615. }
  616. $order_id = vmGet($_REQUEST,'order_id');
  617. $db = $this->getBookingsBetweenDates($this->dateFrom, $this->dateTo, $this->property_id, $order_id);
  618. if($db->num_rows() > 0){
  619. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_DATE_ERR4') );
  620. return false;
  621. }
  622. }
  623. return true;
  624. }
  625. function getBookingsBetweenDates($from = '', $to = '', $property_id = '', $order_id =''){
  626. $db = new ps_DB();
  627. $from = $from ? $from : $this->dateFrom;
  628. $to = $to ? $to : $this->dateTo;
  629. if($from == '' || $to == '') return false;
  630. $yesterday = strtotime('-1 day');
  631. $q = "SELECT property_id FROM #__{vm}_order_booking AS booking
  632. LEFT JOIN #__{vm}_orders AS `order` on order.order_id = booking.order_id
  633. WHERE
  634. (
  635. '$from' BETWEEN `arrival` and DATE_SUB(`departure`, INTERVAL 1 DAY)
  636. OR
  637. DATE_SUB('$to', INTERVAL 1 DAY) BETWEEN `arrival` and DATE_SUB(`departure`, INTERVAL 1 DAY)
  638. OR
  639. `arrival` BETWEEN '$from' AND DATE_SUB('$to', INTERVAL 1 DAY)
  640. OR
  641. DATE_SUB(`departure`, INTERVAL 1 DAY) BETWEEN '$from' AND DATE_SUB('$to', INTERVAL 1 DAY)
  642. )"
  643. .($property_id ? " AND booking.property_id = '$property_id'" : '')
  644. .($order_id ? " AND order.order_id != '$order_id'" : '')
  645. ." AND (order.order_status = 'C' OR (order.cdate >= '$yesterday' AND order.order_status = 'P'))";
  646. $db->query($q);
  647. return $db;
  648. }
  649. function checkNights($from, $to, $nights, $unavailable = array()){
  650. $this->unixdateFrom = strtotime($this->dateFrom);
  651. $this->unixdateTo = strtotime($this->dateTo);
  652. $dateFrom = 'IF(r.season_id, s.dateFrom, r.dateFrom)';
  653. $dateTo = 'IF(r.season_id, s.dateTo, r.dateTo)';
  654. $db = new ps_DB();
  655. $q = "SELECT r.rate_id, r.property_id FROM #__{vm}_seasons_rates AS r
  656. LEFT JOIN #__{vm}_seasons AS s ON s.season_id = r.season_id
  657. WHERE
  658. (
  659. $dateFrom BETWEEN '$from' and DATE_SUB('$to', INTERVAL 1 DAY)
  660. OR
  661. $dateTo BETWEEN '$from' AND DATE_SUB('$to', INTERVAL 1 DAY)
  662. OR
  663. '$from' BETWEEN $dateFrom and $dateTo
  664. OR
  665. '$to' BETWEEN $dateFrom and DATE_SUB($dateTo, INTERVAL 1 DAY)
  666. )"
  667. .(count($unavailable) > 0 ? ' AND r.property_id NOT IN ('.implode(',',$unavailable).')' : '');
  668. $this->display_errors = 0;
  669. $this->people = 1;
  670. $arr = array();
  671. $db->query($q);
  672. $cur_prop_id = $this->property_id;
  673. $cur_prop_data = $this->property_data;
  674. while($db->next_record()){
  675. //Reset the validation rules
  676. $this->validationReset();
  677. //Reset property
  678. $this->property_id = $db->f('property_id');
  679. $this->property_data = null;
  680. //Reeset the discount objects
  681. $this->discountObject = new ps_discount($this);
  682. $valid = 1;
  683. for($i = 0; $i < $this->nights; $i++){
  684. //Get the current day in yyyy-mm-dd format
  685. $date = date('Y-m-d', ($this->unixdateFrom) + ($i * 86400));
  686. $this->property_id = $db->f('property_id');
  687. if(!$this->getNightlyPrice($date, $i)){
  688. $valid = 0;
  689. break;
  690. }
  691. }
  692. //Validation passed, add property to allowed list
  693. if($valid){
  694. $arr[] = $db->f('property_id');
  695. }
  696. }
  697. //Reset the property id
  698. $this->property_id = $cur_prop_id;
  699. $this->property_data = $cur_prop_data;
  700. return $arr;
  701. }
  702. /**
  703. * Get number of nights from x date to y date
  704. *
  705. */
  706. function getNights() {
  707. //Count the number of nights
  708. $checkdate = $this->dateFrom;
  709. $this->nights = 0;
  710. while($checkdate < $this->dateTo){
  711. $this->nights++;
  712. //Increment date
  713. $unixNow = strtotime($checkdate);
  714. $checkdate = date('Y-m-d',strtotime('+1 day',$unixNow));
  715. }
  716. return $this->nights;
  717. }
  718. /**
  719. * This function retrieves the discounts for the relevant
  720. * seasons and applies them to the price
  721. *
  722. */
  723. function getNightlyDiscounts($season_id, $season, $date, $day_no, $total){
  724. $discounts = $this->discountObject->getNightlyDiscounts($season_id, $season, $date, $day_no, $total, $this->nights, $this->dateFrom, $this->dateTo, $this);
  725. return $discounts;
  726. }
  727. /**
  728. * This function retrieves the total discount functions
  729. *
  730. */
  731. function getTotalDiscounts($total){
  732. $discounts = $this->discountObject->getTotalDiscounts($total, $this->nights, $this->dateFrom, $this->dateTo, $this);
  733. return $discounts;
  734. }
  735. /**
  736. * Get all extras that are in products table
  737. * NOT property specific at this stage - more money needed!
  738. *
  739. */
  740. function getExtras() {
  741. $ps_product = new ps_product();
  742. $db = new ps_DB();
  743. $db->query('SELECT * FROM #__{vm}_product where product_publish=\'Y\'');
  744. $prods = $db->loadObjectList('product_id');
  745. $quantities = array();
  746. foreach(vmget($_SESSION, 'cart', array()) as $cart){
  747. $product_id = vmget($cart,'product_id','');
  748. if($product_id != ''){
  749. $quantities[$product_id] = vmget($cart,'quantity');
  750. }
  751. }
  752. foreach($prods as &$prod){
  753. $prod->product_quantity = vmget($quantities,$prod->product_id,0);
  754. $prod->price = $ps_product->get_price($prod->product_id);
  755. }
  756. $this->extrasObjects = $prods;
  757. return $prods;
  758. }
  759. function setExtras($q){
  760. global $Itemid, $vmDisplayLogger;
  761. $ps_cart = new ps_cart();
  762. $extras_total = array('extras_total'=>0);
  763. if(!$q) return $extras_total;
  764. //Get the existing product quantities from the session
  765. $quantities = array();
  766. $cart = vmget($_SESSION,'cart',array());
  767. foreach($cart as $c){
  768. $product_id = vmget($c,'product_id','');
  769. if($product_id != ''){
  770. $quantities[$product_id] = vmget($c,'quantity');
  771. }
  772. }
  773. //Create a new $d array to update quantities if theyve changed
  774. $quantity = array();
  775. $prod_ids = array();
  776. foreach($q as $k => $v){
  777. $qty = vmget($quantities,$k,null);
  778. if(($qty === null && $v > 0) || ($qty !== null && $v ==0) || ($qty !== null && $v != $qty)){
  779. $quantity[] = $v;
  780. $prod_ids[] = $k;
  781. }
  782. }
  783. $d = array();
  784. $d['product_id'] = 1;
  785. $d['prod_id'] = $prod_ids;
  786. $d['quantity'] = $quantity;
  787. $d['category_id'] = '';
  788. $d['page'] = 'shop.cart';
  789. $d['func'] = 'cartAdd';
  790. $d['option'] = 'com_virtuemart';
  791. $d['Itemid'] = $Itemid;
  792. $d['set_price'] = array('');
  793. $d['adjust_price'] = array('');
  794. $d['master_product'] = array('');
  795. //Add products to cart
  796. if(count($prod_ids) > 0){
  797. $ps_cart->add($d);
  798. }
  799. //Get the extras objects
  800. $extras = $this->getExtras();
  801. //Save extras
  802. foreach(vmget($_SESSION, 'cart', array()) as $cart){
  803. $product_id = vmget($cart,'product_id','');
  804. $product_quantity = vmget($cart,'quantity','');
  805. if($product_id){
  806. $extra = $extras[$product_id];
  807. $total = $extra->price['product_price'] * $product_quantity;
  808. $extras_total['extras_total'] += $total;
  809. $extras_total[$product_id] = $extra->price;
  810. }
  811. }
  812. return $extras_total;
  813. }
  814. /**
  815. * Loads the booking data from an existing booking and populates $this;
  816. *
  817. * @param unknown_type $id
  818. */
  819. function loadBookingData($id){
  820. $db = new ps_DB();
  821. $db->query("SELECT * FROM #__{vm}_orders AS o
  822. LEFT JOIN #__{vm}_order_booking AS b ON b.order_id = o.order_id
  823. LEFT JOIN #__{vm}_order_history AS h ON b.order_id = h.order_id
  824. WHERE o.order_id=$id
  825. ORDER BY h.date_added DESC
  826. LIMIT 0, 1");
  827. $db->next_record();
  828. if(!$db->f('order_id')) return false;
  829. if(!$db->f('booking_serialized') || !$this->__unserialize($db->f('booking_serialized'))){
  830. //Set all the booking variables
  831. $this->booking_id = $db->f('order_id');
  832. $this->dateFrom = $db->f('arrival');
  833. $this->dateTo = $db->f('departure');
  834. $this->dateBooked = date('Y-m-d',$db->f('cdate'));
  835. $this->unixdateFrom = strtotime($this->dateFrom);
  836. $this->unixdateTo = strtotime($this->dateTo);
  837. $this->unixdateBooked = strtotime($this->dateBooked);
  838. $this->total = $db->f('total');
  839. $this->subtotal = $db->f('subtotal');
  840. $this->original = $db->f('original');
  841. $this->tax = $db->f('tax_total');
  842. $this->discount = $db->f('discounts');
  843. $this->adv_discount = unserialize($db->f('discount_details'));
  844. $this->getNights();
  845. }else{
  846. //Restore serialized booking
  847. $ps_booking = $this->__unserialize($db->f('booking_serialized'));
  848. foreach(get_class_vars(get_class($ps_booking)) as $key => $value){
  849. $this->$key = $ps_booking->$key;
  850. }
  851. $this->dateFrom = $db->f('arrival');
  852. $this->dateTo = $db->f('departure');
  853. $this->dateBooked = date('Y-m-d',$db->f('cdate'));
  854. $this->unixdateFrom = strtotime($this->dateFrom);
  855. $this->unixdateTo = strtotime($this->dateTo);
  856. $this->unixdateBooked = strtotime($this->dateBooked);
  857. }
  858. $this->booking_id = $id;
  859. }
  860. /**
  861. * Get the available properties between two dates
  862. *
  863. */
  864. function getAvailableProperties($from = '', $to = ''){
  865. if(!$from) $from = $this->getDate('dateFrom');
  866. if(!$to) $to = $this->getDate('dateTo');
  867. if(!$from && !$to) return false;
  868. $this->getNights();
  869. $db = $this->getBookingsBetweenDates($from, $to);
  870. $unavailable = array();
  871. if(is_object($db) && $db->num_rows()){
  872. while($db->next_record()){
  873. $unavailable[] = $db->f('property_id');
  874. }
  875. }
  876. $db = new ps_DB();
  877. $db->query("SELECT id FROM #__hp_properties ".(count($unavailable) ? "WHERE id NOT IN (".implode(',',$unavailable).")" : ''));
  878. $available = array();
  879. if(is_object($db) && $db->num_rows()){
  880. while($db->next_record()){
  881. $available[] = $db->f('id');
  882. }
  883. }
  884. $min_nights = $this->checkNights($from, $to, $this->nights, $unavailable);
  885. $properties = array_intersect($available, $min_nights);
  886. return $properties;
  887. }
  888. /**
  889. * Unserialize
  890. *
  891. * @param unknown_type $sObject
  892. * @return unknown
  893. */
  894. function __unserialize($sObject) {
  895. $__ret =preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $sObject );
  896. return unserialize($__ret);
  897. }
  898. /**
  899. * This function retrieves the current stage payment
  900. * upon the rules defined in global config
  901. */
  902. function getStagePayment($order_total = '', $payment_method_id = 0){
  903. $order_total = $order_total ? $order_total : $this->total;
  904. //INclude payments functions
  905. require_once(CLASSPATH.'ps_payments.php');
  906. $ps_payments = new ps_payments();
  907. $stage_payments = $ps_payments->getStagePayments($order_total, null, null, $this, $payment_method_id);
  908. return $stage_payments;
  909. }
  910. /**
  911. * Function to retrieve VM error messages
  912. *
  913. * @return unknown
  914. */
  915. function getMessages($html = 1, $priority = null){
  916. if($html){
  917. ob_start();
  918. $GLOBALS['vmDisplayLogger']->printLog($priority);
  919. $return = ob_get_contents();
  920. ob_end_clean();
  921. return $return;
  922. }else{
  923. $log = $GLOBALS['vmDisplayLogger'];
  924. $i = 0;
  925. $output = '';
  926. foreach( $log->_messages as $message ) {
  927. if( ( $priority === null || $priority <= $message['priority'] )
  928. && $message['priority'] !== PEAR_LOG_DEBUG
  929. || ( $message['priority'] === PEAR_LOG_DEBUG && DEBUG == '1')) {
  930. $output .= ucfirst($log->priorityToString($message['priority'])) . ': '
  931. . htmlspecialchars($message['message'])
  932. . "\n";
  933. }
  934. $i++;
  935. }
  936. return $output;
  937. }
  938. }
  939. /**
  940. * Set dates function, this sets the arrival and departure dates
  941. *
  942. * @return unknown
  943. */
  944. function setDates(){
  945. global $VM_LANG;
  946. if(!$this->getDate('dateFrom')){
  947. $GLOBALS['vmLogger']->err($VM_LANG->_("VM_BOOKING_DATE_ERR_ARR_EMPTY"));
  948. $this->status = 0;
  949. return false;
  950. }
  951. if(!$this->getDate('dateTo')){
  952. $GLOBALS['vmLogger']->err($VM_LANG->_("VM_BOOKING_DATE_ERR_DEPT_EMPTY"));
  953. $this->status = 0;
  954. return false;
  955. }
  956. return true;
  957. }
  958. /**
  959. * This function retrieves the date from the request array
  960. *
  961. * @param unknown_type $reqName
  962. */
  963. function getDate($reqName){
  964. if(isset($_REQUEST[$reqName])){
  965. $dateFromReq = $this->parseDate(vmget($_REQUEST,$reqName));
  966. //Check if empty values were passed
  967. if($dateFromReq === 0){
  968. $this->$reqName = null;
  969. }else if($dateFromReq !== -1){
  970. $this->$reqName = $dateFromReq;
  971. }
  972. }
  973. return isset($this->$reqName) ? $this->$reqName : false;
  974. }
  975. /**
  976. * Parses a date array from the $_REQUEST array
  977. * into a mysql date stamp
  978. *
  979. */
  980. function parseDate($arr){
  981. if(is_array($arr)){
  982. $d = vmGet($arr,'d',false);
  983. $m = vmGet($arr,'m',false);
  984. $y = vmGet($arr,'y',false);
  985. //Check if values were passed
  986. if(!$d || !$m || !$y){
  987. return 0;
  988. }
  989. //Check if date valriables are present and that they're valid
  990. if(!isset($arr['d']) || !isset($arr['m']) || !isset($arr['y']) || !checkdate($m, $d, $y)){
  991. return -1;
  992. }
  993. //Return a full mysql date
  994. return date('Y-m-d', mktime(0,0,0, $m, $d, $y));
  995. }else{
  996. if($arr){
  997. return $arr;
  998. }else{
  999. return 0;
  1000. }
  1001. }
  1002. }
  1003. /**
  1004. * Check if the person booking the property is the owner
  1005. *
  1006. */
  1007. function isOwner() {
  1008. global $my;
  1009. $db = new ps_DB();
  1010. if($my->id){
  1011. $owner = $this->getOwner();
  1012. return $owner->f('user') == $my->id;
  1013. }else{
  1014. return 0;
  1015. }
  1016. }
  1017. function getOwner($id = 0){
  1018. global $my;
  1019. if(!$id) $id = $this->property_id;
  1020. $db = new ps_DB();
  1021. //Check for owner booking
  1022. $q = "SELECT a.* from #__hp_properties as p
  1023. LEFT JOIN #__hp_agents as a ON p.agent = a.id
  1024. WHERE p.id = $id";
  1025. $db->query($q);
  1026. return $db;
  1027. }
  1028. /**
  1029. * Check if the person booking the property is an admin
  1030. *
  1031. */
  1032. function isAdmin() {
  1033. global $my;
  1034. //return in_array($my->usertype, array(23,24,25));
  1035. return in_array($my->usertype, array('Super Administrator','Administrator','Manager'));
  1036. }
  1037. /**
  1038. * Check if the an admin chose to make the booking free
  1039. *
  1040. */
  1041. function isFree() {
  1042. global $my;
  1043. $makeFree = vmget($_REQUEST,'make_free',0);
  1044. if(($this->admin && $makeFree == 1) || ($this->owner && $makeFree == 1)){
  1045. return 1;
  1046. }else if($makeFree == -1){
  1047. return 0;
  1048. }else{
  1049. return 0;
  1050. }
  1051. }
  1052. /**
  1053. * Get the property data relating to this booking
  1054. *
  1055. */
  1056. function getPropertyData() {
  1057. global $VM_LANG;
  1058. $db = new ps_DB();
  1059. # Load property data
  1060. $q = "SELECT p.*, t.thumb, t.ordering FROM #__hp_properties as p
  1061. LEFT JOIN #__hp_photos as t ON p.id = t.property
  1062. WHERE p.id = $this->property_id
  1063. ORDER BY t.ordering
  1064. Limit 0, 1";
  1065. $db->query($q);
  1066. if(!$db->next_record()){
  1067. if($this->display_errors) $GLOBALS['vmLogger']->err( $VM_LANG->_('VM_BOOKING_PROP_ERR') );
  1068. $this->property_data = null;
  1069. return false;
  1070. }else{
  1071. $this->property_data = $db->get_row();
  1072. $this->property_data->allows_pets = $this->getPropertyField('pets');
  1073. }
  1074. return $this->property_data;
  1075. }
  1076. function getPropertyField($field){
  1077. $db = new ps_DB();
  1078. $db->query("SELECT v.value FROM #__hp_properties2 AS v
  1079. LEFT JOIN #__hp_prop_ef AS ef ON ef.id = v.field
  1080. WHERE ef.name = '$field' AND v.property = $this->property_id");
  1081. $db->next_record();
  1082. return strtolower($db->f('value')) == 'yes' ? 1 : (strtolower($db->f('value')) == 'no' ? 0 : $db->f('value'));
  1083. }
  1084. /**
  1085. * Returns special offer data for a particular property
  1086. *
  1087. */
  1088. function getPropertySpecial($id){
  1089. if($id){
  1090. //Store the season object in $this season var
  1091. if(is_null($this->ps_season)) $this->ps_season = new ps_season();
  1092. $this->property_id = $id;
  1093. return $this->ps_season->getSeasonDiscount($id);
  1094. }else{
  1095. return false;
  1096. }
  1097. }
  1098. /**
  1099. * Retrieve and set the payment method if there is
  1100. * one relating to the property
  1101. *
  1102. */
  1103. function setPaymentMethod(){
  1104. $db = new ps_DB();
  1105. //Check for owner booking
  1106. $q = "SELECT merchant_id from #__hp_payments WHERE property_id = '$this->property_id' AND enable = 1";
  1107. $db->query($q);
  1108. if($db->num_rows() > 0){
  1109. $this->payment_id = $db->f('merchant_id');
  1110. $_SESSION['booking']['payment'] = $this->payment_id;
  1111. }else{
  1112. $this->payment_id = '';
  1113. $_SESSION['booking']['payment'] = $this->payment_id;
  1114. }
  1115. }
  1116. /**
  1117. * Retrieve booking value minus agent commission
  1118. *
  1119. */
  1120. function getBookingMinusCommission($value) {
  1121. if (isset($value) && defined('VB_AGENT_COMMISSION_TYPE') && defined('VB_AGENT_COMMISSION')) {
  1122. if (VB_AGENT_COMMISSION_TYPE=='pc') { //commission is a percentage
  1123. $value = ($value - (($value/100)*intval(VB_AGENT_COMMISSION)));
  1124. } else { //commission is a fixed fee
  1125. $value = ($value - intval(VB_AGENT_COMMISSION));
  1126. }
  1127. } else {
  1128. $value = intval(0);
  1129. }
  1130. return $value > 0 ? $value : 0;
  1131. }
  1132. }
  1133. ?>