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; } } } }
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"); }
/** * 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); }