[Back] <?php
/*
* Copyright (c) 2022 LatePoint LLC. All rights reserved.
*/
namespace LatePoint\Misc;
class BookingResource{
public int $agent_id;
public int $service_id;
public int $location_id;
public int $max_capacity;
public int $min_capacity;
public int $min_capacity_to_be_blocked;
public int $timeblock_interval;
public string $date;
public array $work_time_periods = [];
public array $booked_time_periods = [];
// time periods that are usually part of work periods but a blocked now for some reason(time past today),
// it's different from booked periods, because they have no bookings
public array $blocked_time_periods = [];
public array $slots = [];
public array $bookable_minutes = [];
public array $work_minutes = [];
function __construct($args = []){
$allowed_props = self::allowed_props();
foreach($args as $key => $arg){
if(in_array($key, $allowed_props)) $this->$key = $arg;
}
}
/**
* @return \OsAgentModel
*/
function get_agent(): \OsAgentModel{
return ($this->agent_id) ? new \OsAgentModel($this->agent_id) : new \OsAgentModel();
}
protected function retrieve_service_data(){
$service = new \OsServiceModel($this->service_id);
$this->max_capacity = $service->capacity_max;
$this->timeblock_interval = $service->timeblock_interval ? intval($service->timeblock_interval) : \OsSettingsHelper::get_default_timeblock_interval();
$this->min_capacity_to_be_blocked = $service->get_capacity_needed_before_slot_is_blocked();
}
public function get_timeblock_interval(){
if(isset($this->timeblock_interval)){
return $this->timeblock_interval;
}else{
$this->retrieve_service_data();
return $this->timeblock_interval;
}
}
public function get_min_capacity_to_be_blocked(){
if(isset($this->min_capacity_to_be_blocked)){
return $this->min_capacity_to_be_blocked;
}else{
$this->retrieve_service_data();
return $this->min_capacity_to_be_blocked;
}
}
public function get_max_capacity(){
if(isset($this->max_capacity)){
return $this->max_capacity;
}else{
$this->retrieve_service_data();
return $this->max_capacity;
}
}
/**
* @param BookingRequest $booking_request
* @param int $selectable_time_interval
* @return void
*/
public function build_bookable_slots(BookingRequest $booking_request, int $selectable_time_interval){
$this->slots = [];
$this->work_minutes = [];
foreach($this->work_time_periods as $time_period){
for($minute = $time_period->start_time; $minute <= $time_period->end_time - $booking_request->duration; $minute+= $selectable_time_interval){
$period = new TimePeriod(['start_time' => $minute - $booking_request->buffer_before, 'end_time' => $minute + $booking_request->duration + $booking_request->buffer_after]);
$this->work_minutes[] = $minute;
// add booking slot
$slot = new BookingSlot();
$slot->start_date = $this->date;
$slot->start_time = $minute;
$slot->max_capacity = $this->get_max_capacity();
$slot->min_capacity_to_be_blocked = $this->get_min_capacity_to_be_blocked();
// ---------------------
// LOGIC FOR "BOOKED" PERIODS
// ---------------------
foreach($this->booked_time_periods as $booked_period){
if ($booked_period->start_time_with_buffer() >= $period->end_time || $booked_period->end_time_with_buffer() <= $period->start_time) {
// not intersected
}else{
// intersects with a booked period, disqualify it
if($booked_period->service_id != $this->service_id){
// if it's a different service being performed - block full capacity of a slot, because you can't share capacities between different services
$slot->booked_capacity = $this->max_capacity;
}else{
$slot->booked_capacity+= $booked_period->total_attendees;
}
}
}
// ---------------------
// LOGIC FOR "BLOCKED" PERIODS
// ---------------------
foreach($this->blocked_time_periods as $blocked_period){
if ($blocked_period->start_time >= $period->end_time || $blocked_period->end_time <= $period->start_time) {
// not intersected
}else{
// intersects with a blocked period, disqualify it
$slot->booked_capacity = $slot->max_capacity;
}
}
$this->slots[] = $slot;
}
}
}
// Function to print the intersection
private function find_intersection(array $time_periods){
// First interval
$start = $time_periods[0]->start_time;
$end = $time_periods[0]->end_time;
// Check rest of the intervals and find the intersection
$total = count($time_periods);
for ($i = 1; $i < $total; $i++){
// If no intersection exists
if ($time_periods[$i]->start_time > $end || $time_periods[$i]->end_time < $start){
return [];
}else{
// Else update the intersection
$start = max($start, $time_periods[$i]->start_time);
$end = min($end, $time_periods[$i]->end_time);
}
}
return new TimePeriod(['start_time' => $start, 'end_time' => $end]);
}
public function add_blocked_period(BlockedPeriod $blocked_period){
$this->blocked_time_periods[] = $blocked_period;
}
public function add_booked_period(BookedPeriod $booked_period, $block_full_capacity = false){
if($block_full_capacity){
$max_capacity_booked_period = clone $booked_period;
$max_capacity_booked_period->total_attendees = $this->max_capacity;
$this->booked_time_periods[] = $max_capacity_booked_period;
}else{
$this->booked_time_periods[] = $booked_period;
}
}
public function add_available_periods(array $work_period_groups){
$available_periods = [];
foreach($work_period_groups as $group_work_periods){
// loop through groups, if multiple group - add them by comparing them against each other
$available_periods = ($available_periods) ? $this->compare_periods($available_periods, $group_work_periods) : $group_work_periods;
}
$this->work_time_periods = $available_periods;
}
private function compare_periods(array $available_periods, array $periods_to_intersect): array{
$intersects = [];
foreach($available_periods as $available_period){
foreach($periods_to_intersect as $intersect_period){
$intersect = $this->find_intersection([$available_period, $intersect_period]);
if($intersect) $intersects[] = $intersect;
}
}
return $intersects;
}
public function intersect_time_period(TimePeriod $time_period){
}
// TODO instead of trying to find overlaps, just add a period and then overlaps can be skipped using array_unique for available minutes
public function add_time_period(TimePeriod $time_period_to_add){
$overlapped_time_periods = [];
$not_overlapped_time_periods = [];
// search for all periods that overlap, to later merge them into one
foreach($this->work_time_periods as $time_period){
if($time_period->check_if_overlaps($time_period_to_add)){
$overlapped_time_periods[] = $time_period;
}else{
$not_overlapped_time_periods[] = $time_period;
}
}
if($overlapped_time_periods){
// if overlapping periods were found - find a unified range between them and create a new list of available time
// periods which should include those that were not overlapped
$this->work_time_periods = $not_overlapped_time_periods;
$merged_time_period = TimePeriod::get_unified_period_from_overlapped_periods($merged_time_period);
$this->work_time_periods[] = $merged_time_period;
}else{
// nothing was overlapped, simply add this time period
$this->work_time_periods[] = $time_period_to_add;
}
}
public function generate_resource_id(): string{
return $this->agent_id.'_'.$this->service_id.'_'.$this->location_id;
}
public static function create_from_connection(\OsConnectorModel $connection): BookingResource{
$booking_request = new BookingResource([ 'agent_id' => $connection->agent_id,
'service_id' => $connection->service_id,
'location_id' => $connection->location_id]);
$booking_request->retrieve_service_data();
return $booking_request;
}
public static function allowed_props(): array{
return ['agent_id',
'service_id',
'location_id',
'date',
'start_time',
'end_time'];
}
}