Skip to content

Allotment

Allotment is the per-date inventory a supplier grants Volāre on each service rate (supplier_service_rates.allotment). AllotmentService resolves which rates an offer/booking consumes, checks remaining capacity, and consumes/releases units as bookings are finalized or cancelled.

  • Deciding whether an offer is bookable on the web (getOfferAvailability / bulk getOffersAvailability)
  • Booking finalization — consumeAllotment() is called inside BookingFinalizationService’s transaction
  • Admin offer preview — the availability breakdown shown per service (OfferPreviewData)

Not for date-range blackouts — that is Stop Sales (see below).

resolveServiceRates(Offer, array $session) maps an offer (plus any checkout upsell overrides in the session) to the SupplierServiceRate rows it consumes, each with the list of calendar service_dates:

  • Hotels emit one consumed date per stay night — each consecutive itinerary day at the hotel becomes departure_date + (day − 1). Checkout hotel upgrades replace the base hotel for the covered night range.
  • Activities and transfers emit one date per itinerary day they sit on; upsell selections on a day replace that day’s included/base entries. A trip-level luxury transfer maps to day 1 (the departure date).
  • Package services (when the tour has them) emit a single date equal to the offer’s departure date.

The rate selected for each service is the one whose window covers departure_date (SupplierService::getRateForDate()). Multi-night stays do NOT re-resolve per night, even if the stay spills into a neighbouring rate window — this deliberately matches the pricing assumption.

Consumption is tracked as one AllotmentConsumption row per (booking, rate, service_date). Remaining capacity for an offer is:

MIN over every (rate × service_date) of: rate.allotment − consumed_count

getOfferAvailability(Offer) returns an AllotmentAvailabilityResult DTO: available (the MIN), isAvailable, the bottleneck service name, and a per-service breakdown. Offers with no supplier tour rate or no constrained services are unlimited().

getOffersAvailability(Collection) is the bulk variant for product pages (all offers must share a tour; base services only, no upsells) — a few bulk queries instead of N per offer. Used by ProductByMarketController.

consumeAllotment(Booking, array $session)must run inside a DB transaction (it does, in BookingFinalizationService):

  1. Locks all involved rate rows with lockForUpdate(), ordered by id (deterministic — no deadlocks), preventing concurrent overbooking
  2. Pre-checks every (rate, service_date) tuple and throws InsufficientAllotmentException before writing anything if one is already full
  3. Inserts the consumption rows (firstOrCreate, so re-finalization is idempotent)
  4. When this booking takes the last unit of any (rate, date), an AllotmentExhaustedNotification email goes to the Volāre entity address after commit (plus configured copies — see Email Copies)

releaseAllotment(Booking) deletes the booking’s consumption rows. It fires automatically from the Booking model’s updated hook whenever a booking’s status changes to cancelled (backend/app/Models/Booking.php).

Allotment Stop sale
Granularity Per service rate × calendar date Whole tour × date window
Semantics Counted capacity, consumed booking by booking Binary blackout, ignores capacity
Recovers When bookings are cancelled (release) When the window expires or is deleted
Managed as allotment integer on SupplierServiceRate StopSale rows on the tour

Both gates apply independently — an offer must have remaining allotment AND no active stop sale to be bookable.

  • Availability is always the worst (rate, date) tuple — one exhausted hotel night blocks the whole offer
  • The departure-date rate governs the whole stay; nights never split across rate windows
  • Overbooking is prevented with pessimistic locks at finalization, not just the earlier availability check
  • Cancelling a booking returns its units automatically
  • Stop Sales — tour-level date blackouts (the other bookability gate)
  • Booking Lifecycle — where consumption happens (finalization)
  • Suppliers — services and rates data model
  • Source: backend/app/Services/Allotment/AllotmentService.php
  • Source: backend/app/Services/Allotment/AllotmentAvailabilityResult.php
  • Source: backend/app/Models/SupplierServiceRate.php