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);
 }