JOURNAL // ARCHIVE_ENTRY
Bookable Calendar: Meeting Links and Better Booking Workflows
Since my last Bookable Calendar update, I have added Google Meet and Microsoft Teams links, given calendar owners more control over their schedules, and built out the next round of reservation workflows. That includes rescheduling, attendance tracking, a better customer summary, and waitlists that hold an actual place while someone decides whether to claim it.
The work is split across two versions. The 3.1.0 tag includes meeting creation and owner-managed schedules alongside the payments and external availability work I covered previously. The reservation workflows below are on 3.2.x, which is still in development.
A meeting link for each reservation
Syncing a reservation to Google Calendar or Outlook is useful, but an online appointment also needs somewhere to happen. Both provider integrations now support creating a unique meeting for each reservation: Google Meet through Google Calendar and Microsoft Teams through Outlook.
These are optional settings on each calendar. The module checks whether the selected external calendar supports the meeting provider before enabling them. Google conference requests use the reservation UUID as a stable identifier, so retries and later reservation updates preserve the same meeting instead of generating another link. Outlook updates also preserve the existing Teams meeting.
If a calendar already has future reservations, there is an option to queue those events when meeting creation is enabled. One Microsoft-specific detail: turning the setting off does not remove Teams information from events that already have it. Microsoft does not allow an online event to be converted back to an offline event.
Let people manage their own schedules
A teacher should be able to maintain their own availability without becoming an administrator for everyone else's calendar. The new calendar ownership model supports that separation.
New calendars belong to the person who creates them. Roles can receive separate permissions to create calendars, edit their own calendars, and create or edit openings on those calendars. The administration lists respect that ownership too.
Those permissions do not let someone transfer ownership, move an opening onto another person's calendar, or change sensitive notification and integration settings. Existing calendars are left unassigned by the database update, so an administrator needs to choose their owners.
A workspace for the calendar owner
The next step on 3.2.x is managing the people who booked those schedules. There is now an owner workspace at /bookable-calendar/my-calendars, with a Reservations tab for each calendar the account can manage.
Owners can see reservations, party sizes, reservation states, and attendance. Viewing, editing, cancelling, and checking people in have separate permissions; owning a calendar does not automatically grant every staff action. A calendar can also opt into sending its owner reservation notifications.
This gives schedule managers somewhere to handle their own appointments without exposing another owner's customer list.
Move an appointment without losing it
Customers can reschedule to an available opening on the same calendar. The reservation keeps its ID and UUID, and the old capacity is released only when the move succeeds. If the destination fills up or the reservation changes while the form is open, the move is rejected and the original booking remains in place. A successful reschedule queues a change notification.
Calendars now have independent switches for customer cancellation and rescheduling, with a separate notice period for each. A lesson might allow rescheduling until the day before, for example, while an informal appointment might allow changes right up to its start. Staff with the appropriate permission can override those restrictions.
The same policies apply to forms, API requests, and direct entity writes. Disabling rescheduling does not prevent unrelated edits to reservation details, and disabling customer cancellation does not stop expired payment holds or declined waitlist offers from releasing capacity.
Keep paid reservations consistent with Commerce
Rescheduling a paid reservation retains its order association. Changing its calendar, party size, or purchased quantity independently is rejected because that would change the booking without making the corresponding payment adjustment.
Cancellation removes unpaid draft-cart items. For paid orders, it preserves the financial records and records the cancellation on the order. Cancelling an appointment does not automatically issue a refund; the site's Commerce and payment workflow still owns that decision.
The cancellation screen explains this distinction. Releasing a time slot should not silently imply that money has been returned.
A more useful reservation page
The customer summary brings the appointment time and timezone, party size, reservation status, available actions, and Commerce payment details into one place. Google Meet or Teams join links appear there once external synchronization supplies them.
There is also a meeting URL token for notification templates. Because provider work runs through queues, the URL may not exist when the initial confirmation is sent. Google conference creation can require another queue attempt before the join link is ready.
Customers can download an .ics file to add the appointment to their calendar. The download uses the reservation's existing access rules and leaves out customer email addresses and private management tokens. It is a single-event download, not a live subscription. After rescheduling, someone using only the download may need to import it again.
Find an opening without scrolling through everything
Availability now has pagination, a starting-date filter, a display timezone selector, and a Next available action. Dates and daily groups follow the selected timezone, including daylight-saving changes.
Filtering happens before the opening entities are rendered. The results account for booking windows, disabled openings, external busy periods, party capacity, and outstanding payment or waitlist holds. The action buttons and filters also received styling work so they fit the rest of the booking interface.
Availability can still change between viewing a page and submitting a booking. The transactional capacity check remains the final authority when the reservation is saved.
Record what happened after the booking
Staff can record check-in, completion, and no-show outcomes. Completion and no-show require the appointment to have started, and staff can update past appointments without running into rules intended for new bookings.
There is also optional reservation history with a configurable retention period. It records lifecycle snapshots, who performed an action, cancellation reasons, and attendance events. Cancellation still deletes the active reservation and frees its capacity; a separate history record can remain available to authorized staff in the owner workspace.
History is off unless a retention period is configured. Cron removes expired records, and setting retention back to zero stops new history records without changing the expiry of existing ones.
Waitlists with expiring offers
A full opening can now offer a waitlist. Customers join for their party size and receive a private management link. Joining alone does not create a reservation or charge a payment.
When capacity becomes available, cron processes the waiting entries and creates a real reservation hold for an eligible party. The customer receives a private offer link with a configurable expiry. A free booking confirms when the offer is claimed; a priced booking goes through Commerce checkout and confirms only after payment.
Declined or expired offers release the held capacity. Claimed links cannot be reused, and duplicate waiting entries are checked by email and opening.
The ordering is based on arrival, with room for a smaller party to receive an offer when an earlier, larger party cannot fit. Offers depend on cron and notification queue processing, so they are not sent immediately inside a cancellation request. A waitlist also does not reserve capacity ahead of a direct booking until its offer hold has actually been created.
More regression coverage
This work added tests for owner isolation, customer change deadlines, stale edits, full reschedule destinations, private calendar downloads, attendance, waitlist claims and expiry, and paid booking changes.
The recorded September 5 test run passed 112 tests with 2,548 assertions and reached 85.35% line coverage. The required coverage threshold is now 85%, and the coverage scope still includes unexecuted production files. Provider tests use fixtures rather than live customer accounts or payment gateways.
For anyone trying the new workflows, the workflow documentation covers permissions, configuration, and the current limitations. The 3.2 branch needs database updates and a cache rebuild after updating the code. Calendar closures, booking buffers, and reminder-setup assistance are still outside this round of work.
There is a lot more to an appointment than choosing an empty slot. This round gives customers better ways to manage their bookings and gives calendar owners the tools to handle the changes that follow. If you try it, feedback on those everyday workflows belongs in the Bookable Calendar issue queue.