# Reservations in 3.4.6

Free and Pro use the same reservation engine. Public requests use `[yatoon_reservations]`; staff manage them in **Yatoon Menu → Reservations**. A submitted request is pending, not a guaranteed confirmed booking.

## Configure capacity before taking bookings

Open **Tables, dining time & guest services** on the Reservations screen. Enter physical tables, one per line:

```
main|Table 1|4
main|Table 2|2
```

The first column is a configured location key, followed by the table name and seats. Pro can configure several locations; Free uses its single location. Each party must fit at one table. Tables cannot be automatically joined. Do not add combinations of physical tables which are also listed separately. Reservation tables do not grant QR ordering permissions.

With an inventory, new requests hold a table for the complete dining period plus cleanup. Defaults are **90 minutes dining + 15 minutes cleanup**. Existing bookings keep their saved timing when these defaults change. Start times must follow configured reservation times, operating hours, closures and booking horizon. Operating hours define arrival times, not a required departure time.

The existing guest limit now limits **concurrent reservation guests**, including guests arriving at different overlapping times. **0 = unlimited**. If no table inventory is configured for a location, this guest limit and conflicts for explicitly assigned table names are enforced; zero guest limit plus no inventory remains unlimited. Configure tables or a guest limit to prevent unlimited requests.

New requests automatically take an available table. Existing unassigned requests are conservatively placed for availability checks; staff should assign their tables before enabling a busy service. This can decline a request rather than risk overbooking. An assigned table cannot overlap another pending, confirmed or seated reservation. A seated party keeps its table until staff complete the visit, even if its planned end has passed. Complete old seated records promptly. Walk-in guests need a reservation/seat record to count in this reservation inventory; an unrelated POS order alone is not a seat reservation.

## Staff workflow

- Confirm, seat, complete, cancel or mark no-show using the existing actions.
- For confirmed/seated visits, choose a table and click **Save table**. This does not change the booking status or send another confirmation email. Busy/small/wrong-location tables are rejected.
- Staff actions check the booking revision, preventing a stale browser from overwriting a newer table assignment or guest change. Refresh the list after a conflict.
- Table-only changes preserve queued confirmation emails and the reminder schedule. They do not send a second reminder for the same visit. Guest notices have a separate revision; cancelling or rescheduling still invalidates obsolete notices.
- Configure table inventory before the service. Removing or shrinking a table used by an active/future assigned reservation is blocked; first reassign or finish the booking.

## Guest changes and cancellations

The success message and reservation emails include a **Manage reservation** link. The link is a private bearer credential signed for that booking. It is not authenticated by reservation number or phone number. Keep it private. Pages disable caching, indexing, framing and referrer transmission and do not load third-party assets. Exclude the `yatoon_reservation_manage` query from any external full-page cache.

GET requests only display details. Changes require POST, a nonce scoped to the private link, a current booking revision and request throttling. Online changes are available only for future pending/confirmed bookings. Default confirmed-booking cutoff is **60 minutes before arrival**; pending requests can be cancelled/changed until their start, provided the pending hold has not expired. Seated/completed/cancelled/expired visits cannot be changed by the guest. Links stop displaying after the booked time plus one day; rotating WordPress auth salts also invalidates them.

Rescheduling keeps the party size and location. It checks a new configured future time and complete-visit capacity while excluding the guest's existing booking. On success, it atomically holds the new time/table, releases the old one and returns to **pending** for restaurant confirmation. On failure, the original booking remains unchanged. Changing party size/location requires contacting the restaurant. Cancellation releases the table and cannot be undone online.

## Pending expiry and reminders

Default pending expiry is **120 minutes**. Set 0 to disable expiry for new requests. Confirmed bookings do not expire automatically. Existing pending bookings at first upgrade get a full grace period starting at upgrade time, instead of expiring immediately based on their original creation date. Later expiry changes apply to new, reopened or rescheduled requests.

Expired pending requests stop consuming availability immediately on the next read, without waiting for cron. Background maintenance finalizes their Expired status and queues notices. Staff may reopen them after rechecking time/capacity.

Default reminder lead time is **1440 minutes (24 hours)**; 0 disables it. Late confirmations get a midpoint reminder rather than a duplicate email immediately after confirmation. Reminders require a guest email, enabled email delivery and functioning WordPress cron. Maintenance runs every five minutes when scheduled tasks run. Low-traffic sites should configure server cron to invoke WordPress cron. No reminder is sent after arrival, and obsolete versions are skipped after changes/cancellations. Emails are durably queued and retried by the existing queue; mail acceptance does not prove inbox delivery.

Deposits, waiting lists and booking-linked food preorders are not included in this release.

## Upgrade and deployment

Back up the database first, deactivate the other edition and install the appropriate **3.4.6** ZIP. The schema migration adds dining/buffer snapshots, pending hold expiry, an edit revision and a separate guest-notice revision/clock. The independent schema marker `346-2` also upgrades an earlier 3.4.6 installation and preserves existing queue identities. Review table inventory, timing, pending expiry, mail and cron settings after upgrading. Clear page/static caches. This delivery does not submit to SVN or install on the live website.

Shared files and header fragments are listed in `docs/shared-components.json`. The maintainer source bundle contains the canonical shared components; compile each edition's menu bundle after source changes and check the shared manifest before packaging.
