2026-03-27 11:54:51 +00:00

806 lines
26 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Simply Schedule Availabilities Availabilities Model.
*
* @since 0.0.3
* @package Simply_Schedule_Availabilities
*/
use League\Period\Period;
/**
* Simply Schedule Availabilities Availabilities Model.
*
* @since 0.0.3
*/
class SSA_Availability_Model extends SSA_Db_Model {
protected $slug = 'availability';
protected $pluralized_slug = 'availability';
protected $version = '1.9.3';
/**
* Parent plugin class.
*
* @since 0.0.2
*
* @var Simply_Schedule_Availabilities
*/
protected $plugin = null;
/**
* Constructor.
*
* @since 0.0.2
*
* @param Simply_Schedule_Availabilities $plugin Main plugin object.
*/
public function __construct( $plugin ) {
// $this->version = $this->version.'.'.time(); // dev mode
parent::__construct( $plugin );
$this->hooks();
}
/**
* Initiate our hooks.
*
* @since 0.0.2
*/
public function hooks() {
}
public function belongs_to() {
return array(
'AppointmentType' => array(
'model' => $this->plugin->appointment_type_model,
'foreign_key' => 'appointment_type_id',
),
);
}
protected $schema = array(
'start_date' => array(
'field' => 'start_date',
'label' => 'Start Date',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'datetime',
'mysql_length' => '',
'mysql_unsigned' => false,
'mysql_allow_null' => true,
'mysql_extra' => '',
'cache_key' => false,
),
'end_date' => array(
'field' => 'end_date',
'label' => 'End Date',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'datetime',
'mysql_length' => '',
'mysql_unsigned' => false,
'mysql_allow_null' => true,
'mysql_extra' => '',
'cache_key' => false,
),
'capacity_available' => array(
'field' => 'capacity_available',
'label' => 'Capacity Available',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'MEDIUMINT',
'mysql_length' => 6,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'capacity_reserved' => array(
'field' => 'capacity_reserved',
'label' => 'Capacity Reserved',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'MEDIUMINT',
'mysql_length' => 6,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'buffer_available' => array(
'field' => 'buffer_available',
'label' => 'Buffer Available',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'MEDIUMINT',
'mysql_length' => 6,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'buffer_reserved' => array(
'field' => 'buffer_reserved',
'label' => 'Buffer Reserved',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'MEDIUMINT',
'mysql_length' => 6,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'capacity_meta' => array(
'field' => 'capacity_meta',
'label' => 'Capacity Meta',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'TEXT',
'mysql_length' => false,
'mysql_unsigned' => false,
'mysql_allow_null' => true,
'mysql_extra' => '',
'cache_key' => false,
'encoder' => 'json',
),
'appointment_type_id' => array(
'field' => 'appointment_type_id',
'label' => 'Appointment Type ID',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'BIGINT',
'mysql_length' => 20,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'staff_id' => array(
'field' => 'staff_id',
'label' => 'Staff Id',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'BIGINT',
'mysql_length' => 11,
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'resource_group_id' => array(
'field' => 'resource_group_id',
'label' => 'Resource Group Id',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'BIGINT',
'mysql_length' => 11,
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'type' => array(
'field' => 'type',
'label' => 'Type',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'VARCHAR',
'mysql_length' => '16',
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'subtype' => array(
'field' => 'subtype',
'label' => 'Subtype',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'VARCHAR',
'mysql_length' => '16',
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'description' => array(
'field' => 'description',
'label' => 'Description',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'TEXT',
'mysql_length' => '',
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'status' => array(
'field' => 'status',
'label' => 'Status',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'VARCHAR',
'mysql_length' => 250,
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'is_all_day' => array(
'field' => 'is_all_day',
'label' => 'Is All Day Event?',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'VARCHAR',
'mysql_length' => 250,
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'cache_key' => array(
'field' => 'cache_key',
'label' => 'Cache Key',
'default_value' => false,
'format' => '%d',
'mysql_type' => 'BIGINT',
'mysql_length' => 11,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'cache_args_hash' => array(
'field' => 'cache_args_hash',
'label' => 'Args Hash',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'INT',
'mysql_length' => 10,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'date_created' => array(
'field' => 'date_created',
'label' => 'Date Created',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'datetime',
'mysql_length' => '',
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'date_modified' => array(
'field' => 'date_modified',
'label' => 'Date Modified',
'default_value' => false,
'format' => '%s',
'mysql_type' => 'datetime',
'mysql_length' => '',
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'availability_type' => array(
'field' => 'availability_type',
'label' => 'Availability Type',
'default_value' => false, // 'busy', 'buffer', 'free',
'format' => '%s',
'mysql_type' => 'VARCHAR',
'mysql_length' => '8',
'mysql_unsigned' => false,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'is_available' => array(
'field' => 'is_available',
'label' => 'Is Available',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'TINYINT',
'mysql_length' => 1,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
'availability_score' => array(
'field' => 'availability_score',
'label' => 'Availability Score',
'default_value' => 0,
'format' => '%d',
'mysql_type' => 'INT',
'mysql_length' => 3,
'mysql_unsigned' => true,
'mysql_allow_null' => false,
'mysql_extra' => '',
'cache_key' => false,
),
);
public $indexes = array(
'appointment_type_id' => [ 'appointment_type_id' ],
'is_available' => [ 'is_available' ],
'start_date' => [ 'start_date' ],
'end_date' => [ 'end_date' ],
'type' => [ 'type' ],
'subtype' => [ 'subtype' ],
'cache_key' => [ 'cache_key' ],
'cache_args_hash' => [ 'cache_args_hash' ],
'date_created' => [ 'date_created' ],
'cache_query' => [ 'cache_args_hash', 'start_date', 'end_date', 'cache_key' ],
);
public function filter_where_conditions( $where, $args ) {
global $wpdb;
if ( !empty( $args['appointment_type_id'] ) ) {
$where .= $wpdb->prepare( ' AND appointment_type_id=%d', sanitize_text_field( $args['appointment_type_id'] ) );
}
if ( !empty( $args['resource_group_id'] ) ) {
$where .= $wpdb->prepare( ' AND resource_group_id=%d', sanitize_text_field( $args['resource_group_id'] ) );
}
if ( !empty( $args['staff_id'] ) ) {
$where .= $wpdb->prepare( ' AND staff_id=%d', sanitize_text_field( $args['staff_id'] ) );
}
if ( isset( $args['is_available'] ) ) {
$where .= $wpdb->prepare( ' AND is_available=%d', sanitize_text_field( $args['is_available'] ) );
}
if ( isset( $args['type'] ) ) {
$where .= $wpdb->prepare( ' AND type=%s', sanitize_text_field( $args['type'] ) );
}
if ( isset( $args['subtype'] ) ) {
$where .= $wpdb->prepare( ' AND subtype=%s', sanitize_text_field( $args['subtype'] ) );
}
if ( isset( $args['cache_args_hash'] ) ) {
$where .= $wpdb->prepare( ' AND cache_args_hash=%d', sanitize_text_field( $args['cache_args_hash'] ) );
}
if ( isset( $args['cache_key'] ) ) {
$where .= $wpdb->prepare( ' AND cache_key=%d', sanitize_text_field( $args['cache_key'] ) );
}
if ( isset( $args['intersects_period'] ) ) {
if ( $args['intersects_period'] instanceof Period ) {
$start_date_string = $args['intersects_period']->getStartDate()->format( 'Y-m-d H:i:s' );
$end_date_string = $args['intersects_period']->getEndDate()->format( 'Y-m-d H:i:s' );
// it should END in the queried period
// OR
// it should START in the queried period
// OR
// it should CONTAIN the queried period
$where .= " AND (
(end_date >= '{$start_date_string}' AND end_date <= '{$end_date_string}' )
OR
(start_date <= '{$end_date_string}' AND start_date >= '{$start_date_string}' )
OR
(start_date <= '{$start_date_string}' AND end_date >= '{$end_date_string}' )
)";
}
}
return $where;
}
public function update_rows( $availability_period_rows, $args = array() ) {
$args = array_merge( array(
'cache_key' => time(),
'type' => '',
'subtype' => '',
), $args );
$execution_datetime = gmdate( 'Y-m-d H:i:s' );
global $wpdb;
foreach ($availability_period_rows as $key => $availability_period_row) {
$availability_period_row['cache_key'] = $args['cache_key'];
$response = $this->insert( $availability_period_row );
}
$query = "DELETE FROM {$this->get_table_name()} WHERE 1=1";
$query = $wpdb->prepare( $query .= " AND cache_key < %d", $args['cache_key'] );
$query = $wpdb->prepare( $query .= " AND start_date >= %s", $args['start_date_min'] );
$query = $wpdb->prepare( $query .= " AND start_date <= %s", $args['start_date_max'] );
$query = $wpdb->prepare( $query .= " AND type = %s", $args['type'] );
$query = $wpdb->prepare( $query .= " AND subtype = %s", $args['subtype'] );
$result = $wpdb->query( $query );
}
public function bulk_delete( $args=array() ) {
return $this->db_bulk_delete( $args );
}
public function register_routes() {
// override register_routes() because we don't want any default behavior reading/writing to this database table
$namespace = $this->api_namespace.'/v' . $this->api_version;
$base = $this->get_api_base();
register_rest_route( $namespace, '/' . $base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'availability_query' ),
// 'permission_callback' => array( $this, 'get_items_permissions_check' ),
'permission_callback' => '__return_true',
'args' => array(
),
),
) );
register_rest_route( $namespace, '/' . $base . '/troubleshoot', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'troubleshoot' ),
// 'permission_callback' => array( $this, 'get_items_permissions_check' ),
'permission_callback' => '__return_true',
'args' => array(
'start_date' => array(
'required' => true,
),
'end_date' => array(
'required' => false,
),
'appointment_type_id' => array(
'required' => true,
),
),
),
) );
}
public function availability_query( $request ) {
$params = $request->get_params();
$appointment_type = SSA_Appointment_Type_Object::instance( (int) $params['appointment_type_id'] );
try {
$appointment_type_title = $appointment_type->title;
} catch ( Exception $e ) {
return 'appointment_type_not_found';
}
$start_date = ssa_datetime( $params['start_date'] ); // TODO: handle invalid dates?
if ( ! empty( $params['end_date'] ) ) {
$end_date = ssa_datetime( $params['end_date'] );
$period = new Period( $start_date, $end_date );
} else {
$period = new Period( $start_date, $start_date->add( $appointment_type->get_duration_interval() ) );
}
$availability_query = new SSA_Availability_Query(
$appointment_type,
$period,
array(
'cache_level_read' => 1,
'cache_level_write' => 1,
)
);
$bookable_start_datetime_strings = $availability_query->get_bookable_appointment_start_datetime_strings();
if ( ! empty( $bookable_start_datetime_strings['0']['start_date'] ) ) {
$bookable_start_datetime_strings = wp_list_pluck( $bookable_start_datetime_strings, 'start_date' );
}
return $bookable_start_datetime_strings;
}
public function troubleshoot( $request ) {
$params = $request->get_params();
$appointment_type = SSA_Appointment_Type_Object::instance( (int) $params['appointment_type_id'] );
try {
$appointment_type_title = $appointment_type->title;
} catch ( Exception $e ) {
return 'appointment_type_not_found';
}
$start_date = ssa_datetime( $params['start_date'] ); // TODO: handle invalid dates?
if ( ssa_datetime() > $start_date ) {
return array(
'is_bookable' => false,
'reasons' => array(
array(
'reason' => 'start_date_past',
'reason_title' => __( 'That time has already passed', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date you selected is in the past, so a customer can\'t book it without a time machine :)', 'simply-schedule-appointments' ),
'help_center_url' => '',
)
)
);
}
if ( ! empty( $params['end_date'] ) ) {
$end_date = ssa_datetime( $params['end_date'] );
$period = new Period( $start_date, $end_date );
} else {
$period = new Period( $start_date, $start_date->add( $appointment_type->get_duration_interval() ) );
}
$appointment = SSA_Appointment_Factory::create( $appointment_type, array(
'id' => 0,
'start_date' => $start_date->format( 'Y-m-d H:i:s' ),
) );
$public_query = new SSA_Availability_Query(
$appointment_type,
$period,
array(
'cache_level_read' => false,
'cache_level_write' => false,
)
);
if ( $public_query->is_prospective_appointment_bookable( $appointment ) ) {
return array(
'is_bookable' => true,
'reasons' => array(
array(
'is_bookable' => true,
'reason' => 'available',
'reason_title' => __( 'Time slot is available', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is available to be booked.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
)
)
);
}
// the time isn't bookable, so let's find out why
$case = new SSA_Availability_Detective_Case(
$appointment_type,
$period,
$appointment
);
$case = $case->investigate();
if ( ! empty( $case->culprits ) ) {
$response = array(
'is_bookable' => false,
'cleared' => $case->cleared,
'culprits' => $case->culprits,
'reasons' => array()
);
// Show Appointment Type Reason
$found_appt_type_reason = false;
if ( in_array( 'appointment_type.separate_appointment_type_availability', $case->culprits ) ) {
$found_appt_type_reason = true;
$response['reasons'][] = array(
'reason' => 'appointments',
'reason_title' => __( 'Separate Appointment Type Availability', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of a booked appointment in another appointment type.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'appointment_type.min_booking_notice', $case->culprits ) ) {
$found_appt_type_reason = true;
$response['reasons'][] = array(
'reason' => 'appointment_type.min_booking_notice',
'reason_title' => __( 'Minimum booking notice', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is sooner than your minimum booking notice.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'appointment_type.max_booking_notice', $case->culprits ) ) {
$found_appt_type_reason = true;
$response['reasons'][] = array(
'reason' => 'appointment_type.max_booking_notice',
'reason_title' => __( 'Advance', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is later than your maximum advance booking notice.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'appointment_type.max_per_day', $case->culprits ) ) {
$found_appt_type_reason = true;
$response['reasons'][] = array(
'reason' => 'appointment_type.max_per_day',
'reason_title' => __( 'Maximum # Per Day', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is on a date that has already reached your setting for the maximum number of appointments per day.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'appointment_type.availability_window', $case->culprits ) ) {
$found_appt_type_reason = true;
$response['reasons'][] = array(
'reason' => 'appointment_type.availability_window',
'reason_title' => __( 'Availability Window', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is outside the availability window you set for this appointment type.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
// Show Appointments Reason
$found_appointment_reason = false;
$most_wanted_culprits = array('appointment_type.appointments.pending_payment', 'appointment_type.appointments.pending_form');
if( count( array_intersect( $case->culprits, $most_wanted_culprits ) ) > 0 ) {
$case->culprits = array_filter($case->culprits, static function ($element) {
return $element !== "appointment_type.appointments.booked";
});
}
if( !$found_appt_type_reason ) {
// JOIN Pending Form/Payment
if ( in_array( 'appointment_type.appointments.pending_form', $case->culprits ) && in_array( 'appointment_type.appointments.pending_payment', $case->culprits )) {
$found_appointment_reason = true;
$response['reasons'][] = array(
'reason' => 'appointments.pending_form',
'reason_title' => __( 'Pending Appointments', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of pending appointments.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
$case->culprits = array_filter($case->culprits, static function ($element) {
return $element !== "appointment_type.appointments.pending_form" && $element !== "appointment_type.appointments.pending_payment";
});
}
// Booked
if ( in_array( 'appointment_type.appointments.booked', $case->culprits ) ) {
$found_appointment_reason = true;
$response['reasons'][] = array(
'reason' => 'appointments',
'reason_title' => __( 'Booked Appointment', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of a booked appointment.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
// Pending Form
if ( in_array( 'appointment_type.appointments.pending_form', $case->culprits ) ) {
$found_appointment_reason = true;
$response['reasons'][] = array(
'reason' => 'appointments.pending_form',
'reason_title' => __( 'Pending Form Submissions', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of pending form submissions.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
// Pending Payment
if ( in_array( 'appointment_type.appointments.pending_payment', $case->culprits ) ) {
$found_appointment_reason = true;
$response['reasons'][] = array(
'reason' => 'appointments.pending_payment',
'reason_title' => __( 'Pending Payments', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of pending payments (a customer booked this time but has not completed the payment yet).', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
// Appointments
if ( !$found_appointment_reason && in_array( 'appointment_type.appointments', $case->culprits ) ) {
$response['reasons'][] = array(
'reason' => 'appointments',
'reason_title' => __( 'Appointment', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of appointments.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
// Appointment Type
if ( !$found_appointment_reason && in_array( 'appointment_type', $case->culprits ) ) {
$response['reasons'][] = array(
'reason' => 'appointment_type',
'reason_title' => __( 'Appointment Type settings', 'simply-schedule-appointments' ),
'reason_description' => __( 'Please contact support to get help looking at your appointment type settings.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
}
if ( in_array( 'staff', $case->culprits ) ) {
$response['reasons'][] = array(
'reason' => 'staff_appointments',
'reason_title' => __( 'Team Member', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because there are no team members available.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'resources', $case->culprits ) ) {
$response['reasons'][] = array(
'reason' => 'resources_appointments',
'reason_title' => __( 'Resources', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because there are no resources available.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'blackout_dates', $case->culprits ) ) {
$response['reasons'][] = array(
'reason' => 'blackout_dates',
'reason_title' => __( 'Blackout Dates', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is on a global blackout date.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
if ( in_array( 'google_calendar', $case->culprits ) ) {
$response['reasons'][] = array(
'reason' => 'google_calendar',
'reason_title' => __( 'Google Calendar', 'simply-schedule-appointments' ),
'reason_description' => __( 'The date and time you selected is unavailable because of an event on an excluded Google Calendar.', 'simply-schedule-appointments' ),
'help_center_url' => '',
'ssa_path' => '',
);
}
return $response;
}
// we couldn't detect a reason, so let's recommend contacting support
return array(
'is_bookable' => false,
'reasons' => array(
array(
'reason' => 'unknown',
'reason_title' => __( 'Please contact support', 'simply-schedule-appointments' ),
'reason_description' => __( 'This time is unavailable, but we are unable to detect the reason automatically. Please contact support.', 'simply-schedule-appointments' ),
'ssa_path' => '/ssa/support/help',
)
)
);
}
public function investigate( $case, SSA_Appointment_Type_Object $appointment_type, Period $period, SSA_Appointment_Object $appointment ) {
// try required but not cleared
// if no uncleared requirements, try cleared + next suspect, one by one
// try combinations of suspects (google calendar + staff)
if ( empty( $case['suspects'] ) ) {
return $case;
}
$suspect_under_interrogation = array_shift( $case['suspects'] );
$solo_interrogation_args = array_merge(
$case['required'],
array( $suspect_under_interrogation )
);
$solo_interrogation_query = new SSA_Availability_Query(
$appointment_type,
$period,
array_merge( $solo_interrogation_args, array(
'cache_level_read' => false,
'cache_level_write' => false,
) )
);
if ( $solo_interrogation_query->is_prospective_appointment_bookable( $appointment ) ) {
$case['cleared'][] = $suspect_under_interrogation;
} else {
$case['culprits'][] = $suspect_under_interrogation;
}
return $this->investigate( $case, $appointment_type, $period, $appointment );
}
}