Skip to content

Commit 770f441

Browse files
authored
feat(booking): add private fares types (#692)
* refactor(booking): add CreateOfferRequestPassenger interface * feat(booking): add private_fares field to CreateOfferRequest interface * feat(booking): add fare_type field to CreateOfferRequestPassenger interface * feat(booking): add private_fares field to Offer interface * fix(booking): update field types to be optional * test(booking): add test for private fares
1 parent ee0a173 commit 770f441

File tree

5 files changed

+237
-11
lines changed

5 files changed

+237
-11
lines changed

src/booking/OfferRequests/OfferRequests.spec.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import nock from 'nock'
22
import { Client } from '../../Client'
3-
import { CreateOfferRequest } from './OfferRequestsTypes'
3+
import { CreateOfferRequest, OfferRequest } from './OfferRequestsTypes'
44
import { mockCreateOfferRequest, mockOfferRequest } from './mockOfferRequest'
55
import { OfferRequests } from './OfferRequests'
6+
import { OfferPrivateFare } from 'types'
67

78
describe('OfferRequests', () => {
89
afterEach(() => {
@@ -127,4 +128,77 @@ describe('OfferRequests', () => {
127128
// In this case, `offers` won't be in the response, but the offer request's id will still be returned and can be used with the List Offers endpoint to retrieve the offers.
128129
expect(response.data?.id).toBe(mockOfferRequest.id)
129130
})
131+
132+
test('should create an offer request and return an offer with corporate private fares', async () => {
133+
const mockCorporatePrivateFare: OfferPrivateFare = {
134+
corporate_code: 'FLX53',
135+
tracking_reference: 'ABN:2345678',
136+
type: 'corporate',
137+
}
138+
139+
const mockResponseWithCorporatePrivateFares: OfferRequest = {
140+
...mockOfferRequest,
141+
offers: [
142+
{
143+
...mockOfferRequest.offers[0],
144+
private_fares: [mockCorporatePrivateFare],
145+
},
146+
],
147+
}
148+
149+
nock(/(.*)/)
150+
.post(`/air/offer_requests/`)
151+
.reply(200, { data: mockResponseWithCorporatePrivateFares })
152+
153+
const response = await new OfferRequests(
154+
new Client({ token: 'mockToken ' })
155+
).create({
156+
...mockCreateOfferRequest,
157+
private_fares: {
158+
QF: [
159+
{
160+
corporate_code: 'FLX53',
161+
tracking_reference: 'ABN:2345678',
162+
},
163+
],
164+
},
165+
})
166+
167+
expect(response.data.offers[0].private_fares).toHaveLength(1)
168+
expect(response.data.offers[0].private_fares[0].type).toBe('corporate')
169+
})
170+
171+
test('should create an offer request and return an offer with lesuire private fares', async () => {
172+
const mockLeisurePrivateFare: OfferPrivateFare = {
173+
type: 'leisure',
174+
}
175+
176+
const mockResponseWithLeisurePrivateFare: OfferRequest = {
177+
...mockOfferRequest,
178+
offers: [
179+
{
180+
...mockOfferRequest.offers[0],
181+
private_fares: [mockLeisurePrivateFare],
182+
},
183+
],
184+
}
185+
186+
nock(/(.*)/)
187+
.post(`/air/offer_requests/`)
188+
.reply(200, { data: mockResponseWithLeisurePrivateFare })
189+
190+
const response = await new OfferRequests(
191+
new Client({ token: 'mockToken ' })
192+
).create({
193+
...mockCreateOfferRequest,
194+
passengers: [
195+
{
196+
fare_type: 'teacher',
197+
},
198+
],
199+
})
200+
201+
expect(response.data.offers[0].private_fares).toHaveLength(1)
202+
expect(response.data.offers[0].private_fares[0].type).toBe('leisure')
203+
})
130204
})

src/booking/OfferRequests/OfferRequestsTypes.ts

Lines changed: 138 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,117 @@ export interface OfferRequestSlice {
3434
destination_type: PlaceType
3535
}
3636

37+
interface CreateOfferRequestPassengerCommon {
38+
/**
39+
* The passenger's family name. Only `space`, `-`, `'`, and letters from the
40+
* `ASCII`, `Latin-1 Supplement` and `Latin Extended-A` (with the exceptions
41+
* of `Æ`, `æ`, `IJ`, `ij`, `Œ`, `œ`, `Þ`, and `ð`) Unicode charts are accepted.
42+
* All other characters will result in a validation error. The minimum length
43+
* is 1 character, and the maximum is 20 characters.
44+
*
45+
* This is only required if you're also including
46+
* **Loyalty Programme Accounts**.
47+
*/
48+
family_name?: string
49+
50+
/**
51+
* The passenger's given name. Only `space`, `-`, `'`, and letters from the
52+
* `ASCII`, `Latin-1 Supplement` and `Latin Extended-A` (with the exceptions
53+
* of `Æ`, `æ`, `IJ`, `ij`, `Œ`, `œ`, `Þ`, and `ð`) Unicode charts are accepted.
54+
* All other characters will result in a validation error. The minimum length
55+
* is 1 character, and the maximum is 20 characters.
56+
*
57+
* This is only required if you're also including
58+
* **Loyalty Programme Accounts**.
59+
*/
60+
given_name?: string
61+
62+
/**
63+
* The **Loyalty Programme Accounts** for this passenger.
64+
*/
65+
loyalty_programme_accounts?: LoyaltyProgrammeAccount[]
66+
}
67+
68+
interface CreateOfferRequestAdultPassenger
69+
extends CreateOfferRequestPassengerCommon {
70+
age?: never
71+
fare_type?: never
72+
/**
73+
* The type of the passenger. If the passenger is aged 18 or over, you should
74+
* specify a `type` of `adult`. If a passenger is aged under 18, you should
75+
* specify their `age` instead of a `type`. A passenger can have only a type
76+
* or an age, but not both.
77+
*/
78+
type: Extract<DuffelPassengerType, 'adult'>
79+
}
80+
81+
interface CreateOfferRequestNonAdultPassenger
82+
extends CreateOfferRequestPassengerCommon {
83+
/**
84+
* The age of the passenger on the `departure_date` of the final slice. e.g.
85+
* if you a searching for a round trip and the passenger is 15 years old at
86+
* the time of the outbound flight, but they then have their birthday and are
87+
* 16 years old for the inbound flight, you must set the age to 16. You should
88+
* specify an `age` for passengers who are under 18 years old. A passenger can
89+
* have only a type or an age, but not both. You can optionally pass age with
90+
* `fare_type` though.
91+
*/
92+
age: number
93+
fare_type?: never
94+
type?: never
95+
}
96+
97+
export type CreateOfferRequestPassengerFareType =
98+
| 'accompanying_adult'
99+
| 'contract_bulk'
100+
| 'contract_bulk_child'
101+
| 'contract_bulk_infant_with_seat'
102+
| 'contract_bulk_infant_without_seat'
103+
| 'frequent_flyer'
104+
| 'group_inclusive_tour'
105+
| 'group_inclusive_tour_child'
106+
| 'humanitarian'
107+
| 'individual_inclusive_tour_child'
108+
| 'marine'
109+
| 'seat_only'
110+
| 'student'
111+
| 'teacher'
112+
| 'tour_operator_inclusive'
113+
| 'tour_operator_inclusive_infant'
114+
| 'unaccompanied_child'
115+
| 'visiting_friends_and_family'
116+
117+
interface CreateOfferRequestPassengerWithFareType
118+
extends CreateOfferRequestPassengerCommon {
119+
/**
120+
* The age of the passenger on the `departure_date` of the final slice. e.g.
121+
* if you a searching for a round trip and the passenger is 15 years old at
122+
* the time of the outbound flight, but they then have their birthday and are
123+
* 16 years old for the inbound flight, you must set the age to 16. You should
124+
* specify an `age` for passengers who are under 18 years old. A passenger can
125+
* have only a type or an age, but not both. You can optionally pass age with
126+
* `fare_type` though.
127+
*/
128+
age?: number
129+
130+
/**
131+
* The fare type of the passenger. If the passenger is aged less than 18, you
132+
* should pass in age as well.
133+
*/
134+
fare_type: CreateOfferRequestPassengerFareType
135+
type?: never
136+
}
137+
138+
export type CreateOfferRequestPassenger =
139+
| CreateOfferRequestAdultPassenger
140+
| CreateOfferRequestNonAdultPassenger
141+
| CreateOfferRequestPassengerWithFareType
142+
143+
export interface CreateOfferRequestPrivateFare {
144+
corporate_code: string
145+
tracking_reference: string
146+
}
147+
37148
/**
38149
* The passengers who want to travel. A passenger will have only a type or an age.
39150
*/
@@ -143,27 +254,44 @@ export interface OfferRequest {
143254

144255
export interface CreateOfferRequest {
145256
/**
146-
* The cabin that the passengers want to travel in
257+
* The cabin that the passengers want to travel in.
147258
*/
148259
cabin_class?: CabinClass
149260

150261
/**
151-
* The passengers who want to travel.
152-
* If you specify an age for a passenger, the type may differ for the same passenger in different offers due to airline's different rules. e.g. one airline may treat a 14 year old as an adult, and another as a young adult.
262+
* The maximum number of connections within any slice of the offer. For
263+
* example 0 means a direct flight which will have a single segment within
264+
* each slice and 1 means a maximum of two segments within each slice of the
265+
* offer.
153266
*/
154-
passengers: Omit<OfferRequestPassenger, 'id'>[]
267+
max_connections?: 0 | 1 | 2
155268

156269
/**
157-
* The [slices](https://duffel.com/docs/api/overview/key-principles) that make up this offer request.
158-
* One-way journeys can be expressed using one slice, whereas return trips will need two.
270+
* The passengers who want to travel. If you specify an `age` for a passenger,
271+
* the `type` may differ for the same passenger in different offers due to
272+
* airline's different rules. E.g. one airline may treat a 14 year old as an
273+
* adult, and another as a young adult. You may only specify an `age` or a
274+
* `type` – not both.
159275
*/
160-
slices: Omit<OfferRequestSlice, 'origin_type' | 'destination_type'>[]
276+
passengers: CreateOfferRequestPassenger[]
161277

162278
/**
163-
* The maximum number of connections within any slice of the offer.
164-
* For example 0 means a direct flight which will have a single segment within each slice and 1 means a maximum of two segments within each slice of the offer.
279+
* The private fare codes for this Offer Request. You can pass in multiple
280+
* airlines with their specific private fare codes. The key is the airline's
281+
* IATA code that provided the private fare code. The `corporate_code` is
282+
* provided to you by the airline and the `tracking_reference` is to identify
283+
* your business by the airlines.
165284
*/
166-
max_connections?: 0 | 1 | 2
285+
private_fares?: {
286+
[iataCode: string]: CreateOfferRequestPrivateFare[]
287+
}
288+
289+
/**
290+
* The [slices](https://duffel.com/docs/api/overview/key-principles) that make
291+
* up this offer request. One-way journeys can be expressed using one slice,
292+
* whereas return trips will need two.
293+
*/
294+
slices: Omit<OfferRequestSlice, 'origin_type' | 'destination_type'>[]
167295
}
168296

169297
export interface CreateOfferRequestQueryParameters {

src/booking/Offers/OfferTypes.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ export interface Offer {
9191
*/
9292
payment_requirements: PaymentRequirements
9393

94+
/**
95+
* The private fares applied on this offer.
96+
*/
97+
private_fares: OfferPrivateFare[]
98+
9499
/**
95100
* The slices that make up this offer. Each slice will include one or more segments,
96101
* the specific flights that the airline is offering to take the passengers from the slice's `origin` to its `destination`.
@@ -291,6 +296,23 @@ export interface PaymentRequirements {
291296
requires_instant_payment: boolean
292297
}
293298

299+
export interface OfferPrivateFare {
300+
/**
301+
* The corporate code that was applied, if any.
302+
*/
303+
corporate_code?: string
304+
305+
/**
306+
* The tracking reference that was applied, if any.
307+
*/
308+
tracking_reference?: string
309+
310+
/**
311+
* The type of private fare applied.
312+
*/
313+
type: 'corporate' | 'leisure' | 'negotiated'
314+
}
315+
294316
export interface OfferPassenger {
295317
/**
296318
* The age of the passenger on the departure_date of the final slice.

src/booking/Offers/mockOffer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export const mockOffer: Offer = {
140140
price_guarantee_expires_at: '2020-01-17T10:42:14.545Z',
141141
payment_required_by: '2020-01-17T10:42:14.545Z',
142142
},
143+
private_fares: [],
143144
partial: false,
144145
passengers: [
145146
{

src/booking/Offers/mockPartialOffer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export const mockPartialOffer: Offer = {
140140
price_guarantee_expires_at: '2020-01-17T10:42:14.545Z',
141141
payment_required_by: '2020-01-17T10:42:14.545Z',
142142
},
143+
private_fares: [],
143144
partial: true,
144145
passengers: [
145146
{

0 commit comments

Comments
 (0)