function doAllocate() { // query all (bed-level) resources available with no allocations for // any of the dates given ordered by resource_id $availResourceIds = AllocationDBO::fetchAvailableBeds($this->resourceId, $this->reqRoomType, $this->bookingDates, $this->resourceProps); // loop thru $existingAllocationRows removing those resources already present AND // where there is *any* overlap in dates foreach ($this->existingAllocationRows as $row) { if ($row->isExistsBookingForAnyDate($this - bookingDates)) { // remove from $availResourceIds if any of the booking dates overlap if (($key = array_search($row->resourceId, $availResourceIds)) !== false) { unset($availResourceIds[$key]); } } } // of those that remain; find first available where they belong to the same room // and #available in room <= sum($numF, $numM, $numX) // this will be sorted first $availResourceIds = sortFirstResourcesThatWillFitCurrentAllocation($availResourceIds); // now we go through newAllocationRows and assign resourceIds in turn foreach ($availResourceIds as $bedResourceId) { foreach ($this->newAllocationRows as $row) { if ($row->resourceId == $this->resourceId) { $row->resourceId = $bedResourceId; break; } } } }
/** * Toggles the checkout state of the given allocation and position in allocationCells. * $allocationId : id of allocation * $posn : position within allocationCells to toggle (multiple checkout dates may be possible for any given allocation) */ function toggleCheckoutOnBookingDate($allocationId, $posn) { // find out the date that was toggled by adding the offset to minDate $checkoutDate = clone $this->showMinDate; $checkoutDate->add(new DateInterval('P' . $posn . 'D')); AllocationDBO::toggleCheckoutOnBookingDate($allocationId, $checkoutDate); }
/** * Runs the search by allocation grouped by resource. * Saves the result in the current object. */ function doSearch() { $bookingResources = AllocationDBO::getAllocationsByResourceForDateRange($this->showMinDate, $this->showMaxDate, $this->resourceId, null, null); if (sizeof($bookingResources) != 1) { error_log("Unexpected number of items in AllocationViewResource::doSearch() "); foreach ($bookingResources as $br) { error_log("Found BookingResource {$br->resourceId}"); } } if (sizeof($bookingResources) >= 0) { $this->bookingResource = array_shift($bookingResources); } else { $this->bookingResource = null; } }
/** * Returns a new BookingSummary object for the given booking id. * $bookingId : id of booking to get * Returns non-null object. Throws DatabaseException if not found. */ static function fetchBookingSummaryForId($bookingId) { global $wpdb; $resultset = $wpdb->get_results($wpdb->prepare("SELECT bk.booking_id, bk.firstname, bk.lastname, bk.referrer, bk.created_by, bk.created_date\r\n FROM " . $wpdb->prefix . "booking bk\r\n WHERE bk.booking_id = %d", $bookingId)); if ($wpdb->last_error) { error_log("Failed to execute query " . $wpdb->last_query); throw new DatabaseException($wpdb->last_error); } foreach ($resultset as $res) { $summary = new BookingSummary($res->booking_id, $res->firstname, $res->lastname, $res->referrer, $res->created_by, new DateTime($res->created_date)); $summary->guests = AllocationDBO::fetchGuestNamesForBookingId($res->booking_id); $summary->statuses = AllocationDBO::fetchStatusesForBookingId($res->booking_id); $summary->resources = ResourceDBO::fetchResourcesForBookingId($res->booking_id); $summary->comments = self::fetchBookingComments($res->booking_id, BookingComment::COMMENT_TYPE_USER); $summary->bookingDates = AllocationDBO::fetchDatesForBookingId($res->booking_id); $summary->isCheckoutAllowed = AllocationDBO::isCheckoutAllowedForBookingId($res->booking_id); return $summary; } throw new DatabaseException("Booking ID {$bookingId} not found!"); }
/** * Saves current allocation to the db. * $mysqli : manual db connection (for transaction handling) * $bookingId : booking id for this allocation * Returns allocation id of newly created record * Throws AllocationException on resource conflict */ function save($mysqli, $bookingId) { global $wpdb; // create the allocation if it doesn't exist if ($this->id == 0) { $allocationId = AllocationDBO::insertAllocation($mysqli, $bookingId, $this->resourceId, $this->name, $this->gender, $this->reqRoomSize, $this->reqRoomType); error_log("inserted allocation {$allocationId}"); // then create the booking dates for the allocation AllocationDBO::insertBookingDates($mysqli, $allocationId, $this->bookingDates); } else { // update the existing allocation AllocationDBO::updateAllocation($mysqli, $this->id, $this->resourceId, $this->name, $this->gender, $this->resourceMap); error_log("updating allocation {$this->id}"); // clear out those with blank statuses (these are dates now marked as 'available') foreach ($this->bookingDates as $dt => $bookingDate) { if ($bookingDate->status == '') { unset($this->bookingDates[$dt]); } } // diff existing booking dates with the ones we want to save AllocationDBO::mergeUpdateBookingDates($mysqli, $this->id, $this->bookingDates); } return $allocationId; }
public function testCancellationNoShowInDormStillBookable() { self::createTestBooking("NoShowFemales", "FemalesInMixedDorm", array("M" => 0, "F" => 2, "X" => 0), 132, "10", null, array('09.04.2014', '10.04.2014'), array()); // $resourceProps // verify derived room type for the given dates $resourceIdDateRoomToRoomType = AllocationDBO::getDerivedRoomTypesForDates(DateTime::createFromFormat('!d.m.Y', "09.04.2014"), DateTime::createFromFormat('!d.m.Y', "10.04.2014")); $this->assertEquals("X", $resourceIdDateRoomToRoomType[132]["09.04.2014"], "mixed for 09.04.2014"); $this->assertEquals("FX", $resourceIdDateRoomToRoomType[132]["10.04.2014"], "female/mixed for 10.04.2014"); $freeBedIds = AllocationDBO::fetchAvailableBeds(132, 1, "X", array('09.04.2014', '10.04.2014'), array(), array()); // $resourceProps $this->assertEquals(6, sizeof($freeBedIds), "Expecting 6 free beds in Snow White room"); // find the booking that was just created $bookingSummaryArr = BookingDBO::getBookingsForDateRange(DateTime::createFromFormat('!d.m.Y', '09.04.2014'), DateTime::createFromFormat('!d.m.Y', '09.04.2014'), 'checkin', null, null, 'FemalesInMixedDorm'); $this->assertEquals(1, sizeof($bookingSummaryArr), "Expecting 1 booking created"); // verify booking summary query brings back the saved values $bookingSummary = array_shift(array_values($bookingSummaryArr)); // load existing booking $booking = new AddBooking(); $booking->load($bookingSummary->id); $allocationArr = $this->queryByXPath('/editbooking/allocations/allocation[name="NoShowFemales-1"]', $booking->toXml()); $this->assertEquals(1, sizeof($allocationArr), "expecting 1 allocation with name 'NoShowFemales-1'"); $allocation = array_shift($allocationArr); // toggle until cancelled $rowid = (string) $allocation->rowid; $booking->toggleBookingStateAt($rowid, "09.04.2014"); $booking->toggleBookingStateAt($rowid, "09.04.2014"); $booking->toggleBookingStateAt($rowid, "09.04.2014"); $state = $booking->toggleBookingStateAt($rowid, "09.04.2014"); $this->assertEquals("cancelled", $state, "expecting state on 09.04.2014 to be 'cancelled'"); $booking->toggleBookingStateAt($rowid, "10.04.2014"); $booking->toggleBookingStateAt($rowid, "10.04.2014"); $booking->toggleBookingStateAt($rowid, "10.04.2014"); $state = $booking->toggleBookingStateAt($rowid, "10.04.2014"); $this->assertEquals("cancelled", $state, "expecting state on 10.04.2014 to be 'cancelled'"); $booking->save(); // i should be able to fit 9 more people on the 10/11 of April self::createTestBooking("FillUpTheRoom", "WithMaxOccupants", array("M" => 5, "F" => 4, "X" => 0), 132, "10", null, array('10.04.2014', '11.04.2014'), array()); // $resourceProps // verify derived room type for the given dates $resourceIdDateRoomToRoomType = AllocationDBO::getDerivedRoomTypesForDates(DateTime::createFromFormat('!d.m.Y', "10.04.2014"), DateTime::createFromFormat('!d.m.Y', "11.04.2014")); $this->assertEquals("X", $resourceIdDateRoomToRoomType[132]["10.04.2014"], "mixed for 10.04.2014"); $this->assertEquals("X", $resourceIdDateRoomToRoomType[132]["11.04.2014"], "mixed for 11.04.2014"); }
/** * Loads current allocations from the db and resets the min/max dates to default. * $bookingId : booking id for this allocation */ function load($bookingId) { $this->allocationRows = AllocationDBO::fetchAllocationRowsForBookingId($bookingId, $this->resourceMap); $this->setDefaultMinMaxDates(); }
/** * Assigns all allocation rows matching $resourceId with a specific leaf-level resource (bed) * based on current availability, requested room type and existing allocations. * $resourceId : id of (parent) resource to update * $reqRoomType : requested room type (M/F/X/MX/FX) * $bookingDates : array() of booking dates in format d.M.y * $resourceProps : array of resource property ids (allocate only to resources with these properties) * $allocationRows : array() of current AllocationRows * $newAllocations : array() of uncommitted allocation rows. resourceIds that are assigned to a parent resource * will be allocated to a leaf-level resources if possible. Those unable to be allocated * will have their isAvailable flag set to false. */ function allocateResources($resourceId, $reqRoomType, $bookingDates, $resourceProps, &$allocationRows, &$newAllocations) { // collect all dates for all allocations sharing this resourceId // (we should only need to check one as they *should* all be the same but just in case) $numUnallocated = $this->getNumAllocationsMatchingResource($alloc->resourceId, $reqRoomType, $newAllocations); error_log("number unallocated: {$numUnallocated}"); $bookingDates = $this->collectDatesWithResourceId($alloc->resourceId, $reqRoomType, $newAllocations); error_log("after collectDatesWithResourceId: " . $numUnallocated . " bookingDates : " . sizeof($bookingDates)); // check that the user didn't do anything stupid like try to assign two people into the same bed if ($numUnallocated > 1 && $alloc->resourceId != null && $this->resourceMap[$alloc->resourceId]->resource_type == 'bed') { throw new AllocationException("You cannot assign more than one person into the same bed!"); } // don't include those we've already allocated but not yet committed $excludedResourceIds = $this->getLeafResourceIdsWithOverlappingDates($bookingDates, $allocationRows); // find all beds that can be assigned for the given requested room type and dates $bedResourceIds = AllocationDBO::fetchAvailableBeds($resourceId, $numUnallocated, $reqRoomType, $bookingDates, $excludedResourceIds, $resourceProps); $this->allocateBeds($resourceId, $bedResourceIds, $newAllocations); // if we didn't have enough availability to allocate based on reqRoomType, try and allocate just based on gender /* should have been handled above if (sizeof($bedResourceIds) < $numUnallocated) { $numUnallocated = $this->getNumAllocationsMatchingResource($resourceId, $reqRoomType, $newAllocations); if ($reqRoomType == 'X') { $bedResourceIds = AllocationDBO::fetchAvailableBeds($resourceId, $numUnallocated, $this->inferRoomTypes($resourceId, $newAllocations), $bookingDates, $excludedResourceIds, $this->getResourceIds($allocationRows), $resourceProps); } else if ($reqRoomType == 'M') { $bedResourceIds = AllocationDBO::fetchAvailableBeds($resourceId, $numUnallocated, array('M', 'MX'), $bookingDates, $excludedResourceIds, $this->getResourceIds($allocationRows), $resourceProps); } else if ($reqRoomType == 'F') { $bedResourceIds = AllocationDBO::fetchAvailableBeds($resourceId, $numUnallocated, array('F', 'FX'), $bookingDates, $excludedResourceIds, $this->getResourceIds($allocationRows), $resourceProps); } $this->allocateBeds($resourceId, $bedResourceIds, $newAllocations); } */ // no more availability, ... add to "overflow" group // if (sizeof($bedResourceIds) < $numUnallocated) { // $this->allocateBeds($resourceId, $allocationRows, array(ResourceDBO::OVERFLOW_RESOURCE_ID)); // } /* // update availability subtracting those that have been allocated but not committed foreach ($existingAllocationRows as $ar) { foreach ($rsAvailability as $key => $rowAvail) { if ($rowAvail->resource_id == $ar->resourceId) { unset($rsAvailability[$key]); } } } // we may have a situation where the user has some uncommitted allocations // in a new room, or where previous (uncommitted) allocations may have changed // a room from MX to M or from FX to F // (male/mixed to mixed with addition of a F or female/mixed to mixed with addition of a M) // so adding new allocations may bring the room into an invalid M/F state // ignore this for now; this will be validated again at the db level when saving // count the number of available beds per room $bedCounts = array(); // array() of bed resource id[] indexed by room resource ids $resourceIds = array(); // all available bed resource ids foreach ($rsAvailability as $rowAvail) { if (unset($bedCounts[$rowAvail->parent_resource_id])) { $bedCounts[$rowAvail->parent_resource_id] = array(); } $bedCounts[$rowAvail->parent_resource_id][] = $rowAvail->resource_id; $resourceIds[] = $rowAvail->resource_id; } // find resource ids where all can fit in the same room $roomTypes = AllocationDBO::fetchRoomTypes($resourceId, $bookingDates); foreach ($bedCounts as $parentResId => $bedIds) { if (sizeof($bedIds) >= $numGuests && // do not try to stuff a M/F allocation request into a mixed room at this point (false === isset($roomTypes[$parentResId]) || ($roomTypes[$parentResId] != 'MX' && $roomTypes[$parentResId] != 'FX'))) { // if we have enough free beds in the room, use these resources $resourceIds = $bedIds; break; } } */ //$this->inferRoomType($resourceId, $allocationRows) // $this->doAssignAllocations($allocationRows, $resourceId, $resourceIds); }