package shoppingsession import ( "errors" "fmt" "time" "github.com/cockroachdb/apd/v3" "github.com/google/uuid" ) // date/time formats const ( DateTimeFormat = "2006-01-02T15:04:05.000Z" LocalDateTimeFormat = "2006-01-02T15:04:05" ) // DateTime e.g. 2018-10-10T19:10:59.164Z type DateTime struct{ time.Time } // LocalDateTime e.g. 2018-06-22T18:00:00 type LocalDateTime struct{ time.Time } // ShoppingSession - contains inventory with user selections type ShoppingSession struct { ArrangerID *uuid.UUID `json:"arrangerId" format:"uuid" faker:"uuid"` ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` Meta Meta `json:"meta"` RailInventory RailInventory `json:"railInventory"` FareDetails *FareDetails `json:"fareDetails"` ShoppingContext ShoppingContext `json:"shoppingContext" validate:"required"` // Travel Ids to use for shopping context TravelerID uuid.UUID `json:"travelerId" validate:"required" format:"uuid" faker:"uuid"` CostSummary CostSummary `json:"costSummary"` } // Meta - contains metadata for shopping session type Meta struct { CreatedTime string `json:"createdTime" validate:"required"` LastModifiedTime string `json:"lastModifiedTime" validate:"required"` State string `json:"state"` } // RailInventory - contains information about the requested connections type RailInventory struct { FareCodes []FareCode `json:"fareCodes" validate:"required,omitempty,dive"` // FareCodes comprising the fares FareRule *FareRules `json:"fareRule,omitempty"` // generic and specific fare rules Fares []InventoryFare `json:"fares" validate:"required,omitempty,dive"` // All available fares Journeys [][]Journey `json:"journeys" validate:"required,omitempty,dive,required,dive"` // Available journeys grouped by leg SearchCriteria SearchCriteria `json:"searchCriteria" validate:"required"` // The search criteria elected by the user RailCards []RailCard `json:"railCards" validate:"required,omitempty,dive"` // List of Rail Cards that are both supported by the vendor(s) and available on the traveler's profile Selections UserSelections `json:"selections" validate:"required"` // User selections } // FareCode Describes what can be purchased (class of service, ancillaries, etc.) for one segment type FareCode struct { FareClass string `json:"fareClass" validate:"required"` // The carrier specific class of service FareCode string `json:"fareCode" validate:"required"` // The fare code as presented by the supplier ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` ServiceClass string `json:"serviceClass" validate:"required,omitempty,oneof=FIRST SECOND" faker:"oneof=FIRST SECOND"` SupplierClassCode string `json:"supplierClassCode,omitempty"` // The supplier specific code for class of service } // InventoryFare Describes the cost and conditions for what can be purchased for one or more segments type InventoryFare struct { AmountBeforeDiscount *apd.Decimal `json:"amountBeforeDiscount,omitempty"` // The amount before discount for the fare BaseAmount *float64 `json:"baseAmount,omitempty"` // The base amount for the fare Currency string `json:"currency" validate:"required"` // The currency used in this fare DiscountCode string `json:"discountCode,omitempty"` // The discount code on the fare DisplayName string `json:"displayName" validate:"required"` // The fare name as defined by the supplier and displayed to end-user FareCodeIDS []uuid.UUID `json:"fareCodeIds" validate:"required,min=1,dive,required" format:"uuid" faker:"uuidslice"` // fare codes which can be reserved ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` RailCardProgramCodes []string `json:"railCardProgramCodes" validate:"required"` // List of cards used to get the discounted fare RestrictedToFaresIDS []uuid.UUID `json:"restrictedToFaresIds" validate:"required,omitempty,dive" format:"uuid" faker:"uuidslice"` // List of fares that can be combined with this fare. When empty array, no restrictions apply RuleID uuid.UUID `json:"ruleId,omitempty" format:"uuid" faker:"uuid"` // Reference to a FareRule applicable to this particular fare Source string `json:"source" validate:"required,oneof=renfe trainline" faker:"oneof=renfe trainline"` // The supplier we obtained this fare from SupplierCode string `json:"supplierCode,omitempty"` // Identifier of the fare used in external system SurchargeAmount float64 `json:"surchargeAmount,omitempty"` // The total surcharge amount for the fare TaxAmount float64 `json:"taxAmount,omitempty"` // The total tax amount for the fare TotalAmount float64 `json:"totalAmount"` // The total amount for the fare Type string `json:"type,omitempty" validate:"omitempty,oneof=SINGLE RETURN OPEN_RETURN" faker:"oneof=SINGLE RETURN OPEN_RETURN"` // The type of fare. TODO RAIL-13183: Make it required } // FareRules generic and specific fare rules type FareRules struct { Fares []FareRule `json:"fares" validate:"required,omitempty,dive"` // Fare rules applicable to fares in this inventory Generic *AdditionalRulesText `json:"generic,omitempty"` // generic fare rule } // FareRule - contains fare rule description type FareRule struct { AdditionalRules *AdditionalRulesText `json:"additionalRules,omitempty"` // Fare rules in a textual format. This is used either in addition or instead of the structured cancellation and exchange rules. CancellationRules []CancellationRulesItem `json:"cancellationRules" validate:"required,omitempty,dive"` // List of rules relating to cancellation of the fare. To be evaluated as ordered by expiration. ExchangeRules []ExchangeRulesItem `json:"exchangeRules" validate:"required,omitempty,dive"` // List of rules relating to exchangeability of the fare. To be evaluated as ordered by expiration. ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` // id of the fare rule, used as a reference by fare to match the rule PermittedOrigins []RailStation `json:"permittedOrigins" validate:"required,omitempty,dive"` // List of origin stations that this fare may be used for. PermittedDestinations []RailStation `json:"permittedDestinations" validate:"required,omitempty,dive"` // List of destination stations that this fare may be used for. RegulationLinks []RegulationLink `json:"regulationLinks" validate:"required,omitempty,dive"` // List of regulation links applicable to the fare. RouteRestriction *LocalizedString `json:"routeRestriction,omitempty"` // Restrictions on the routes that this fare may be used on. Seating string `json:"seating,omitempty" validate:"omitempty,oneof=SEATMAP SEAT_PAID PREFERENCES NONE" faker:"oneof=SEATMAP SEAT_PAID PREFERENCES NONE"` // SEATMAP: Seat selection is available free of charge. SEAT_PAID: Only paid seat selection is available. PREFERENCES: Only seat preferences may be specified. NONE: All seating options are unavailable. SupplierReferences []FareRuleSupplierReference `json:"supplierReferences" validate:"required,omitempty,dive"` // References to supplier resources that are used to populate these fare rules. } // RegulationLink - contains regulation (PRR or NCR) link type RegulationLink struct { Type string `json:"type" validate:"required,oneof=PASSENGER_RIGHTS_REGULATION NATIONAL_RAIL_CONDITIONS" faker:"oneof=PASSENGER_RIGHTS_REGULATION NATIONAL_RAIL_CONDITIONS"` URL string `json:"url" validate:"required"` // a url to regulation site } // AdditionalRulesText - contains fare rule description type AdditionalRulesText struct { Paragraphs []AdditionalRulesParagraph `json:"paragraphs" validate:"required,omitempty,dive"` URL string `json:"url"` // a url to the original source of the fare rules } // AdditionalRulesParagraph - one paragraph of the fare rule description type AdditionalRulesParagraph struct { Language string `json:"language,omitempty"` // language designator according to BCP 47 Value string `json:"value,omitempty"` // the paragraph text Title string `json:"title,omitempty"` // the title of the paragraph } // LocalizedString A string in a particular language type LocalizedString struct { Language string `json:"language,omitempty"` // language designator according to BCP 47 Value string `json:"value,omitempty"` // the localized string itself } // CancellationRulesItem - Cancellation rules type CancellationRulesItem struct { Expiration *DateTime `json:"expiration,omitempty"` // a point in time until which this particular rule applies (UTC). Null means the rule applies without a limit. Penalty *Penalty `json:"penalty,omitempty"` // A penalty that applies to this particular rule Type string `json:"type" validate:"required,omitempty,oneof=ALLOWED FORBIDDEN" faker:"oneof=ALLOWED FORBIDDEN"` // Type of the rule specifying whether exchange is allowed or forbidden before the specified expiration Voucher string `json:"voucher,omitempty" validate:"omitempty,oneof=MANDATORY OPTIONAL NONE" faker:"oneof=MANDATORY OPTIONAL NONE"` // specifies whether a refund is returned in the form of a voucher for future travel } // Penalty - exchange or cancelation penalty type Penalty struct { Currency string `json:"currency"` // Currency ISO code, optional Type string `json:"type" validate:"required,omitempty,oneof=PERCENTAGE CURRENCY" faker:"oneof=PERCENTAGE CURRENCY"` Value float64 `json:"value"` // the size of the penalty, in percents or specified currency (based on type) } // ExchangeRulesItem - Exchange rules type ExchangeRulesItem struct { Expiration *DateTime `json:"expiration,omitempty"` // a point in time until which this particular rule applies (UTC). Null means the rule applies without a limit. Penalty *Penalty `json:"penalty,omitempty"` // A penalty that applies to this particular rule Type string `json:"type" validate:"required,omitempty,oneof=ALLOWED FORBIDDEN" faker:"oneof=ALLOWED FORBIDDEN"` // Type of the rule specifying whether exchange is allowed or forbidden before the specified expiration } // FareRuleSupplierReference - Reference to a supplier resource that is used to populate fare rules type FareRuleSupplierReference struct { Type string `json:"type"` // Type of the supplier reference Value string `json:"value"` // Value of the supplier reference IsLoaded bool `json:"isLoaded"` // Indicates whether the referenced data has already been retrieved from the supplier } // Journey Describes a way to get from an origin to a destination across one or more segments type Journey struct { ArrivalDateTime LocalDateTime `json:"arrivalDateTime" validate:"required"` // The date this leg ends DepartureDateTime LocalDateTime `json:"departureDateTime" validate:"required"` // The date this journey begins Destination RailStation `json:"destination" validate:"required"` // The destination station ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` Origin RailStation `json:"origin" validate:"required"` // The origin station PolicyCompliance *JourneyFareCompliance `json:"policyCompliance,omitempty"` Segments []InventorySegment `json:"segments" validate:"required,min=1,dive,required"` // The list of connecting segments that compose this journey SupplierCode string `json:"supplierCode,omitempty"` // The journey code used by the supplier Co2EmissionInGramsPerPassenger *int `json:"co2EmissionInGramsPerPassenger,omitempty"` // Per passenger grams-equivalent carbon dioxide footprint for this journey } // RailStation Rail station type RailStation struct { City string `json:"city,omitempty"` // The city name of the station Country string `json:"country,omitempty"` // The country name of the station CountryCode string `json:"countryCode" validate:"omitempty,len=2"` // Country code of the rail station, eg. 'ES' (using ISO 3166-1 alpha-2 standard) ID string `json:"id,omitempty"` // Reference to places store, optional Latitude *float64 `json:"latitude,omitempty"` // The latitude of the station Longitude *float64 `json:"longitude,omitempty"` // The longitude of the station Name string `json:"name" validate:"required"` // Name of the rail station SupplierCode string `json:"supplierCode" validate:"required"` // The station code used by the supplier Timezone string `json:"timezone,omitempty"` // The timezone of the station } // JourneyFareCompliance - fares which are breaking violation rules type JourneyFareCompliance struct { BatchID uuid.UUID `json:"batchId" validate:"required" format:"uuid" faker:"uuid"` // Id added by policy to denote the batch of inventory evaluated. Default value implies no corresponding batch. Fares []FaresItem `json:"fares" validate:"required,dive"` SkipViolationReason bool `json:"skipViolationReason"` // Whether skip violation reason } // FaresItem - Contains reference to the fare and the list of the broken rules type FaresItem struct { FareID uuid.UUID `json:"fareId" validate:"required" format:"uuid" faker:"uuid"` // The id of the fare which is one of the permissible fares that can be purchased for this journey RuleMatches []RuleMatch `json:"ruleMatches" validate:"required,dive"` // Array of rules that matched the journey fare. If a rules action overrides another rule the rule that is overridden will not be in this array. ruleMatches will not be defined if no rules match. } // RuleMatch - contains violation rule type RuleMatch struct { Action string `json:"action" validate:"required,omitempty,oneof=ALLOW REQUIRE_APPROVAL REQUIRE_PRE_APPROVAL_N_LOG REQUIRE_PRE_APPROVAL_N_NOTIFY REQUIRE_PASSIVE_APPROVAL NOTIFY_MANAGER LOG_FOR_REPORTS HIDE_RESULTS DISPLAY_MESSAGE SHOW_DO_NOT_ALLOW" faker:"oneof=ALLOW REQUIRE_APPROVAL REQUIRE_PRE_APPROVAL_N_LOG REQUIRE_PRE_APPROVAL_N_NOTIFY REQUIRE_PASSIVE_APPROVAL NOTIFY_MANAGER LOG_FOR_REPORTS HIDE_RESULTS DISPLAY_MESSAGE SHOW_DO_NOT_ALLOW"` // Action to apply to rate. Message string `json:"message,omitempty"` // The message associated with the rule. This is needed often, even if rate is not selected (displayed by UI). RuleValueID string `json:"ruleValueId" validate:"required"` // ruleValueId of rule that this rate matched } // InventorySegment Describes a direct (no switching of equipment) way to get from an origin to a destination type InventorySegment struct { ArrivalDateTime LocalDateTime `json:"arrivalDateTime" validate:"required"` // The date this segment ends Carrier string `json:"carrier,omitempty"` // The code of the company operating the train on this segment CarrierName string `json:"carrierName,omitempty"` // The human readable name of the carrier DepartureDateTime LocalDateTime `json:"departureDateTime" validate:"required"` // The date this segment begins Destination RailStation `json:"destination" validate:"required"` // The destination station Duration float64 `json:"duration" validate:"gte=0"` // duration of the segment in minutes EquipmentType string `json:"equipmentType,omitempty"` // Type of train serving this segment (ICE/Re/High-speed...) FareCodes []uuid.UUID `json:"fareCodes" validate:"required,dive,required" format:"uuid" faker:"uuidslice"` // identifiers of available fare codes on the segment ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` Origin RailStation `json:"origin" validate:"required"` // The origin station SegmentNumber int `json:"segmentNumber" validate:"gt=0"` // The segment sequence number, starting from 1 TrainNumber string `json:"trainNumber,omitempty"` // Train reporting number as specified by the operator } // SearchCriteria Rail search criteria type SearchCriteria struct { Journeys []JourneySearchCriteria `json:"journeys" validate:"required,omitempty,dive"` Type string `json:"type,omitempty" validate:"omitempty,oneof=ONE_WAY ROUND_TRIP OPEN_RETURN"` // The type of search to execute. TODO RAIL-13182: Make it required } // JourneySearchCriteria Rail search criteria for a single journey in a trip (e.g. outbound journey) type JourneySearchCriteria struct { DateTime LocalDateTime `json:"dateTime" validate:"required"` // The desired departure time from origin station (search by arrival to be added later) From string `json:"from" validate:"required"` // The origin station represented by a reference to places store To string `json:"to" validate:"required"` // The destination station represented by a reference to places store FromLocation JourneySearchCriteriaLocation `json:"fromLocation" validate:"required"` // The origin station ToLocation JourneySearchCriteriaLocation `json:"toLocation" validate:"required"` // The destination station } // JourneySearchCriteriaLocation Rail search criteria location (e.g. origin or destination) type JourneySearchCriteriaLocation struct { ID string `json:"id" validate:"required"` // Reference to Omnisearch location Name string `json:"name"` // Name of the location } type RailCard struct { ProgramCode string `json:"programCode" validate:"required"` ProgramName string `json:"programName" validate:"required"` } // UserSelections single journey and fare selected by the user type UserSelections struct { FopUUID uuid.UUID `json:"fopUuid" format:"uuid" faker:"uuid"` // The uuid of the form of payment selected for booking JourneySelections []JourneySelection `json:"journeySelections" validate:"required,omitempty,dive"` // References to journeys and fares selected by the user LoyaltyID string `json:"loyaltyId,omitempty"` // customer loyalty account ID selected by the user SeatPreference *SeatPreferenceSelection `json:"seatPreference"` // References user's seat preferences SeatSelections []SeatSelection `json:"seatSelections" validate:"required,omitempty,dive"` // User selected seats EmailID string `json:"emailId" validate:"required"` // The id of the email selected for booking PhoneNumberID string `json:"phoneNumberId" validate:"required"` // The id of the phone selected for booking TicketDeliveryType string `json:"ticketDeliveryType" validate:"omitempty,oneof=EMAIL ETICKET KIOSK MAIL PASSBOOK PICKUP PRINT SMS URL"` // The type of ticket delivery that was selected TravelerRuleViolations []TravelerRuleViolation `json:"travelerRuleViolations" validate:"required,omitempty,dive"` } // JourneySelection single journey and fare selected by the user type JourneySelection struct { FareIDS []uuid.UUID `json:"fareIds" validate:"required,min=1,dive,required" format:"uuid" faker:"uuidslice"` JourneyID uuid.UUID `json:"journeyId" validate:"required" format:"uuid" faker:"uuid"` } // SeatPreferenceSelection Describe seat preferences type SeatPreferenceSelection struct { Coach string `json:"coach" validate:"omitempty,oneof=COMPARTMENT SLEEPER" faker:"oneof=COMPARTMENT SLEEPER"` Deck string `json:"deck" validate:"omitempty,oneof=UPPER LOWER" faker:"oneof=UPPER LOWER"` Direction string `json:"direction" validate:"omitempty,oneof=AIRLINE BACK FORWARD" faker:"oneof=AIRLINE BACK FORWARD"` NearPreference string `json:"nearPreference" validate:"omitempty,oneof=LUGGAGE_RACK TOILET" faker:"oneof=LUGGAGE_RACK TOILET"` NoiseComfort string `json:"noiseComfort" validate:"omitempty,oneof=QUIET" faker:"oneof=QUIET"` PhysicalRequirement string `json:"physicalRequirement" validate:"omitempty,oneof=POWER_SOCKET TABLE VIDEO" faker:"oneof=POWER_SOCKET TABLE VIDEO"` Position string `json:"position" validate:"omitempty,oneof=AISLE INDIVIDUAL ISOLATED_WINDOW MIDDLE PRIORITY_SEATING WINDOW" faker:"oneof=AISLE INDIVIDUAL ISOLATED_WINDOW MIDDLE PRIORITY_SEATING WINDOW"` Type string `json:"type" validate:"omitempty,oneof=HALL TABLE HANDICAPPED" faker:"oneof=HALL TABLE HANDICAPPED"` } // SeatSelection Describe specific seat selection done using seat map data type SeatSelection struct { CarriageNumber string `json:"carriageNumber" validate:"required"` // Designation of carriage within a train SeatNumber string `json:"seatNumber" validate:"required"` // Designation of seat within a carriage SegmentID uuid.UUID `json:"segmentId" validate:"required" format:"uuid" faker:"uuid"` // Reference to the segment to which the seat reservation applies } // TravelerRuleViolation - Contains travelr comments on violation rules type TravelerRuleViolation struct { CreatedTime string `json:"createdTime" validate:"required,ne="` RuleIDS []string `json:"ruleIds" validate:"required,min=1,dive,required"` // An array of rule ids matched/violated SegmentIDS []uuid.UUID `json:"segmentIds" validate:"required,min=1,dive,required" format:"uuid" faker:"uuidslice"` // An array of segment ids associated for each rule violation TravelerComments string `json:"travelerComments"` // Comments provided by the traveler about the rule match. ViolationReasonCode string `json:"violationReasonCode" validate:"required"` // violation reason code configured in config. ViolationReasonDescription string `json:"violationReasonDescription"` // Description of the violation reason code. ViolationReasonID uuid.UUID `json:"violationReasonId" validate:"required" format:"uuid" faker:"uuid"` // Id of violation reason code in config. } // ShoppingContext Travel Ids to use for shopping context type ShoppingContext struct { CompanyID uuid.UUID `json:"companyId" validate:"required" format:"uuid" faker:"uuid"` RuleClassID string `json:"ruleClassId" validate:"required"` SetID string `json:"setId"` TravelConfigID string `json:"travelConfigId" validate:"required"` TripID uuid.UUID `json:"tripId" format:"uuid" faker:"uuid"` // Existing trip context where shopping session booking to be added VirtualPayRecordLocatorID string `json:"virtualPayRecordLocatorId"` // A record locator Id used for virtual pay instead of using the booking Id } // FareDetails contains available options for the selected connections and fares type FareDetails struct { SeatPreferenceOptions SeatPreferenceOptions `json:"seatPreferenceOptions"` SupplierReservationID string `json:"supplierReservationId,omitempty"` Reservability string `json:"reservability" validate:"required,oneof=MANDATORY NONE OPTIONAL" faker:"oneof=MANDATORY NONE OPTIONAL"` SeatPreferences []SeatPreference `json:"seatPreferences" validate:"required,dive"` PaymentOptions []PaymentOption `json:"paymentOptions" validate:"required,dive"` TicketDeliveries []TicketDelivery `json:"ticketDeliveries" validate:"required,dive"` BookingFee *FeeAmount `json:"bookingFee,omitempty"` } // SeatPreference describes single seat preference available for a fare type SeatPreference struct { SupplierCode string `json:"supplierCode"` Coaches []CoachPreference `json:"coaches" validate:"required,omitempty,dive"` Decks []DeckPreference `json:"decks" validate:"required,omitempty,dive"` NearPreferences []NearPreference `json:"nearPreferences" validate:"required,omitempty,dive"` NoiseComforts []NoiseComfortPreference `json:"noiseComforts" validate:"required,omitempty,dive"` PhysicalRequirements []PhysicalRequirementPreference `json:"physicalRequirements" validate:"required,omitempty,dive"` Directions []SeatDirectionPreference `json:"directions" validate:"required,omitempty,dive"` Positions []SeatPositionPreference `json:"positions" validate:"required,omitempty,dive"` } // CoachPreference represents coach preference type CoachPreference struct { SupplierCode string `json:"supplierCode"` Coach string `json:"coach" validate:"oneof=COMPARTMENT SLEEPER" faker:"oneof=COMPARTMENT SLEEPER"` } // DeckPreference represents deck preference type DeckPreference struct { SupplierCode string `json:"supplierCode"` Deck string `json:"deck" validate:"oneof=UPPER LOWER" faker:"oneof=UPPER LOWER"` } // NearPreference represents near preference type NearPreference struct { SupplierCode string `json:"supplierCode"` NearPreference string `json:"nearPreference" validate:"oneof=LUGGAGE_RACK TOILET" faker:"oneof=LUGGAGE_RACK TOILET"` } // NoiseComfortPreference represents noise comfort preference type NoiseComfortPreference struct { SupplierCode string `json:"supplierCode"` NoiseComfort string `json:"noiseComfort" validate:"oneof=QUIET" faker:"oneof=QUIET"` } // PhysicalRequirementPreference represents physical requirement preference type PhysicalRequirementPreference struct { SupplierCode string `json:"supplierCode"` PhysicalRequirement string `json:"physicalRequirement" validate:"oneof=POWER_SOCKET TABLE VIDEO" faker:"oneof=POWER_SOCKET TABLE VIDEO"` } // SeatDirectionPreference represents direction preference type SeatDirectionPreference struct { SupplierCode string `json:"supplierCode"` Direction string `json:"direction" validate:"oneof=AIRLINE BACK FORWARD" faker:"oneof=AIRLINE BACK FORWARD"` } // SeatPositionPreference represents position preference type SeatPositionPreference struct { SupplierCode string `json:"supplierCode"` Position string `json:"position" validate:"oneof=AISLE INDIVIDUAL ISOLATED_WINDOW MIDDLE PRIORITY_SEATING WINDOW" faker:"oneof=AISLE INDIVIDUAL ISOLATED_WINDOW MIDDLE PRIORITY_SEATING WINDOW"` } // SeatPreferenceOptions contains all of the supported seat preferences type SeatPreferenceOptions struct { Coaches []string `json:"coaches,omitempty" validate:"omitempty,dive,oneof=COMPARTMENT SLEEPER" faker:"oneof=COMPARTMENT SLEEPER"` Decks []string `json:"decks,omitempty" validate:"omitempty,dive,oneof=UPPER LOWER" faker:"oneof=UPPER LOWER"` Directions []string `json:"directions,omitempty" validate:"omitempty,dive,oneof=FORWARD BACK" faker:"oneof=FORWARD BACK"` Positions []string `json:"positions,omitempty" validate:"omitempty,dive,oneof=AISLE WINDOW ISOLATED_WINDOW MIDDLE" faker:"oneof=AISLE WINDOW ISOLATED_WINDOW MIDDLE"` Types []string `json:"types,omitempty" validate:"omitempty,dive,oneof=HALL TABLE HANDICAPPED" faker:"oneof=HALL TABLE HANDICAPPED"` } // PaymentOption describes single payment option type PaymentOption struct { ID uuid.UUID `json:"id" faker:"uuid" format:"uuid" validate:"required"` Fee *FeeAmount `json:"fee,omitempty"` Type string `json:"type,omitempty" validate:"omitempty,oneof=ON_ACCOUNT CREDIT_CARD" faker:"oneof=ON_ACCOUNT CREDIT_CARD"` } // TicketDelivery describes single a single ticket delivery option type TicketDelivery struct { SupplierCode string `json:"supplierCode,omitempty"` Type string `json:"type,omitempty" validate:"omitempty,oneof=EMAIL ETICKET KIOSK MAIL PASSBOOK PICKUP PRINT SMS URL" faker:"oneof=EMAIL ETICKET KIOSK MAIL PASSBOOK PICKUP PRINT SMS URL"` Fee *FeeAmount `json:"fee,omitempty"` IsCollectableAtOriginStation *bool `json:"isCollectableAtOriginStation,omitempty"` // Indicates whether the traveler can collect the ticket at the origin station or not. null/missing if unknown or irrelevant. CollectionInformationURL string `json:"collectionInformationUrl,omitempty"` // Contains the URL that the user can navigate to to find more information about the collection for this ticket delivery. null/missing if unknown or irrelevant. } // Cost represents a monetary amount and the corresponding currency type Cost struct { Amount *apd.Decimal `json:"amount" validate:"required" faker:"decimal"` Currency string `json:"currency" validate:"required"` } // For backward compatibility type FeeAmount = Cost // CostSummary - contains total cost and cost breakdown type CostSummary struct { TotalCost Cost `json:"totalCost"` CostBreakdown CostBreakdown `json:"costBreakdown"` } // CostBreakdown - contains multiple booking cost line items type CostBreakdown struct { BookingCosts []BookingCost `json:"bookingCosts"` } // BookingCost - contains data for a single booking cost line item, e.g. base fare, taxes, fees, with hierarchical structure type BookingCost struct { ID uuid.UUID `json:"id" validate:"required" format:"uuid" faker:"uuid"` ParentID *uuid.UUID `json:"parentId" format:"uuid" faker:"uuid"` Type string `json:"type" validate:"required,oneof=FEE TAX BASEAMOUNT ANCILLARY GROUP REFUND PENALTY" faker:"oneof=FEE TAX BASEAMOUNT ANCILLARY GROUP REFUND PENALTY"` Code string `json:"code"` Localization *CostLocalization `json:"localization"` Included *bool `json:"included"` Cost *Cost `json:"cost"` Discount *BookingCostDiscount `json:"discount"` } type BookingCostDiscount struct { DiscountedCost Cost `json:"discountedCost" validate:"required"` Localization *CostLocalization `json:"localization" validate:"required"` Reason string `json:"reason" validate:"required"` } // CostLocalization - contains localized text for a cost line item type CostLocalization struct { ID string `json:"id"` MessageParams []LocalizationMessageParameter `json:"messageParams"` LocalizedText string `json:"localizedText"` } // LocalizationMessageParameter - contains a single parameter for a localized text type LocalizationMessageParameter struct { Name string `json:"name" validate:"required"` Value string `json:"value" validate:"required"` Type string `json:"type" validate:"required"` } func (t DateTime) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"%s"`, t.Format(DateTimeFormat))), nil } // UnmarshalJSON stores parsed time.Time object wrapped into struct // // Also validate input if it starts with '"' and ends with '"' // and length is greater than 2 (we also should count 2 quotes) // to prevent panic of the code func (t *DateTime) UnmarshalJSON(data []byte) error { if len(data) < 2 { return errors.New("DateTime is empty") } if data[0] != '"' || data[len(data)-1] != '"' { return errors.New("format of the DateTime is invalid") } ts, err := time.Parse(DateTimeFormat, string(data[1:len(data)-1])) if err != nil { return fmt.Errorf("failed to parse DateTime: %w", err) } *t = DateTime{Time: ts} return nil } func (t LocalDateTime) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"%s"`, t.Format(LocalDateTimeFormat))), nil } // UnmarshalJSON stores parsed time.Time object wrapped into struct // // Also validate input if it starts with '"' and ends with '"' // and length is greater than 2 (we also should count 2 quotes) // to prevent panic of the code func (t *LocalDateTime) UnmarshalJSON(data []byte) error { if len(data) < 2 { return errors.New("locacDateTime uses wrong date format") } if data[0] != '"' || data[len(data)-1] != '"' { return errors.New("format of the DateTime is invalid") } ts, err := time.Parse(LocalDateTimeFormat, string(data[1:len(data)-1])) if err != nil { return errors.New("locacDateTime unable to parse: " + err.Error()) } *t = LocalDateTime{Time: ts} return nil }