blocked_period.php
1 year ago
booked_period.php
1 year ago
booking_request.php
1 year ago
booking_resource.php
1 year ago
booking_slot.php
1 year ago
filter.php
1 year ago
process_action.php
1 year ago
process_event.php
1 year ago
role.php
1 year ago
router.php
1 year ago
step.php
1 year ago
stripe_connect_customer.php
1 year ago
time_period.php
1 year ago
user.php
1 year ago
work_period.php
1 year ago
booking_resource.php
233 lines
| 1 | <?php |
| 2 | /* |
| 3 | * Copyright (c) 2022 LatePoint LLC. All rights reserved. |
| 4 | */ |
| 5 | |
| 6 | namespace LatePoint\Misc; |
| 7 | |
| 8 | class BookingResource{ |
| 9 | public int $agent_id; |
| 10 | public int $service_id; |
| 11 | public int $location_id; |
| 12 | public int $max_capacity; |
| 13 | public int $min_capacity; |
| 14 | public int $min_capacity_to_be_blocked; |
| 15 | public int $timeblock_interval; |
| 16 | public string $date; |
| 17 | public array $work_time_periods = []; |
| 18 | public array $booked_time_periods = []; |
| 19 | // time periods that are usually part of work periods but a blocked now for some reason(time past today), |
| 20 | // it's different from booked periods, because they have no bookings |
| 21 | public array $blocked_time_periods = []; |
| 22 | public array $slots = []; |
| 23 | |
| 24 | |
| 25 | public array $bookable_minutes = []; |
| 26 | public array $work_minutes = []; |
| 27 | |
| 28 | function __construct($args = []){ |
| 29 | $allowed_props = self::allowed_props(); |
| 30 | foreach($args as $key => $arg){ |
| 31 | if(in_array($key, $allowed_props)) $this->$key = $arg; |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | /** |
| 36 | * @return \OsAgentModel |
| 37 | */ |
| 38 | function get_agent(): \OsAgentModel{ |
| 39 | return ($this->agent_id) ? new \OsAgentModel($this->agent_id) : new \OsAgentModel(); |
| 40 | } |
| 41 | |
| 42 | protected function retrieve_service_data(){ |
| 43 | $service = new \OsServiceModel($this->service_id); |
| 44 | $this->max_capacity = $service->capacity_max; |
| 45 | $this->timeblock_interval = $service->timeblock_interval ? intval($service->timeblock_interval) : \OsSettingsHelper::get_default_timeblock_interval(); |
| 46 | $this->min_capacity_to_be_blocked = $service->get_capacity_needed_before_slot_is_blocked(); |
| 47 | } |
| 48 | |
| 49 | |
| 50 | public function get_timeblock_interval(){ |
| 51 | if(isset($this->timeblock_interval)){ |
| 52 | return $this->timeblock_interval; |
| 53 | }else{ |
| 54 | $this->retrieve_service_data(); |
| 55 | return $this->timeblock_interval; |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | |
| 60 | public function get_min_capacity_to_be_blocked(){ |
| 61 | if(isset($this->min_capacity_to_be_blocked)){ |
| 62 | return $this->min_capacity_to_be_blocked; |
| 63 | }else{ |
| 64 | $this->retrieve_service_data(); |
| 65 | return $this->min_capacity_to_be_blocked; |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | public function get_max_capacity(){ |
| 70 | if(isset($this->max_capacity)){ |
| 71 | return $this->max_capacity; |
| 72 | }else{ |
| 73 | $this->retrieve_service_data(); |
| 74 | return $this->max_capacity; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * @param BookingRequest $booking_request |
| 80 | * @param int $selectable_time_interval |
| 81 | * @return void |
| 82 | */ |
| 83 | public function build_bookable_slots(BookingRequest $booking_request, int $selectable_time_interval){ |
| 84 | $this->slots = []; |
| 85 | $this->work_minutes = []; |
| 86 | foreach($this->work_time_periods as $time_period){ |
| 87 | for($minute = $time_period->start_time; $minute <= $time_period->end_time - $booking_request->duration; $minute+= $selectable_time_interval){ |
| 88 | $period = new TimePeriod(['start_time' => $minute - $booking_request->buffer_before, 'end_time' => $minute + $booking_request->duration + $booking_request->buffer_after]); |
| 89 | $this->work_minutes[] = $minute; |
| 90 | // add booking slot |
| 91 | $slot = new BookingSlot(); |
| 92 | $slot->start_date = $this->date; |
| 93 | $slot->start_time = $minute; |
| 94 | $slot->max_capacity = $this->get_max_capacity(); |
| 95 | $slot->min_capacity_to_be_blocked = $this->get_min_capacity_to_be_blocked(); |
| 96 | |
| 97 | // --------------------- |
| 98 | // LOGIC FOR "BOOKED" PERIODS |
| 99 | // --------------------- |
| 100 | foreach($this->booked_time_periods as $booked_period){ |
| 101 | if ($booked_period->start_time_with_buffer() >= $period->end_time || $booked_period->end_time_with_buffer() <= $period->start_time) { |
| 102 | // not intersected |
| 103 | }else{ |
| 104 | // intersects with a booked period, disqualify it |
| 105 | if($booked_period->service_id != $this->service_id){ |
| 106 | // if it's a different service being performed - block full capacity of a slot, because you can't share capacities between different services |
| 107 | $slot->booked_capacity = $this->max_capacity; |
| 108 | }else{ |
| 109 | $slot->booked_capacity+= $booked_period->total_attendees; |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | // --------------------- |
| 114 | // LOGIC FOR "BLOCKED" PERIODS |
| 115 | // --------------------- |
| 116 | foreach($this->blocked_time_periods as $blocked_period){ |
| 117 | if ($blocked_period->start_time >= $period->end_time || $blocked_period->end_time <= $period->start_time) { |
| 118 | // not intersected |
| 119 | }else{ |
| 120 | // intersects with a blocked period, disqualify it |
| 121 | $slot->booked_capacity = $slot->max_capacity; |
| 122 | } |
| 123 | } |
| 124 | $this->slots[] = $slot; |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | // Function to print the intersection |
| 130 | private function find_intersection(array $time_periods){ |
| 131 | // First interval |
| 132 | $start = $time_periods[0]->start_time; |
| 133 | $end = $time_periods[0]->end_time; |
| 134 | // Check rest of the intervals and find the intersection |
| 135 | $total = count($time_periods); |
| 136 | for ($i = 1; $i < $total; $i++){ |
| 137 | // If no intersection exists |
| 138 | if ($time_periods[$i]->start_time > $end || $time_periods[$i]->end_time < $start){ |
| 139 | return []; |
| 140 | }else{ |
| 141 | // Else update the intersection |
| 142 | $start = max($start, $time_periods[$i]->start_time); |
| 143 | $end = min($end, $time_periods[$i]->end_time); |
| 144 | } |
| 145 | } |
| 146 | return new TimePeriod(['start_time' => $start, 'end_time' => $end]); |
| 147 | } |
| 148 | |
| 149 | public function add_blocked_period(BlockedPeriod $blocked_period){ |
| 150 | $this->blocked_time_periods[] = $blocked_period; |
| 151 | } |
| 152 | |
| 153 | public function add_booked_period(BookedPeriod $booked_period, $block_full_capacity = false){ |
| 154 | if($block_full_capacity){ |
| 155 | $max_capacity_booked_period = clone $booked_period; |
| 156 | $max_capacity_booked_period->total_attendees = $this->max_capacity; |
| 157 | $this->booked_time_periods[] = $max_capacity_booked_period; |
| 158 | }else{ |
| 159 | $this->booked_time_periods[] = $booked_period; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | public function add_available_periods(array $work_period_groups){ |
| 164 | $available_periods = []; |
| 165 | foreach($work_period_groups as $group_work_periods){ |
| 166 | // loop through groups, if multiple group - add them by comparing them against each other |
| 167 | $available_periods = ($available_periods) ? $this->compare_periods($available_periods, $group_work_periods) : $group_work_periods; |
| 168 | } |
| 169 | $this->work_time_periods = $available_periods; |
| 170 | } |
| 171 | |
| 172 | |
| 173 | private function compare_periods(array $available_periods, array $periods_to_intersect): array{ |
| 174 | $intersects = []; |
| 175 | foreach($available_periods as $available_period){ |
| 176 | foreach($periods_to_intersect as $intersect_period){ |
| 177 | $intersect = $this->find_intersection([$available_period, $intersect_period]); |
| 178 | if($intersect) $intersects[] = $intersect; |
| 179 | } |
| 180 | } |
| 181 | return $intersects; |
| 182 | } |
| 183 | |
| 184 | public function intersect_time_period(TimePeriod $time_period){ |
| 185 | } |
| 186 | |
| 187 | |
| 188 | // TODO instead of trying to find overlaps, just add a period and then overlaps can be skipped using array_unique for available minutes |
| 189 | public function add_time_period(TimePeriod $time_period_to_add){ |
| 190 | $overlapped_time_periods = []; |
| 191 | $not_overlapped_time_periods = []; |
| 192 | // search for all periods that overlap, to later merge them into one |
| 193 | foreach($this->work_time_periods as $time_period){ |
| 194 | if($time_period->check_if_overlaps($time_period_to_add)){ |
| 195 | $overlapped_time_periods[] = $time_period; |
| 196 | }else{ |
| 197 | $not_overlapped_time_periods[] = $time_period; |
| 198 | } |
| 199 | } |
| 200 | if($overlapped_time_periods){ |
| 201 | // if overlapping periods were found - find a unified range between them and create a new list of available time |
| 202 | // periods which should include those that were not overlapped |
| 203 | $this->work_time_periods = $not_overlapped_time_periods; |
| 204 | $merged_time_period = TimePeriod::get_unified_period_from_overlapped_periods($merged_time_period); |
| 205 | $this->work_time_periods[] = $merged_time_period; |
| 206 | }else{ |
| 207 | // nothing was overlapped, simply add this time period |
| 208 | $this->work_time_periods[] = $time_period_to_add; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | public function generate_resource_id(): string{ |
| 213 | return $this->agent_id.'_'.$this->service_id.'_'.$this->location_id; |
| 214 | } |
| 215 | |
| 216 | public static function create_from_connection(\OsConnectorModel $connection): BookingResource{ |
| 217 | $booking_request = new BookingResource([ 'agent_id' => $connection->agent_id, |
| 218 | 'service_id' => $connection->service_id, |
| 219 | 'location_id' => $connection->location_id]); |
| 220 | $booking_request->retrieve_service_data(); |
| 221 | return $booking_request; |
| 222 | } |
| 223 | |
| 224 | |
| 225 | public static function allowed_props(): array{ |
| 226 | return ['agent_id', |
| 227 | 'service_id', |
| 228 | 'location_id', |
| 229 | 'date', |
| 230 | 'start_time', |
| 231 | 'end_time']; |
| 232 | } |
| 233 | } |