PhoenixKitBookings.Engine (PhoenixKitBookings v0.1.1)

Copy Markdown View Source

Adapter between the Bookings domain and phoenix_live_calendar's booking rules engine (Layer 3: BookingConfig / Availability / Constraints / TimeSlots), plus the day/night path (DayEngine) the lib doesn't cover.

Time frame

All minute-unit math runs in the site frame — wall-clock time in the site's configured offset (core's offset-hours model, "time_zone" setting). v1 services are physical (hotel / massage parlor / gym), so slots are shown and validated in venue-local time regardless of the viewer (the Cal.com lockTimeZoneToggleOnBookingPage behavior). Storage is always true UTC: frame_to_utc/1 on the way in, utc_to_frame/1 on the way out. Frame datetimes are UTC-tagged shifted values (the same trick as core's DateUtils.shift_to_offset/2 display path).

Day/night services never touch clock time — dates are frame-free by the workspace's all-day convention.

Unbounded duration

BookingConfig.effective_max_duration/1 treats nil as "same as duration", so a free-form service with no ceiling (max_duration: nil) maps to a large sentinel instead; real fit is still enforced by the availability-window check. (Candidate upstream tweak: an explicit unbounded semantic in the lib.)

Summary

Functions

Maps timed bookings onto ABSOLUTELY blocking events (overlap: false, no buffer expansion) — provider cross-service conflicts: a person can't be in two places regardless of the current service's seat pool.

Bookable slots of a minute service on a frame-local date: [{start_time, end_time, :available | :booked | :unavailable}].

Maps a Service row onto the lib's %BookingConfig{} (minute units).

Maps active bookings onto lib %Event{}s in the site frame.

Per-date remaining capacity of a day/night service over a date range: %{date => remaining_seats}. Closed dates map to 0.

Shifts a site-frame wall clock back to true UTC.

Builds a true-UTC datetime from a frame-local date + time.

Maps availability rules onto lib %Availability{} structs. A service with no rules is always open — a synthetic full-day window keeps slot generation and validation consistent.

Site offset in seconds (core offset-hours "time_zone" setting).

Today's date in the site frame.

Shifts a true-UTC datetime into the site frame (UTC-tagged wall clock).

Validates a booking request against the service's rules and the current active bookings. Advisory — PhoenixKitBookings.Bookings.create_booking/5 re-runs it inside a locked transaction.

Functions

blocking_events(bookings)

Maps timed bookings onto ABSOLUTELY blocking events (overlap: false, no buffer expansion) — provider cross-service conflicts: a person can't be in two places regardless of the current service's seat pool.

bookable_slots(service, rules, date, bookings)

Bookable slots of a minute service on a frame-local date: [{start_time, end_time, :available | :booked | :unavailable}].

booking_config(service)

Maps a Service row onto the lib's %BookingConfig{} (minute units).

bookings_to_events(bookings, service)

Maps active bookings onto lib %Event{}s in the site frame.

overlap: seats > 1 is deliberate: with one seat the event must BLOCK (Constraints.validate_no_overlap rejects only overlap: false events); with pooled seats events must pass the overlap check and be COUNTED by validate_capacity/slot_status instead.

Each event is pre-expanded by the service buffers ([start - buffer_before, end + buffer_after)) because the lib buffers only the REQUEST side: with both sides expanded, two consecutive bookings need a gap of buffer_after + buffer_before — the existing booking's cleanup plus the new booking's prep — which is the intended semantics for a shared per-service buffer config.

day_capacity(service, rules, from, until, bookings)

Per-date remaining capacity of a day/night service over a date range: %{date => remaining_seats}. Closed dates map to 0.

frame_to_utc(dt)

Shifts a site-frame wall clock back to true UTC.

frame_to_utc(date, time)

Builds a true-UTC datetime from a frame-local date + time.

lib_availability(rules)

Maps availability rules onto lib %Availability{} structs. A service with no rules is always open — a synthetic full-day window keeps slot generation and validation consistent.

site_offset_seconds()

Site offset in seconds (core offset-hours "time_zone" setting).

today()

Today's date in the site frame.

utc_to_frame(dt)

Shifts a true-UTC datetime into the site frame (UTC-tagged wall clock).

validate_request(service, rules, range, active_bookings, opts \\ [])

Validates a booking request against the service's rules and the current active bookings. Advisory — PhoenixKitBookings.Bookings.create_booking/5 re-runs it inside a locked transaction.

range is {starts_at_utc, ends_at_utc} (DateTime, minute services) or {:dates, starts_on, ends_on} (day/night services).

Returns :ok | {:error, reason_atom, message}.