# WIP - V3 Migration guide

The required changes from Offer V2 to V3 are relatively minor, compared to the effort needed to migrate from V1.
Below is a summary of most changes.

## Spec changes

The biggest change is that the spec now only contains the endpoints and models available for partners.

Operation Ids have been simplified, new information has been added about security schema and required permissions.
Tags have been renamed to be better in line with standards. Error description has been simplified.

Headers for rate limiting and Offer expiration have been documented.

## Simple structure changes
Some objects have been merged, as they detailed the same information. Some objects have been renamed.
Any field referencing TariffZones has been removed. TariffZone is no longer an accepted input. Deprecated fields have been removed.

### Headers
| Header              | Old value                         | New value                         |
|---------------------|-----------------------------------|-----------------------------------|
| Skip-Offers-Caching | true/false                        | Replaced by separate endpoints    |
| ResponseType        | SearchResponse/SearchResponseFull | -                                 |
| Accept-Language     | nob/nno/eng                       | nb/nn/en per Entur api guidelines |

### Objects
| Old name                   | New name                    |
|----------------------------|-----------------------------|
| AvailableFulfillmentMethod | AvailableFulfillment        |
| PriceSummary               | Price                       |
| RefType                    | ObjectRef                   |
| SearchWithAuthorityRequest | SearchAuthorityRequest      |
| SearchZonesRequest         | SearchGeoLocationRequest    |
| SearchStopPlaceRequest     | SearchGeoLocationRequest    |
| ZonalValiditySummary       | ZonalValidity               |

