Skip to main content

Bookings & Rentals

Overview​

Bookings & Rentals adds a bookable product type: something sold as a slot in time rather than as a unit of stock. An appointment, a table, a room, a piece of equipment for a weekend.

Availability is computed rather than stored as a list. Rules define when a product can be booked — globally, per product, or per resource — and a slot generator turns those rules into the slots a customer actually sees, minus what is already taken. A conflict detector then re-checks at the moment of booking, so two customers racing for the last slot do not both get it.

Resources are the things a booking consumes: staff, rooms, equipment, vehicles. A resource has a capacity and can modify the price, so booking the senior stylist or the large room costs more.

Bookings carry a timezone and store prices broken into base, resource and per-person components, so what was charged remains explicable later.

It is for appointments, reservations and hire.

Availability​

ItemValue
Module keybookings
TierPremium
Entitlement keybookings
Admin tabbookings
Enabled optionaiowc_module_enabled_bookings (off until turned on)
REST namespaceaiowc/v1
Product typeBookable

Enabling the module creates the tables below and schedules four jobs.

registerHooks() performs no licence check of its own; the module registry applies the entitlement gate centrally. It registers the product type, the frontend, the jobs, the WooCommerce email classes, the calendar provider and the email-automation triggers.

Settings​

This module declares no DEFAULTS constant. Its configuration is per product, per resource and per availability rule rather than a set of module-wide switches.

The booking​

FieldMeaning
start_datetime, end_datetime, all_dayWhen the booking is.
timezoneThe zone it was made in — stored per booking, not assumed.
duration_value, duration_unitIts length, in minutes, hours, days or weeks.
personsHow many people it is for.
statuspending, confirmed, complete, cancelled, expired or no_show.
payment_statusunpaid, paid, refunded or partial.
base_price, resource_price, person_price, total_priceThe price broken into its parts.
customer_*Email, name and phone — captured even for a guest booking.
notes, internal_notesWhat the customer said, and what staff recorded. Kept separate.
custom_fieldsAnything else the store collects, as JSON.
external_calendar_id, external_event_id, calendar_synced_atThe linked calendar event.
confirmed_at, cancelled_at, cancelled_by, cancellation_reasonThe audit of what happened and who did it.

Storing a timezone per booking, rather than converting to one store zone, is what makes a booking survive a daylight-saving change with its meaning intact.

Resources​

FieldMeaning
typestaff, room, equipment, vehicle or custom.
capacityHow many bookings it serves at once.
price_modifier_type, price_modifiernone, fixed, percent or multiply, and the amount.
availability_overrideRules specific to this resource, as JSON.
display_order, is_activeHow it is presented and whether it is offered.

A booking is linked to its resources through a join table, so one booking can consume more than one.

Availability rules​

A rule has a scope — global, product or resource — so a store defines its general opening hours once and overrides them where a particular product or member of staff differs.

Status machine​

Status changes go through a state machine rather than being set freely, and every transition is written to a log readable at /bookings/{id}/logs. Whether a customer may cancel is decided by the aiowc_booking_cancellation_allowed filter, so a store can express its own cancellation window.

Calendar sync​

The module defines a calendar provider interface with two implementations: a Google Calendar provider and a null provider that does nothing.

🔴 The null provider is what runs unless Google Calendar is configured. That is a deliberate design — the module works without an external calendar, and the sync job simply has nothing to do. A booking's external_event_id stays empty and calendar_synced_at is never set.

Admin screen​

The Bookings tab lists bookings and confirms, cancels, completes, reschedules or marks them no-show, individually or in bulk; manages resources and availability rules; shows statistics; exports bookings; and reads a booking's transition log. A diagnostics service backs a check of the module's own configuration.

REST API endpoints​

All routes are on aiowc/v1 and answer with the { success, data, message } envelope.

Administrator routes — manage_woocommerce​

MethodPathPurpose
GET / POST/bookingsList and create bookings.
GET / PATCH / DELETE/bookings/{id}Manage one booking.
POST/bookings/{id}/confirmConfirm it.
POST/bookings/{id}/cancelCancel it.
POST/bookings/{id}/completeMark it complete.
POST/bookings/{id}/no-showMark the customer as not having arrived.
POST/bookings/{id}/rescheduleMove it.
GET/bookings/{id}/logsIts transition history.
POST/bookings/bulk/confirm / /bulk/cancelAct on several at once.
GET/bookings/exportExport bookings.
GET/bookings/statisticsCounts and totals.
GET / POST/bookings/resourcesList and create resources.
GET / PATCH / DELETE/bookings/resources/{id}Manage one resource.
POST/bookings/resources/reorderChange their display order.
GET / POST/bookings/rulesList and create availability rules.
GET / PATCH / DELETE/bookings/rules/{id}Manage one rule.

