PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.2.11
LatePoint – Calendar Booking Plugin for Appointments and Events v5.2.11
5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / misc / booking_resource.php
latepoint / lib / misc Last commit date
blocked_period.php 3 months ago booked_period.php 3 months ago booking_request.php 3 months ago booking_resource.php 3 months ago booking_slot.php 3 months ago filter.php 3 months ago process_action.php 3 months ago process_event.php 3 months ago role.php 3 months ago router.php 3 months ago step.php 3 months ago stripe_connect_customer.php 3 months ago time_period.php 3 months ago user.php 3 months ago work_period.php 3 months ago
booking_resource.php
258 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 ) ) {
32 $this->$key = $arg;
33 }
34 }
35 }
36
37 /**
38 * @return \OsAgentModel
39 */
40 function get_agent(): \OsAgentModel {
41 return ( $this->agent_id ) ? new \OsAgentModel( $this->agent_id ) : new \OsAgentModel();
42 }
43
44 protected function retrieve_service_data() {
45 $service = new \OsServiceModel( $this->service_id );
46 $this->max_capacity = $service->capacity_max;
47 $this->timeblock_interval = $service->timeblock_interval ? intval( $service->timeblock_interval ) : \OsSettingsHelper::get_default_timeblock_interval();
48 $this->min_capacity_to_be_blocked = $service->get_capacity_needed_before_slot_is_blocked();
49 }
50
51
52 public function get_timeblock_interval() {
53 if ( isset( $this->timeblock_interval ) ) {
54 return $this->timeblock_interval;
55 } else {
56 $this->retrieve_service_data();
57 return $this->timeblock_interval;
58 }
59 }
60
61
62 public function get_min_capacity_to_be_blocked() {
63 if ( isset( $this->min_capacity_to_be_blocked ) ) {
64 return $this->min_capacity_to_be_blocked;
65 } else {
66 $this->retrieve_service_data();
67 return $this->min_capacity_to_be_blocked;
68 }
69 }
70
71 public function get_max_capacity() {
72 if ( isset( $this->max_capacity ) ) {
73 return $this->max_capacity;
74 } else {
75 $this->retrieve_service_data();
76 return $this->max_capacity;
77 }
78 }
79
80 /**
81 * @param BookingRequest $booking_request
82 * @param int $selectable_time_interval
83 * @return void
84 */
85 public function build_bookable_slots( BookingRequest $booking_request, int $selectable_time_interval ) {
86 $this->slots = [];
87 $this->work_minutes = [];
88 foreach ( $this->work_time_periods as $time_period ) {
89 for ( $minute = $time_period->start_time; $minute <= $time_period->end_time - $booking_request->duration; $minute += $selectable_time_interval ) {
90 $period = new TimePeriod(
91 [
92 'start_time' => $minute - $booking_request->buffer_before,
93 'end_time' => $minute + $booking_request->duration + $booking_request->buffer_after,
94 ]
95 );
96
97 $this->work_minutes[] = $minute;
98
99 // add booking slot
100 $slot = new BookingSlot();
101 $slot->start_date = $this->date;
102 $slot->start_time = $minute;
103 $slot->max_capacity = $this->get_max_capacity();
104 $slot->min_capacity_to_be_blocked = $this->get_min_capacity_to_be_blocked();
105
106 // ---------------------
107 // LOGIC FOR "BOOKED" PERIODS
108 // ---------------------
109 foreach ( $this->booked_time_periods as $booked_period ) {
110 if ( $booked_period->start_time_with_buffer() >= $period->end_time || $booked_period->end_time_with_buffer() <= $period->start_time ) {
111 // not intersected
112 } else {
113 // intersects with a booked period, disqualify it
114 if ( $booked_period->service_id != $this->service_id ) {
115 // if it's a different service being performed - block full capacity of a slot, because you can't share capacities between different services
116 $slot->booked_capacity = $this->max_capacity;
117 } else {
118 $slot->booked_capacity += $booked_period->total_attendees;
119 }
120 }
121 }
122
123 // ---------------------
124 // LOGIC FOR "BLOCKED" PERIODS
125 // ---------------------
126 foreach ( $this->blocked_time_periods as $blocked_period ) {
127 if ( $blocked_period->start_time >= $period->end_time || $blocked_period->end_time <= $period->start_time ) {
128 // not intersected
129 } else {
130 // intersects with a blocked period, disqualify it
131 $slot->booked_capacity = $slot->max_capacity;
132 }
133 }
134
135 $this->slots[] = $slot;
136 }
137 }
138 }
139
140 // Function to print the intersection
141 private function find_intersection( array $time_periods ) {
142 // First interval
143 $start = $time_periods[0]->start_time;
144 $end = $time_periods[0]->end_time;
145 // Check rest of the intervals and find the intersection
146 $total = count( $time_periods );
147 for ( $i = 1; $i < $total; $i++ ) {
148 // If no intersection exists
149 if ( $time_periods[ $i ]->start_time > $end || $time_periods[ $i ]->end_time < $start ) {
150 return [];
151 } else {
152 // Else update the intersection
153 $start = max( $start, $time_periods[ $i ]->start_time );
154 $end = min( $end, $time_periods[ $i ]->end_time );
155 }
156 }
157 return new TimePeriod(
158 [
159 'start_time' => $start,
160 'end_time' => $end,
161 ]
162 );
163 }
164
165 public function add_blocked_period( BlockedPeriod $blocked_period ) {
166 $this->blocked_time_periods[] = $blocked_period;
167 }
168
169 public function add_booked_period( BookedPeriod $booked_period, $block_full_capacity = false ) {
170 if ( $block_full_capacity ) {
171 $max_capacity_booked_period = clone $booked_period;
172 $max_capacity_booked_period->total_attendees = $this->max_capacity;
173 $this->booked_time_periods[] = $max_capacity_booked_period;
174 } else {
175 $this->booked_time_periods[] = $booked_period;
176 }
177 }
178
179 public function add_available_periods( array $work_period_groups ) {
180 $available_periods = [];
181 foreach ( $work_period_groups as $group_work_periods ) {
182 // loop through groups, if multiple group - add them by comparing them against each other
183 $available_periods = ( $available_periods ) ? $this->compare_periods( $available_periods, $group_work_periods ) : $group_work_periods;
184 }
185 $this->work_time_periods = $available_periods;
186 }
187
188
189 private function compare_periods( array $available_periods, array $periods_to_intersect ): array {
190 $intersects = [];
191 foreach ( $available_periods as $available_period ) {
192 foreach ( $periods_to_intersect as $intersect_period ) {
193 $intersect = $this->find_intersection( [ $available_period, $intersect_period ] );
194 if ( $intersect ) {
195 $intersects[] = $intersect;
196 }
197 }
198 }
199 return $intersects;
200 }
201
202 public function intersect_time_period( TimePeriod $time_period ) {
203 }
204
205
206 // TODO instead of trying to find overlaps, just add a period and then overlaps can be skipped using array_unique for available minutes
207 public function add_time_period( TimePeriod $time_period_to_add ) {
208 $overlapped_time_periods = [];
209 $not_overlapped_time_periods = [];
210 // search for all periods that overlap, to later merge them into one
211 foreach ( $this->work_time_periods as $time_period ) {
212 if ( $time_period->check_if_overlaps( $time_period_to_add ) ) {
213 $overlapped_time_periods[] = $time_period;
214 } else {
215 $not_overlapped_time_periods[] = $time_period;
216 }
217 }
218 if ( $overlapped_time_periods ) {
219 // if overlapping periods were found - find a unified range between them and create a new list of available time
220 // periods which should include those that were not overlapped
221 $this->work_time_periods = $not_overlapped_time_periods;
222 $merged_time_period = TimePeriod::get_unified_period_from_overlapped_periods( $merged_time_period );
223 $this->work_time_periods[] = $merged_time_period;
224 } else {
225 // nothing was overlapped, simply add this time period
226 $this->work_time_periods[] = $time_period_to_add;
227 }
228 }
229
230 public function generate_resource_id(): string {
231 return $this->agent_id . '_' . $this->service_id . '_' . $this->location_id;
232 }
233
234 public static function create_from_connection( \OsConnectorModel $connection ): BookingResource {
235 $booking_request = new BookingResource(
236 [
237 'agent_id' => $connection->agent_id,
238 'service_id' => $connection->service_id,
239 'location_id' => $connection->location_id,
240 ]
241 );
242 $booking_request->retrieve_service_data();
243 return $booking_request;
244 }
245
246
247 public static function allowed_props(): array {
248 return [
249 'agent_id',
250 'service_id',
251 'location_id',
252 'date',
253 'start_time',
254 'end_time',
255 ];
256 }
257 }
258