docs/cms-api.md describes the endpoints TYPO3 serves to MyE&P and the chatbot (/api/hotel, /api/product, /api/calendar-hotels, /api/content/*): key, parameters, responses, errors. It is the other side of MyE&P's API consumer guide. docs/calendars.md covers the price calendar, the Belegungskalender and the iCal feed: which product gets which, the MyE&P endpoints behind them, caching, troubleshooting, and the rule that a product's calendar hotel must carry the BusPro code, not a group house's CMS code (which broke three example trips). The README replaces the TYPO3 base distribution boilerplate and links both. The plugin renaming SQL in ep_products' README is obsolete since the migration. The cmsApiKey description names /api/calendar-hotels. Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.9 KiB
Availability and prices on product pages
TYPO3 shows three things built from contingent data. None of them talks to bpn-connect directly: all go through MyE&P, which keeps a local snapshot of bpn-connect's availability and holds the group house prices.
| What | Where | Shown for | MyE&P endpoint |
|---|---|---|---|
| Price calendar + price tables | product detail, tab "Termine/Preise" | products with a calendar hotel and season summer (s) |
contingents/calendar, contingents/prices |
| Belegungskalender (availability calendar) | product detail, sidebar | products with a calendar hotel and season not summer | contingents/availability (+ contingents/calendar for the months to leave out) |
| iCal feed for booking portals | https://www.ep-reisen.de/contingents/<hotel>.ical |
every hotel | contingents/availability |
So every product with a calendar hotel shows exactly one calendar. The MyE&P side is documented in
its docs/api-consumer-guide.md (EP/myep).
The calendar hotel field
A product's calendar hotel (calendar_hotel) decides whose availability and prices it shows.
TYPO3 sends that hotel's code to MyE&P, which only understands BusPro calendar codes (the
codes bpn-connect knows, e.g. DGS, LPJ, DKS).
Most hotel records carry the BusPro code. Group houses also have content records of their own
(DKSGRP, LPJGRP, SERASCSO, …) whose code is the CMS code MyE&P uses to fetch content via
/api/hotel. These must not be used as calendar hotel: MyE&P and bpn-connect do not know
them, and the calendar shows its error message. This broke three example trips until 2026-10-29.
| Use as calendar hotel | Not |
|---|---|
DKS Sportclub Schweizerhaus (uid 193) |
DKSGRP Davos Klosters - Gruppenhaus Schweizerhaus |
LPJ Sportclub Jenatsch (uid 37) |
LPJGRP Lenzerheide - Gruppenhaus Jenatsch |
/api/calendar-hotels lists all calendar hotels in use; every code in it should be a BusPro code.
A hidden hotel record counts as no calendar hotel: the product shows no calendar and the code is
not in the list (e.g. ASB Sportclub Bader).
Price calendar (summer)
Product/Detail.html, sectionDates, partialProduct/PriceCalendar;AjaxCalendarController::priceCalendarAction→contingents/calendar(status and price per day),priceTableAction→contingents/pricesfor the current and the next year (loaded when scrolled into view).- Only works for hotels that are an accommodation with prices in MyE&P (summer group houses).
Belegungskalender (winter)
Product/Detail.html, sectionAvailabilityCalendar, loaded by htmx when it scrolls into view (intersect once, so the hidden copy of the sidebar on the other breakpoint does not load);AjaxCalendarController::availabilityAction, templateAjaxCalendar/Availability.html, month selectcalendar_controller.js.- All months in one response; the select only switches between them. 12 months from the current one, extended to the last month MyE&P has data for.
- Months with prices are left out: they are summer, covered by the price calendar. Only summer
months have prices today; that is the whole distinction. If
contingents/calendarfails, all months are shown (not cached). - Colours as on TYPO3 10: green available (
OK, and days without data), yellow on request (ON_REQUEST), red not available (BLOCKED), past days grey; a status change from the previous day is drawn as a diagonal. Theavailability-*classes are built in PHP, so they are safelisted intailwind.config.js; without that Tailwind purges them. - Works for every hotel MyE&P syncs, with or without an accommodation (see below).
iCal feed
IcalController::contingentsAction,IcalService; route/contingents/{hotel}.icalon theep-reisensite (config/sites/ep-reisen/config.yaml, page 9, type 1710),{hotel}is the hotel's path segment.- One
Unavailableevent per run ofBLOCKEDorON_REQUESTdays with the same status, as on TYPO3 10. - Never cached. When MyE&P has no availability it answers
503withRetry-After: 900instead of an empty calendar, which the portals would read as "every day free"; they keep their last import.
Which hotels MyE&P has availability for
MyE&P syncs its snapshot from bpn-connect every 15 minutes during the day and hourly at night, for
the calendar codes of its accommodations plus every code in TYPO3's /api/calendar-hotels
(see cms-api.md). So a hotel offered only in winter needs nothing in MyE&P: setting
it as calendar hotel of a visible product is enough; it is synced with the next run.
contingents/availability answers:
400for a code that is not synced (not an accommodation, not in TYPO3's list),502when the hotel's snapshot is missing or older than 6 hours (sync failing),200with one entry per known day otherwise.
contingents/calendar and contingents/prices additionally need an accommodation with prices.
MyE&P's GET /api/contingents/status lists every synced code with its source, prices, last sync
and last error: the first place to look when a calendar does not load.
Caching
- The AJAX responses (price calendar, price tables, Belegungskalender) are page-cached on the AJAX
page for at most 1 hour (
cache_period = 3600) and never past midnight (cache_clearAtMidnight, past days must turn grey). Browsers do not cache them (sendCacheHeaders = 0). - Errors are not cached (
disablePageCache()): the next visitor triggers a new attempt. - Every AJAX URL carries a cHash (required for
tx_epproducts_ajax), so each hotel and year has its own cache entry. - Entries are tagged
ep_hotel_<code>. Nothing flushes them automatically; a deploy flushes all caches. To refresh by hand:vendor/bin/typo3 cache:flush --group pages, or flush the tag via theCacheManagerfor a single hotel. - On top of that comes MyE&P's sync interval, so a calendar is at most about 1¼ hours behind bpn-connect.
When a calendar does not load
The message is "Der Kalender konnte aktuell nicht geladen werden."; the iCal feed answers 503.
| Cause | How to tell | Fix |
|---|---|---|
Calendar hotel has a CMS code (…GRP, …SO) |
code not BusPro; MyE&P 400, bpn-connect 404 |
set the hotel record with the BusPro code as calendar hotel |
| Code not synced by MyE&P | not in contingents/status; MyE&P 400 |
check that the product and its calendar hotel are visible (then it is in /api/calendar-hotels) |
| Sync failing for the hotel | contingents/status: available: false, lastError |
MyE&P / bpn-connect |
| MyE&P unreachable or its OAuth failing | all hotels fail; TYPO3 log EP.EpProducts.MyEP.ApiClient |
MyE&P |
| Price calendar only: no accommodation or no prices | MyE&P 400 / empty |
prices are maintained in MyE&P's Groups admin |
TYPO3 logs every failed MyE&P call in var/log/typo3_*.log (component
EP.EpProducts.MyEP.ApiClient, for the iCal feed also EP.EpProducts.Service.IcalService).