Customer routes​

MethodPathPurposePermissionRequired args
GET/public/bookings/slotsAvailable slots for a product over a date range.Public, rate-limitedproductId, startDate, endDate
GET/public/bookings/resourcesThe resources a product offers.Public, rate-limitedproductId
POST/public/bookings/pricePrice a proposed booking.Public, rate-limited–
GET/public/bookings/my-bookingsThe caller's bookings.Customer check–
GET/public/bookings/{id}One of the caller's bookings.Customer checkid
POST/public/bookings/{id}/cancelCancel their own booking.Customer checkid
POST/public/bookings/{id}/rescheduleMove their own booking.Customer checkid, startDatetime, endDatetime

The four customer routes that name a booking id run a customer permission check, so a customer cannot read or change a booking that is not theirs by guessing an id.

WooCommerce integration​

AreaHooks
Product typeproduct_type_selector, product_type_options, woocommerce_product_class, woocommerce_product_data_tabs / _panels, woocommerce_process_product_meta
Product pagewoocommerce_before_add_to_cart_button, woocommerce_product_single_add_to_cart_text, woocommerce_quantity_input_args / _classes
Cartwoocommerce_add_to_cart_validation, _add_cart_item_data, _cart_id, _get_item_data, _before_calculate_totals, _check_cart_items
Checkoutwoocommerce_checkout_process, woocommerce_checkout_create_order_line_item
Order statuswoocommerce_order_status_processing, _completed, _cancelled, _failed, _refunded
Accountwoocommerce_account_menu_items, woocommerce_account_bookings_endpoint
Emailswoocommerce_email_classes — the module ships confirmation, cancellation and reminder emails as real WooCommerce email classes, so they are editable in WooCommerce's own email settings
Order displaywoocommerce_order_item_meta_end

Shortcodes [aiowc_booking_calendar], [aiowc_booking_search] and [aiowc_my_bookings].

woocommerce_cart_id is notable: it makes each booking a distinct cart line, so two different slots for the same product do not collapse into one line with quantity 2.

Database schema​

TableHolds
{prefix}aiowc_bookingsThe bookings, as described above.
{prefix}aiowc_booking_resourcesThe resources: type, capacity, price modifier, availability override.
{prefix}aiowc_booking_resource_assignmentsWhich resources a booking consumes.
{prefix}aiowc_booking_availability_rulesWhen things may be booked, scoped globally, per product or per resource.
{prefix}aiowc_booking_logsEvery status transition, with who made it and why.
{prefix}aiowc_rental_inventoryRental stock held by this module.
{prefix}aiowc_rental_reservationsRental reservations held by this module.

The last two matter: this module contains its own rental implementation, separate from the standalone Product Rentals module. See the limits below.

Background jobs​

HookIntervalWork
aiowc_booking_expiryevery 15 minutesExpires pending bookings that were never paid for.
aiowc_booking_reminderhourlySends reminders for upcoming bookings.
aiowc_booking_calendar_synchourlyPushes bookings to the external calendar, where configured.
aiowc_booking_cleanupdailyPrunes old log rows.

The 15-minute expiry job is what releases a slot held by an abandoned checkout, which is why it runs far more often than the others.

Action hooks for integrators​

Lifecycle: aiowc_booking_created, _confirmed, _cancelled, _completed, _rescheduled, _no_show, _expired, _reminder_sent, aiowc_rental_checked_out.

Filters: aiowc_booking_cancellation_allowed, aiowc_booking_max_cart_age, aiowc_customer_booking_actions, and three for the reminder email's data, content and headers.

Also aiowc_booking_cleanup_complete, aiowc_booking_calendar_sync_complete, and aiowc_email_automation_trigger.

Entitlement limits​

bookings is an on/off grant with no cap on bookings, resources or rules.

Health check​

The module reports a warning when its tables are missing or WooCommerce is inactive, and otherwise reports that it is functioning normally. The diagnostics service gives more detail.

Known gaps​

  • Rentals are implemented twice. This module owns aiowc_rental_inventory and aiowc_rental_reservations, while the standalone Product Rentals module owns aiowc_rentals, aiowc_rental_periods and aiowc_rental_calendar. They are separate implementations with separate data and separate admin screens. A store should choose one.
  • Calendar sync does nothing until Google Calendar is configured — the null provider is the default, and the sync job runs hourly regardless with nothing to do.
  • The module has no settings row, so behaviour that a store might expect to configure once — a default cancellation window, a default reminder lead time — is expressed per rule, per product or through a filter instead.
  • Bookings store a customer's name, email and phone directly on the booking row, including for guest bookings, which is customer personal data outside WooCommerce's own order tables and should be accounted for in a retention policy.