### Fields
| Object             | Old value                   | New value                                                     |
|--------------------|-----------------------------|---------------------------------------------------------------|
| ApiError           | error                       | title                                                         |
| ApiError           | message                     | detail                                                        |
| OfferSummary       | availableFulfillmentMethods | availableFulfilmentMethods (spelling change)                  |
| OfferSummary       | -                           | salesPackageId                                                |
| OfferSummary       | -                           | salesPackageVersion                                           |
| OptionalProduct    | discountRight               | discountRights, type change to array                          |
| PreassignedProduct | discountRight               | discountRights, type change to array                          |
| PropertiesSummary  | -                           | nuisanceFacilities                                            |
| SearchResponse     | -                           | notices                                                       |
| UnavailableProduct | -                           | name                                                          |
| ZonalValidity      | -                           | See [separate description](#redesign-of-geographicalvalidity) |

## Enums
Nearly all enum types have been removed, having been replaced by strings.
This is to increase flexibility and bring the Offers API more in line with what is returned from JourneyPlanner.
Lists of known values will be provided, however the client **must** implement fallback logic to handle unknown values.
It is expected that new values will be added during the lifespan of this major version.
We do not guarantee a grace period between introducing new values and using them.

The only enums remaining are those used as input, as well as those used when categorizing a Recommendation.

### Renaming of enum values
To bring Offers more in line with the NeTEx standard, the following Enums have been renamed.
Please note this includes both the actual enum used for both requests and string values returned in responses.

| Enum name             | Old value          | New value       |
|-----------------------|--------------------|-----------------|
| UserType              | YOUTH              | YOUNG_PERSON    |
| AccommodationFacility | FAMILY_COMPARTMENT | FAMILY_CARRIAGE |
| AccommodationFacility | RECLINING_SEAT     | RECLINING_SEATS |

## Merging of endpoints
The endpoints of `/offers/v2/zones` and `/offers/v2/stop-places` have been merged into one endpoint `/offers/v3/search/geographical-location`.
This endpoint supports searching by FareZones, StopPlaces or a mix. The endpoint also supports filtering on transport mode.
If the transport mode is absent, all transport modes will be considered unless both StopPlaces are rail stations.

The different combinations of inputs in `SearchGeoLocationRequest` are summarized below.

| From      | To        | Result                                                                                                              |
|-----------|-----------|---------------------------------------------------------------------------------------------------------------------|
| FareZone  | FareZone  | Same behaviour as `/offers/v2/zones`                                                                                |
| StopPlace | StopPlace | Same behaviour as `/offers/v2/stop-places`                                                                          |
| StopPlace | FareZone  | Offers will automatically select the `FareZone` from the `StopPlace` with the same authority as the to `FareZone`   |
| FareZone  | StopPlace | Offers will automatically select the `FareZone` from the `StopPlace` with the same authority as the from `FareZone` |

## Redesign of Traveller 
Several fields on Traveller relating to customer identity and functionality specific to identified individuals were grouped into a new structure `TravellerContext`.
`ExistingTicket` on root level was removed, so the client no longer needs to differentiate between one and more travellers, except for `cappingSpecification`.

**V2:**
```json
{
    "id": "ID_A",
    "customerId": "1234567",
    "productIds": [
      "ENT:PreassignedFareProduct:SupplementaryTicket"
    ],
    "cappingSpecification": {
      "customerRef": {
        "accountBasedTicketingCustomerRef": "ENT:CustomerAccount:12345678"
      },
      "fareContractId": "ENT:FareContract:1234567u8iop",
      "cappedDiscountRightId": "ENT:CappedDiscountRight:Bestepris"
    }
}
```

**V3:**
```json
{
    "id": "ID_A",
    "travellerContext": {
      "customerIdentity": [
        {
          "customerRef": "1234567",
          "source": "CUSTOMERS"
        },
        {
          "customerRef": "ENT:CustomerAccount:12345678",
          "source": "ABT"
        }
      ],
      "ownedRights": [
        {
          "id": "ENT:PreassignedFareProduct:SupplementaryTicket",
          "zones": ["ENT:FareZone:1", "ENT:FareZone:2"]
        },
        {
          "id": "ENT:FareContract:1234567u8iop",
          "cappingSpecification": {
            "cappedDiscountRightId": "ENT:CappedDiscountRight:Bestepris"
          }
        }
      ]
    }
}
```

### Customer identity
Multiple sources for customer identity are supported. Old fields have been removed.

| Old location                                                      | New location                                                        |
|-------------------------------------------------------------------|---------------------------------------------------------------------|
| customerId                                                        | travellerContext.customerIdentity[].customerRef (source: CUSTOMERS) |
| cappingSpecification.customerRef.accountBasedTicketingCustomerRef | travellerContext.customerIdentity[].customerRef (source: ABT)       |

### Preowned products

ExistingTicket has been moved from root request level to Traveller replacing `productIds` and renamed to OwnedRight.
FareContracts should be provided here, and have been removed from `cappingSpecification`, while `cappingSpecification` itself
has also been moved into OwnedRight.

## Redesign of GeographicalValidity

PointToPointValidity now includes display names for the stop places. `fromPlace` and `toPlace` have been changed from plain strings to named references.

ZonalValidity now contains some new fields: `fromZone` and `toZone` with named references, and an _unordered_ set of `viaZones`.
FromZone/toZone/viaZones will _not_ be populated when a `groupOfTariffZones` is included.

## Restructure UnavailableProducts
UnavailableProducts no longer guarantees the presence of serviceJourney, if that information is not available. 
This means UnavailableProducts can be returned also from endpoints other than `/offers/v3/search/trip-pattern`. In addition, product name is included for display purposes.

## Notices - Beta
Occasionally, some underlying services may not be responding. Previously, that error has been ignored and partial offers have been returned. 
In order to help clients display information to the customers, a response may include an array of `notices`, describing potentially missing information. 
Examples include, but not limited to, missing quota, seating or personalisation. It is up to the client to determine if this is useful.

## Recommendations
The `recommendations` field in `SearchResponse` has changed from an array of `Recommendation` to a `RecommendationResult` object.
Recommendations are partitioned into two lists based on journey coverage. Partial recommendations still carry the `combineWithToCoverEntireJourney` field, 
describing how to combine them with other recommendations to achieve full journey coverage. The field `id` has been added to `Recommendation`, while `combineWithToCoverEntireJourney`
now refers to this `id` rather than a whole `Recommendation`.

**V2:**
```json
{
  "recommendations": [{}]
}
```

**V3:**
```json
{
  "recommendations": {
    "coveringEntireJourney": [{}],
    "coveringPartOfJourney": [{}]
  }
}
```

| Object                       | Old value                        | New value                        |
|------------------------------|----------------------------------|----------------------------------|
| AccommodationFacility        | ANY_FACILITY_SET                 | ANY                              |
| DurationEnum                 | -                                | ANY                              |
| JourneyOrganizeAlgorithmEnum | FOR_EACH_AND_GROUPED             | -                                |
| Recommendation               | -                                | id                               |
| RuleSpec                     | mixinOffersWithHigherFlexibility | mixInOffersWithHigherFlexibility |
| RuleSpec                     | removeSplitRailReplacementOffers | -                                |

### Default value changes
Default values have been updated to reflect the recommended approach.

| Field                                     | V2 default | V3 default  |
|-------------------------------------------|------------|-------------|
| RuleSpec.onlyIncludeRecommendedOffers     | false      | true        |
| RuleSpec.mixInOffersWithHigherFlexibility | false      | true        |
| RuleSpec.mixInOffersWithOtherFacilitySets | false      | true        |
| RuleSpec.priceComparisonAlgorithm         | BEFORE_SDR | TOTAL_PRICE |
| CategorySpec.typesOfRecommendation        | -          | CHEAPEST    |
| CategorySpec.durationTypes                | -          | ANY         |
| CategorySpec.fareClasses                  | -          | ANY         |
| CategorySpec.facilitySet                  | -          | ANY         |

## Behavioural changes

### Searching with existing ticket
In Offers V2, if the provided existingTicket referred to a zone not included in the trip, an exception was thrown. 
This has been changed to evaluating the trip as normal, which removes the necessity of the client knowing whether the previously owned zone is relevant.

### Searching for passed departures
Searching for a departure which has passed will now result in an exception (404 Not Found) instead of empty offers.

### Mode for alternative transport
When a rail service has been replaced, the actual `mode` will be returned instead of `mode: RAIL` 

### Enforcement of api restrictions
Multiple travellers in the same request identified with the same customer number will be rejected.

Empty arrays are no longer accepted as input in any field.
