GET /api/travels/export flattens the XML export into one row per travel
date x hotel with its pickups resolved, so the Teamer app can import the
disposition in one walk instead of fanning out over /api/travels.
Built from the XML file map, not travel_snapshot: the snapshot table sits
at this grain but refreshes on its own schedule, while the loaders share
the bpn.cache pool under the xml-sync tag. That is what makes /api/hotels,
/api/pickups and this export move together for free.
Each Ziel_*.xml is walked once and TravelParser::getPickups() does the
zeit parsing, which buys both zeit shapes and adjustOvernightTimes() --
a time-only 02:15 listed after a 23:59 rolls onto the next calendar day.
Pickups join against PickupLoader::loadAll() rather than
patchPickupsDetails(), which sorts: the contract is source order.
The whole assembled export is cached, not a page, so skipped, total and
pickupsWithoutMasterData are whole-export figures identical on every page
and a consumer can abort after the first one. Errors stay real status
codes; the builder is deliberately not wrapped in a catch that would
answer 200 with an error body the way hotelAvailability() does.
The route is declared before api_travel_single_code and that route grows
a requirement, or /api/travels/export resolves to byCode('export') and
404s as though the endpoint existed with no data.
Co-Authored-By: Claude Opus 5 <[email protected]>
The Teamer app groups hotels by cost unit and renders a flag per
destination, and cannot derive either from what /api/hotels returns
today.
costUnit is the raw kostenstelle and stays a string: only 281 of 1931
hotels carry one and some hold a space-separated list such as "84 85".
countryCode goes through CountryCodeMapper, whose table is part of the
API contract: BusPro's land column mixes ISO-2, ISO-3 and European
vehicle registration codes for the same country, and a wrong entry
mislabels a destination's flag silently. An unmappable token is null and
warns on the bpn channel, never a guess. country itself is untouched --
it remains the join key into /api/countries[].token.
Co-Authored-By: Claude Opus 5 <[email protected]>
The Teamer app imports travel and hotel master data nightly with no user
in the loop. AuthorizationCodeListener is bound to
AUTHORIZATION_REQUEST_RESOLVE, which only the auth code grant dispatches,
so a userless token never reaches the required_roles gate.
Drops the tagged grant service added in 79709e3a. That tag registered
client_credentials through the bundle's @deprecated grant configurator,
which runs regardless of enable_client_credentials_grant and re-enabled
the grant a second time with its own PT1H. Two enableGrantType() calls
for one grant left the lifetime decided by call order. M2M tokens now use
the shared access_token_ttl of PT10M.
Co-Authored-By: Claude Opus 5 <[email protected]>