6#include "fcbextractor_p.h"
8#include "variantvisitor_p.h"
10#include <KItinerary/ExtractorValidator>
11#include <KItinerary/Organization>
12#include <KItinerary/Person>
13#include <KItinerary/ProgramMembership>
14#include <KItinerary/Reservation>
15#include <KItinerary/Ticket>
16#include <KItinerary/TrainTrip>
24[[nodiscard]]
static QString ticketNameForDocument(
const T &doc)
26 return std::visit([](
auto &&doc) {
27 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(ReservationData), FCB_VERSIONED(OpenTicketData), FCB_VERSIONED(PassData)>) {
28 auto n = doc.tariffs.isEmpty() ?
QString() : doc.tariffs.at(0).tariffDesc;
32 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(PassData)>) {
33 if (!doc.passDescription.isEmpty()) {
34 return doc.passDescription;
43QString FcbExtractor::ticketName(
const Fcb::UicRailTicketData &fcb)
45 return std::visit([](
auto &&fcb) {
46 for (
const auto &doc : fcb.transportDocument) {
47 if (
auto n = ticketNameForDocument(doc.ticket); !n.isEmpty()) {
56[[nodiscard]]
static QString fcbReference(
const T &data)
58 if constexpr (is_any_of_v<
decltype(data), FCB_VERSIONED(ReservationData), FCB_VERSIONED(OpenTicketData), FCB_VERSIONED(PassData)>) {
59 if (!data.referenceIA5.isEmpty()) {
62 if (data.referenceNumIsSet()) {
69QString FcbExtractor::pnr(
const Fcb::UicRailTicketData &fcb)
71 return std::visit([](
auto &&fcb) {
72 if (!fcb.issuingDetail.issuerPNR.isEmpty()) {
76 for (
const auto &doc : fcb.transportDocument) {
77 auto pnr = std::visit([](
auto &&doc) {
78 return fcbReference(doc);
89QString FcbExtractor::seatingType(
const Fcb::UicRailTicketData &fcb)
91 return std::visit([](
auto &&fcb) {
92 for (
const auto &doc : fcb.transportDocument) {
93 auto s = std::visit([](
auto &&doc) {
94 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(ReservationData), FCB_VERSIONED(OpenTicketData), FCB_VERSIONED(PassData)>) {
107[[nodiscard]]
static QString formatIssuerId(
int num)
111 id.insert(0,
QString(4 -
id.size(),
'0'_L1));
116QString FcbExtractor::issuerId(
const Fcb::UicRailTicketData &fcb)
118 return std::visit([](
auto &&fcb) {
119 if (fcb.issuingDetail.issuerNumIsSet()) {
120 return formatIssuerId(fcb.issuingDetail.issuerNum);
122 if (fcb.issuingDetail.issuerIA5IsSet()) {
125 if (fcb.issuingDetail.securityProviderNumIsSet()) {
126 return formatIssuerId(fcb.issuingDetail.securityProviderNum);
128 if (fcb.issuingDetail.securityProviderIA5IsSet()) {
135Organization FcbExtractor::issuer(
const Fcb::UicRailTicketData &fcb)
138 if (
auto id = issuerId(fcb); !
id.isEmpty()) {
139 issuer.setIdentifier(
"uic:"_L1 +
id);
141 std::visit([&issuer](
auto &&fcb) {
142 if (fcb.issuingDetail.issuerNameIsSet()) {
143 issuer.setName(fcb.issuingDetail.issuerName);
149Person FcbExtractor::person(
const Fcb::UicRailTicketData &fcb)
151 return std::visit([](
auto &&fcb) {
153 if (!fcb.travelerDetailIsSet() || fcb.travelerDetail.traveler.size() != 1) {
156 const auto traveler = fcb.travelerDetail.traveler.at(0);
157 if (traveler.firstNameIsSet() || traveler.secondNameIsSet()) {
158 p.setGivenName(
QString(traveler.firstName +
' '_L1 + traveler.secondName).
trimmed());
160 p.setFamilyName(traveler.lastName);
165QDateTime FcbExtractor::issuingDateTime(
const Fcb::UicRailTicketData &fcb)
167 return std::visit([](
auto &&data) {
return data.issuingDetail.issueingDateTime(); }, fcb);
170QDateTime FcbExtractor::validFrom(
const Fcb::UicRailTicketData &fcb)
172 return std::visit([](
auto &&fcb) {
173 for (
const auto &doc : fcb.transportDocument) {
174 auto dt = std::visit([&fcb](
auto &&doc) {
175 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(ReservationData)>) {
176 return doc.departureDateTime(fcb.issuingDetail.issueingDateTime());
183 dt = std::visit([&fcb](
auto &&doc) {
184 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(OpenTicketData), FCB_VERSIONED(PassData)>) {
185 return doc.validFrom(fcb.issuingDetail.issueingDateTime());
197QDateTime FcbExtractor::validUntil(
const Fcb::UicRailTicketData &fcb)
199 return std::visit([](
auto &&fcb) {
200 for (
const auto &doc : fcb.transportDocument) {
201 auto dt = std::visit([&fcb](
auto &&doc) {
202 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(ReservationData)>) {
203 return doc.arrivalDateTime(fcb.issuingDetail.issueingDateTime());
210 dt = std::visit([&fcb](
auto &&doc) {
211 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(OpenTicketData), FCB_VERSIONED(PassData)>) {
212 return doc.validUntil(fcb.issuingDetail.issueingDateTime());
224FcbExtractor::PriceData FcbExtractor::price(
const Fcb::UicRailTicketData &fcb)
226 return std::visit([](
auto &&fcb) {
229 const auto fract = std::pow(10, fcb.issuingDetail.currencyFract);
230 for (
const auto &doc : fcb.transportDocument) {
231 p.price = std::visit([fract](
auto &&doc) ->
double {
232 if constexpr (is_any_of_v<
decltype(doc), FCB_VERSIONED(ReservationData), FCB_VERSIONED(OpenTicketData), FCB_VERSIONED(PassData)>) {
233 return doc.priceIsSet() ? doc.price / fract : NAN;
237 if (!std::isnan(p.price)) {
245template <
typename CardReferenceTypeT>
249 p.setProgramName(card.cardName);
250 if (card.cardIdNumIsSet()) {
252 }
else if (card.cardIdIA5IsSet()) {
258template <
typename TariffTypeT>
262 for (
const auto &tariff : tariffs) {
263 for (
const auto &card : tariff.reductionCard) {
264 return extractCustomerCard(card);
273 const auto issuingDateTime = FcbExtractor::issuingDateTime(fcb);
274 VariantVisitor([&fcb, &result, ticket, issuingDateTime](
auto &&irt) {
278 trip.setProvider(FcbExtractor::issuer(fcb));
279 if (trip.provider().identifier().
isEmpty() && trip.provider().name().
isEmpty()) {
280 trip.setProvider(ticket.issuedBy());
285 FcbExtractor::readDepartureStation(irt, dep);
286 trip.setDepartureStation(dep);
289 FcbExtractor::readArrivalStation(irt, arr);
290 trip.setArrivalStation(arr);
292 trip.setDepartureTime(irt.departureDateTime(issuingDateTime));
293 trip.setArrivalTime(irt.arrivalDateTime(issuingDateTime));
295 if (irt.trainNumIsSet()) {
296 trip.setTrainNumber(irt.serviceBrandAbrUTF8 +
' '_L1 +
QString::number(irt.trainNum));
298 trip.setTrainNumber(irt.serviceBrandAbrUTF8 +
' '_L1 +
QString::fromUtf8(irt.trainIA5));
303 if (irt.placesIsSet()) {
306 for (
const auto &b : irt.places.placeIA5) {
309 for (
auto i : irt.places.placeNum) {
312 s.setSeatNumber(l.
join(
", "_L1));
315 t.setTicketedSeat(s);
318 res.setReservationNumber(FcbExtractor::pnr(fcb));
319 if (res.reservationNumber().
isEmpty()) {
320 res.setReservationNumber(ticket.ticketNumber());
322 t.setTicketNumber(fcbReference(irt));
323 res.setUnderName(FcbExtractor::person(fcb));
324 res.setProgramMembershipUsed(::extractCustomerCard(irt.tariffs));
326 if (irt.priceIsSet()) {
327 res.setTotalPrice(irt.price / std::pow(10, std::visit([](
auto &&fcb) { return fcb.issuingDetail.currencyFract; }, fcb)));
329 res.setPriceCurrency(
QString::fromUtf8(std::visit([](
auto &&fcb) {
return fcb.issuingDetail.currency; }, fcb)));
334 res.setReservationFor(trip);
335 res.setReservedTicket(t);
338 }).visit<FCB_VERSIONED(ReservationData)>(res);
341template <
typename T,
typename CodeTableType>
344 return std::visit([&baseTrip, stationCodeTable, issuingDateTime, &baseRes, &result](
auto &&trainLink) {
345 if constexpr (is_any_of_v<
decltype(trainLink), FCB_VERSIONED(TrainLinkType)>) {
349 if (trainLink.fromStationNameUTF8IsSet()) {
351 FcbExtractor::readDepartureStation(trainLink, stationCodeTable, dep);
352 trip.setDepartureStation(dep);
355 if (trainLink.toStationNameUTF8IsSet()) {
357 FcbExtractor::readArrivalStation(trainLink, stationCodeTable, arr);
358 trip.setArrivalStation(arr);
361 trip.setDepartureDay({});
362 trip.setDepartureTime(trainLink.departureDateTime(issuingDateTime));
364 if (trainLink.trainNumIsSet()) {
374 res.setReservationFor(trip);
381 }, regionalValidity);
386 const auto issuingDateTime = FcbExtractor::issuingDateTime(fcb);
387 VariantVisitor([&fcb, ticket, &result, issuingDateTime] (
auto &&nrt) {
391 t.setTicketedSeat(s);
394 res.setReservationNumber(FcbExtractor::pnr(fcb));
395 if (res.reservationNumber().
isEmpty()) {
396 res.setReservationNumber(ticket.ticketNumber());
398 t.setTicketNumber(fcbReference(nrt));
400 res.setReservedTicket(t);
402 res.setUnderName(FcbExtractor::person(fcb));
403 res.setProgramMembershipUsed(::extractCustomerCard(nrt.tariffs));
405 if (nrt.priceIsSet()) {
406 res.setTotalPrice(nrt.price / std::pow(10, std::visit([](
auto &&fcb) { return fcb.issuingDetail.currencyFract; }, fcb)));
408 res.setPriceCurrency(
QString::fromUtf8(std::visit([](
auto &&fcb) {
return fcb.issuingDetail.currency; }, fcb)));
411 baseTrip.setProvider(FcbExtractor::issuer(fcb));
412 if (baseTrip.provider().name().
isEmpty() && baseTrip.provider().identifier().
isEmpty()) {
413 baseTrip.setProvider(ticket.issuedBy());
416 FcbExtractor::readDepartureStation(nrt, dep);
417 baseTrip.setDepartureStation(dep);
419 FcbExtractor::readArrivalStation(nrt, arr);
420 baseTrip.setArrivalStation(arr);
421 baseTrip.setDepartureDay(nrt.validFrom(issuingDateTime).date());
427 bool trainLinkTypeFound =
false;
428 for (
const auto ®ionalValidity : nrt.validRegion) {
429 trainLinkTypeFound |= extractValidRegion(regionalValidity.value, nrt.stationCodeTable, issuingDateTime, res, baseTrip, result);
432 if (!trainLinkTypeFound) {
434 res.setReservationFor(baseTrip);
440 if (nrt.returnIncluded) {
442 FcbExtractor::readDepartureStation(nrt.returnDescription, nrt.stationCodeTable, retDep);
444 FcbExtractor::readArrivalStation(nrt.returnDescription, nrt.stationCodeTable, retArr);
447 retBaseTrip.setProvider(baseTrip.provider());
448 retBaseTrip.setDepartureStation(retDep);
449 retBaseTrip.setArrivalStation(retArr);
451 bool retTrainLinkTypeFound =
false;
452 for (
const auto ®ionalValidity : nrt.returnDescription.validReturnRegion) {
453 retTrainLinkTypeFound |= extractValidRegion(regionalValidity.value, nrt.stationCodeTable, issuingDateTime, res, retBaseTrip, result);
456 if (!retTrainLinkTypeFound && validator.
isValidElement(retBaseTrip)) {
457 res.setReservationFor(retBaseTrip);
461 }).visit<FCB_VERSIONED(OpenTicketData)>(res);
466 VariantVisitor([&fcb, &result, ticket](
auto &&ccd) {
468 if (ccd.cardIdNumIsSet()) {
473 pm.setProgramName(ccd.cardTypeDescr);
474 pm.setMember(FcbExtractor::person(fcb));
475 pm.setValidFrom(ccd.validFrom().startOfDay());
476 pm.setValidUntil(ccd.validUntil().startOfDay());
479 }).visit<FCB_VERSIONED(CustomerCardData)>(ccd);
484 VariantVisitor([&station](
auto &&data) {
485 FcbExtractor::readDepartureStation(data, station);
486 }).visit<FCB_VERSIONED(ReservationData), FCB_VERSIONED(OpenTicketData)>(doc);
491 VariantVisitor([&station](
auto &&data) {
492 FcbExtractor::readArrivalStation(data, station);
493 }).visit<FCB_VERSIONED(ReservationData), FCB_VERSIONED(OpenTicketData)>(doc);
503 addr.setAddressCountry(u
"DE"_s);
504 station.setAddress(addr);
505 station.setIdentifier(
QString());
static QString classCodeToString(Fcb::v13::TravelClassType classCode)
Convert a class code enum value to a string for human representation.
QString identifier
Identifier.
A frequent traveler, bonus points or discount scheme program membership.
QString ticketToken
The raw ticket token string.
Classes for reservation/travel data models, data extraction and data augmentation.
void push_back(parameter_type value)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString trimmed() const const
QString join(QChar separator) const const