$selected_extras Extras with duration. * @param int|null $exclude_booking_id Booking to exclude from busy set. * @param int $lead_time_min Lead time minutes. * @param int $horizon_days Horizon days. * @param string|null $same_day_cutoff_local Same-day cut-off HH:MM[:SS]. * @param TimeZoneId|null $location_timezone Location timezone for cut-off. */ public function __construct( public readonly int $service_id, public readonly int $duration_id, public readonly int $duration_minutes, public readonly int $buffer_before_min, public readonly int $buffer_after_min, public readonly int $slot_step_min, public readonly int $capacity_max, public readonly bool $allow_partial_group_fill, public readonly ?int $agent_id, public readonly ?int $location_id, public readonly DateTimeImmutable $range_start_utc, public readonly DateTimeImmutable $range_end_utc, public readonly TimeZoneId $customer_timezone, public readonly int $attendees, public readonly array $selected_extras, public readonly ?int $exclude_booking_id, public readonly int $lead_time_min, public readonly int $horizon_days, public readonly ?string $same_day_cutoff_local, public readonly ?TimeZoneId $location_timezone, ) {} /** * Query used only to collect busy intervals (availability stub / FreeBusy). * * @param DateTimeImmutable $range_start_utc Range start UTC. * @param DateTimeImmutable $range_end_utc Range end UTC. * @param TimeZoneId $timezone Display timezone. * @param int|null $agent_id Optional agent filter. */ public static function for_busy_scan( DateTimeImmutable $range_start_utc, DateTimeImmutable $range_end_utc, TimeZoneId $timezone, ?int $agent_id ): self { return new self( 0, 0, 60, 0, 0, 15, 1, true, $agent_id, null, $range_start_utc, $range_end_utc, $timezone, 1, [], null, 0, 0, null, null ); } /** * Total required duration including selected extras. */ public function required_duration_minutes(): int { $extra = 0; foreach ( $this->selected_extras as $row ) { $extra += $row['durationMin'] * $row['quantity']; } return $this->duration_minutes + $extra; } }