Opening Hours Stack (PL-T185 + PL-T184)
En resumen
A layered opening-hours system where a single date's effective hours are resolved most-specific to least-specific. A date-level override beats a seasonal override which beats the weekly baseline. Supports full-day closures, modified hours, intra-day breaks for lunch service, and a unique-per-date constraint that prevents accidental override duplication. Consumed by the booking engine, the customer-CMS wizard and the admin calendar.
Cómo funciona
The baseline lives on ChainVenueProfile as a WeeklyHours object — seven DayHours entries, each with HH:MM time strings for open and close plus an optional list of breaks (lunch service gap, kitchen-closed window, etc.). Seasonal overrides also live on the profile, scoping different weekly patterns to date ranges (e.g. summer hours June 15 to August 31, holiday-season hours December 23 to January 6). Single-date overrides are stored in a dedicated OpeningHoursOverride document keyed by (tenant_id, location_id, date) with a unique constraint preventing duplicates.
Each override has a mode — closed (the venue is shut entirely for the day) or modified (open with custom hours and optional breaks). The resolution function get_effective_opening_hours(date_range) walks the stack: for each date it first checks for an OpeningHoursOverride, then a matching seasonal override, and falls back to the weekly baseline. The booking engine calls this whenever it validates a slot — a midsummer-eve closure becomes a hard 409 on any attempted booking, and a kitchen-break window blocks meal-order submission for that interval.
The API exposes GET for ranged effective hours (used by both the customer CMS and the admin calendar) and POST for creating overrides. Operators typically use the admin UI to set holiday closures and seasonal swings; the customer CMS reads effective hours to render the booking wizard's available days and times.
Capacidades clave
- Three-layer stack: date override → seasonal override → weekly baseline
- Override modes: fully closed or modified hours
- Intra-day breaks (lunch, between-services)
- Unique constraint per (tenant, location, date) — no duplicate overrides
- get_effective_opening_hours() consumed by booking engine + CMS
- Seasonal overrides scoped to date ranges via venue profile
En la práctica
Two weeks before midsummer the venue manager opens Opening Hours → Add Override, picks the date, chooses mode = closed, and types "Midsommarafton — stängt". She also adds a modified override for midsummer day (open 14:00-22:00 instead of the usual 11:00-23:00). The booking calendar immediately reflects both: midsummer eve goes dark grey and rejects new bookings, midsummer day shows shortened hours.
Customers visiting the public website that evening see midsummer eve struck through in the wizard's date picker with the closure message in their language. A month later she sets the summer seasonal override on the profile so weekly Thursday hours run an extra two hours through August — no need to add 13 individual date overrides.
Funcionalidades de este subsistema
8| ID | Status | Funcionalidades |
|---|---|---|
| F22.03.01 | Entregado | WeeklyHours + DayHours model (HH:MM time strings) ✅ |
| F22.03.02 | Entregado | OpeningHoursOverride document for single-date overrides ✅ |
| F22.03.03 | Entregado | Override modes: closed and modified ✅ |
| F22.03.04 | Entregado | Time breaks within a day (e.g. lunch break) ✅ |
| F22.03.05 | Entregado | get_effective_opening_hours() service method ✅ |
| F22.03.06 | Entregado | API: GET effective hours per date range, POST create override ✅ |
| F22.03.07 | Entregado | Unique constraint: one override per (tenant, location, date) ✅ |
| F22.03.08 | Entregado | Seasonal override via ChainVenueProfile ✅ |