ti2-tourplan 1.0.123 → 1.0.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/index.js +20 -0
- package/index.test.js +157 -0
- package/itinerary-cancel-booking.js +6 -44
- package/itinerary-confirm-booking.js +72 -0
- package/itinerary-hostconnect-helpers.js +52 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -105,3 +105,68 @@ The plugin returns both:
|
|
|
105
105
|
- `status`: aggregate over all `ServiceStatus.Status` values:
|
|
106
106
|
- same status on all lines => that status (e.g. `XX`)
|
|
107
107
|
- mixed line statuses => `MIXED`
|
|
108
|
+
|
|
109
|
+
## Confirm Booking (HostConnect)
|
|
110
|
+
|
|
111
|
+
This plugin converts a quote to a confirmed booking using HostConnect `QuoteToBookRequest`. Inventory is allocated for each service line when the quote is confirmed; the returned `ServiceStatus` values indicate whether allocation succeeded per line.
|
|
112
|
+
|
|
113
|
+
### HostConnect XML Request
|
|
114
|
+
|
|
115
|
+
```xml
|
|
116
|
+
<Request>
|
|
117
|
+
<QuoteToBookRequest>
|
|
118
|
+
<AgentID>YOUR_AGENT_ID</AgentID>
|
|
119
|
+
<Password>YOUR_AGENT_PASSWORD</Password>
|
|
120
|
+
<BookingId>14226</BookingId>
|
|
121
|
+
</QuoteToBookRequest>
|
|
122
|
+
</Request>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Equivalent curl:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
curl --location 'https://<HOSTCONNECT_ENDPOINT>/api/hostConnectApi' \
|
|
129
|
+
--header 'Content-Type: application/xml; charset=utf-8' \
|
|
130
|
+
--header 'requestId: <uuid>' \
|
|
131
|
+
--header 'Accept: application/xml' \
|
|
132
|
+
--data '<Request>
|
|
133
|
+
<QuoteToBookRequest>
|
|
134
|
+
<AgentID>YOUR_AGENT_ID</AgentID>
|
|
135
|
+
<Password>YOUR_AGENT_PASSWORD</Password>
|
|
136
|
+
<BookingId>14226</BookingId>
|
|
137
|
+
</QuoteToBookRequest>
|
|
138
|
+
</Request>'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### HostConnect XML Response (example)
|
|
142
|
+
|
|
143
|
+
```xml
|
|
144
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
145
|
+
<!DOCTYPE Reply SYSTEM "hostConnect_5_05_010.dtd">
|
|
146
|
+
<Reply>
|
|
147
|
+
<QuoteToBookReply>
|
|
148
|
+
<BookingId>14226</BookingId>
|
|
149
|
+
<Ref>A2IN111975</Ref>
|
|
150
|
+
<ServiceStatuses>
|
|
151
|
+
<ServiceStatus>
|
|
152
|
+
<Ref>A2IN111975</Ref>
|
|
153
|
+
<ServiceLineId>61738</ServiceLineId>
|
|
154
|
+
<Date>2026-07-03</Date>
|
|
155
|
+
<SequenceNumber>10</SequenceNumber>
|
|
156
|
+
<Status>OK</Status>
|
|
157
|
+
</ServiceStatus>
|
|
158
|
+
</ServiceStatuses>
|
|
159
|
+
</QuoteToBookReply>
|
|
160
|
+
</Reply>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Plugin Return Shape
|
|
164
|
+
|
|
165
|
+
The plugin returns both:
|
|
166
|
+
|
|
167
|
+
- `confirmBookingReply`: raw parsed HostConnect `QuoteToBookReply` object
|
|
168
|
+
- `booking`: normalized object
|
|
169
|
+
- `ref`: from `Ref`
|
|
170
|
+
- `id`: from `BookingId` (or the identifier sent in the request)
|
|
171
|
+
- `status`: aggregate over all `ServiceStatus.Status` values (same rules as cancel), with fallbacks to `BookingStatus`, `Status`, `payload.status`, then `'Confirmed'`
|
|
172
|
+
- `serviceLines`: array of `{ ref, serviceLineId, date, sequenceNumber, status }`
|
package/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const Normalizer = require('./normalizer');
|
|
|
11
11
|
const { searchAvailabilityForItinerary } = require('./availability/itinerary-availability');
|
|
12
12
|
const { addServiceToItinerary } = require('./itinerary-add-service');
|
|
13
13
|
const { cancelBooking } = require('./itinerary-cancel-booking');
|
|
14
|
+
const { confirmBooking } = require('./itinerary-confirm-booking');
|
|
14
15
|
const { searchProductsForItinerary } = require('./itinerary-products-search');
|
|
15
16
|
const { searchItineraries } = require('./itinerary-search');
|
|
16
17
|
const { hostConnectXmlOptions } = require('./utils');
|
|
@@ -680,6 +681,9 @@ class BuyerPlugin {
|
|
|
680
681
|
});
|
|
681
682
|
}
|
|
682
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Cancel a booking.
|
|
686
|
+
*/
|
|
683
687
|
async cancelBooking({
|
|
684
688
|
axios,
|
|
685
689
|
token,
|
|
@@ -693,6 +697,22 @@ class BuyerPlugin {
|
|
|
693
697
|
});
|
|
694
698
|
}
|
|
695
699
|
|
|
700
|
+
/**
|
|
701
|
+
* Convert a quote to a confirmed booking.
|
|
702
|
+
*/
|
|
703
|
+
async confirmBooking({
|
|
704
|
+
axios,
|
|
705
|
+
token,
|
|
706
|
+
payload,
|
|
707
|
+
}) {
|
|
708
|
+
return confirmBooking({
|
|
709
|
+
axios,
|
|
710
|
+
token,
|
|
711
|
+
payload,
|
|
712
|
+
callTourplan: this.callTourplan.bind(this),
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
696
716
|
async searchItineraries({
|
|
697
717
|
axios,
|
|
698
718
|
typeDefsAndQueries,
|
package/index.test.js
CHANGED
|
@@ -274,6 +274,163 @@ describe('search tests', () => {
|
|
|
274
274
|
},
|
|
275
275
|
});
|
|
276
276
|
});
|
|
277
|
+
|
|
278
|
+
it('requires a booking id or reference', async () => {
|
|
279
|
+
await expect(app.cancelBooking({
|
|
280
|
+
axios,
|
|
281
|
+
token,
|
|
282
|
+
payload: {},
|
|
283
|
+
})).rejects.toThrow('Must provide booking id or reference for cancellation');
|
|
284
|
+
expect(mockCallTourplan).not.toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
describe('confirmBooking', () => {
|
|
288
|
+
it('converts a quote to a booking by id', async () => {
|
|
289
|
+
mockCallTourplan.mockImplementationOnce(async () => ({
|
|
290
|
+
QuoteToBookReply: {
|
|
291
|
+
BookingId: '14226',
|
|
292
|
+
Ref: 'A2IN111975',
|
|
293
|
+
ServiceStatuses: {
|
|
294
|
+
ServiceStatus: {
|
|
295
|
+
Ref: 'A2IN111975',
|
|
296
|
+
ServiceLineId: '61738',
|
|
297
|
+
Date: '2026-07-03',
|
|
298
|
+
SequenceNumber: '10',
|
|
299
|
+
Status: 'OK',
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
}));
|
|
304
|
+
const retVal = await app.confirmBooking({
|
|
305
|
+
axios,
|
|
306
|
+
token,
|
|
307
|
+
payload: {
|
|
308
|
+
bookingId: '14226',
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
expect(mockCallTourplan).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
312
|
+
model: {
|
|
313
|
+
QuoteToBookRequest: expect.objectContaining({
|
|
314
|
+
BookingId: '14226',
|
|
315
|
+
}),
|
|
316
|
+
},
|
|
317
|
+
}));
|
|
318
|
+
expect(retVal).toEqual({
|
|
319
|
+
confirmBookingReply: {
|
|
320
|
+
BookingId: '14226',
|
|
321
|
+
Ref: 'A2IN111975',
|
|
322
|
+
ServiceStatuses: {
|
|
323
|
+
ServiceStatus: {
|
|
324
|
+
Ref: 'A2IN111975',
|
|
325
|
+
ServiceLineId: '61738',
|
|
326
|
+
Date: '2026-07-03',
|
|
327
|
+
SequenceNumber: '10',
|
|
328
|
+
Status: 'OK',
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
booking: {
|
|
333
|
+
ref: 'A2IN111975',
|
|
334
|
+
id: '14226',
|
|
335
|
+
status: 'OK',
|
|
336
|
+
serviceLines: [{
|
|
337
|
+
ref: 'A2IN111975',
|
|
338
|
+
serviceLineId: '61738',
|
|
339
|
+
date: '2026-07-03',
|
|
340
|
+
sequenceNumber: '10',
|
|
341
|
+
status: 'OK',
|
|
342
|
+
}],
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('converts a quote to a booking by ref', async () => {
|
|
348
|
+
mockCallTourplan.mockImplementationOnce(async () => ({
|
|
349
|
+
QuoteToBookReply: {
|
|
350
|
+
BookingId: '14226',
|
|
351
|
+
Ref: 'A2IN111975',
|
|
352
|
+
ServiceStatuses: {
|
|
353
|
+
ServiceStatus: [{
|
|
354
|
+
ServiceLineId: '61738',
|
|
355
|
+
Date: '2026-07-03',
|
|
356
|
+
SequenceNumber: '10',
|
|
357
|
+
Status: 'OK',
|
|
358
|
+
}],
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
}));
|
|
362
|
+
const retVal = await app.confirmBooking({
|
|
363
|
+
axios,
|
|
364
|
+
token,
|
|
365
|
+
payload: {
|
|
366
|
+
ref: 'A2IN111975',
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
expect(mockCallTourplan).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
370
|
+
model: {
|
|
371
|
+
QuoteToBookRequest: expect.objectContaining({
|
|
372
|
+
Ref: 'A2IN111975',
|
|
373
|
+
}),
|
|
374
|
+
},
|
|
375
|
+
}));
|
|
376
|
+
expect(retVal.booking.id).toBe('14226');
|
|
377
|
+
expect(retVal.booking.ref).toBe('A2IN111975');
|
|
378
|
+
expect(retVal.booking.status).toBe('OK');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('returns MIXED status when service line statuses differ', async () => {
|
|
382
|
+
mockCallTourplan.mockImplementationOnce(async () => ({
|
|
383
|
+
QuoteToBookReply: {
|
|
384
|
+
BookingId: '14226',
|
|
385
|
+
Ref: 'A2IN111975',
|
|
386
|
+
ServiceStatuses: {
|
|
387
|
+
ServiceStatus: [{
|
|
388
|
+
ServiceLineId: '61738',
|
|
389
|
+
Status: 'OK',
|
|
390
|
+
}, {
|
|
391
|
+
ServiceLineId: '61739',
|
|
392
|
+
Status: 'RQ',
|
|
393
|
+
}],
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
}));
|
|
397
|
+
const retVal = await app.confirmBooking({
|
|
398
|
+
axios,
|
|
399
|
+
token,
|
|
400
|
+
payload: {
|
|
401
|
+
bookingId: '14226',
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
expect(retVal.booking.status).toBe('MIXED');
|
|
405
|
+
expect(retVal.booking.serviceLines).toHaveLength(2);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('requires a booking id or reference', async () => {
|
|
409
|
+
await expect(app.confirmBooking({
|
|
410
|
+
axios,
|
|
411
|
+
token,
|
|
412
|
+
payload: {},
|
|
413
|
+
})).rejects.toThrow('Must provide booking id or reference for confirm booking');
|
|
414
|
+
expect(mockCallTourplan).not.toHaveBeenCalled();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('falls back to Confirmed when no service statuses are returned', async () => {
|
|
418
|
+
mockCallTourplan.mockImplementationOnce(async () => ({
|
|
419
|
+
QuoteToBookReply: {
|
|
420
|
+
BookingId: '14226',
|
|
421
|
+
Ref: 'A2IN111975',
|
|
422
|
+
},
|
|
423
|
+
}));
|
|
424
|
+
const retVal = await app.confirmBooking({
|
|
425
|
+
axios,
|
|
426
|
+
token,
|
|
427
|
+
payload: {
|
|
428
|
+
bookingId: '14226',
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
expect(retVal.booking.status).toBe('Confirmed');
|
|
432
|
+
expect(retVal.booking.serviceLines).toEqual([]);
|
|
433
|
+
});
|
|
277
434
|
});
|
|
278
435
|
describe('template tests', () => {
|
|
279
436
|
let template;
|
|
@@ -2,49 +2,17 @@ const assert = require('assert');
|
|
|
2
2
|
const R = require('ramda');
|
|
3
3
|
const {
|
|
4
4
|
hostConnectXmlOptions,
|
|
5
|
-
escapeInvalidXmlChars,
|
|
6
5
|
} = require('./utils');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
field: 'BookingId',
|
|
13
|
-
value: String(bookingId),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
const bookingRef = payload.ref || payload.reference;
|
|
17
|
-
if (bookingRef) {
|
|
18
|
-
return {
|
|
19
|
-
field: 'Ref',
|
|
20
|
-
value: escapeInvalidXmlChars(String(bookingRef)),
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
};
|
|
6
|
+
const {
|
|
7
|
+
getBookingIdentifier,
|
|
8
|
+
getAggregateServiceStatus,
|
|
9
|
+
} = require('./itinerary-hostconnect-helpers');
|
|
25
10
|
|
|
26
11
|
const extractCancellationReply = replyObj => (
|
|
27
12
|
R.path(['CancelServicesReply'], replyObj)
|
|
28
13
|
|| {}
|
|
29
14
|
);
|
|
30
15
|
|
|
31
|
-
const getServiceStatusList = cancellationReply => {
|
|
32
|
-
let serviceStatuses = R.pathOr([], ['ServiceStatuses', 'ServiceStatus'], cancellationReply);
|
|
33
|
-
if (!Array.isArray(serviceStatuses)) serviceStatuses = [serviceStatuses];
|
|
34
|
-
return serviceStatuses
|
|
35
|
-
.map(serviceStatus => R.path(['Status'], serviceStatus))
|
|
36
|
-
.filter(Boolean)
|
|
37
|
-
.map(status => String(status).toUpperCase());
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const getAggregateServiceStatus = cancellationReply => {
|
|
41
|
-
const statuses = getServiceStatusList(cancellationReply);
|
|
42
|
-
if (!statuses.length) return null;
|
|
43
|
-
const uniqueStatuses = [...new Set(statuses)];
|
|
44
|
-
if (uniqueStatuses.length === 1) return uniqueStatuses[0];
|
|
45
|
-
return 'MIXED';
|
|
46
|
-
};
|
|
47
|
-
|
|
48
16
|
const normalizeCancellationResponse = (replyObj, payload, identifier) => {
|
|
49
17
|
const cancellationReply = extractCancellationReply(replyObj);
|
|
50
18
|
const aggregateServiceStatus = getAggregateServiceStatus(cancellationReply);
|
|
@@ -62,13 +30,7 @@ const normalizeCancellationResponse = (replyObj, payload, identifier) => {
|
|
|
62
30
|
};
|
|
63
31
|
|
|
64
32
|
/**
|
|
65
|
-
* Cancels a booking by id or reference
|
|
66
|
-
* @param {Object} params - The parameters for the cancellation
|
|
67
|
-
* @param {Object} params.axios - The axios instance
|
|
68
|
-
* @param {Object} params.token - The token for the cancellation
|
|
69
|
-
* @param {Object} params.payload - The payload for the cancellation
|
|
70
|
-
* @param {Object} params.callTourplan - The callTourplan function
|
|
71
|
-
* @returns {Promise<Object>} The cancellation response
|
|
33
|
+
* Cancels a booking by id or reference via HostConnect CancelServicesRequest.
|
|
72
34
|
*/
|
|
73
35
|
const cancelBooking = async ({
|
|
74
36
|
axios,
|
|
@@ -83,7 +45,7 @@ const cancelBooking = async ({
|
|
|
83
45
|
assert(hostConnectEndpoint, 'Must provide token.hostConnectEndpoint for booking cancellation');
|
|
84
46
|
assert(hostConnectAgentID, 'Must provide token.hostConnectAgentID for booking cancellation');
|
|
85
47
|
assert(hostConnectAgentPassword, 'Must provide token.hostConnectAgentPassword for booking cancellation');
|
|
86
|
-
const identifier =
|
|
48
|
+
const identifier = getBookingIdentifier(payload);
|
|
87
49
|
assert(identifier, 'Must provide booking id or reference for cancellation');
|
|
88
50
|
|
|
89
51
|
const baseRequest = {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const R = require('ramda');
|
|
3
|
+
const { hostConnectXmlOptions } = require('./utils');
|
|
4
|
+
const {
|
|
5
|
+
getBookingIdentifier,
|
|
6
|
+
getAggregateServiceStatus,
|
|
7
|
+
mapServiceStatusLines,
|
|
8
|
+
} = require('./itinerary-hostconnect-helpers');
|
|
9
|
+
|
|
10
|
+
const extractConfirmBookingReply = replyObj => (
|
|
11
|
+
R.path(['QuoteToBookReply'], replyObj)
|
|
12
|
+
|| {}
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const normalizeConfirmBookingResponse = (replyObj, payload, identifier) => {
|
|
16
|
+
const confirmBookingReply = extractConfirmBookingReply(replyObj);
|
|
17
|
+
const aggregateServiceStatus = getAggregateServiceStatus(confirmBookingReply);
|
|
18
|
+
return {
|
|
19
|
+
confirmBookingReply,
|
|
20
|
+
booking: {
|
|
21
|
+
ref: R.path(['Ref'], confirmBookingReply),
|
|
22
|
+
id: R.path(['BookingId'], confirmBookingReply) || identifier.value,
|
|
23
|
+
status: aggregateServiceStatus
|
|
24
|
+
|| R.path(['BookingStatus'], confirmBookingReply)
|
|
25
|
+
|| R.path(['Status'], confirmBookingReply)
|
|
26
|
+
|| payload.status
|
|
27
|
+
|| 'Confirmed',
|
|
28
|
+
serviceLines: mapServiceStatusLines(confirmBookingReply),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Converts a quote to a confirmed booking via HostConnect QuoteToBookRequest.
|
|
35
|
+
* Inventory is allocated for each service line; returned ServiceStatus values
|
|
36
|
+
* indicate whether allocation succeeded per line.
|
|
37
|
+
*/
|
|
38
|
+
const confirmBooking = async ({
|
|
39
|
+
axios,
|
|
40
|
+
token: {
|
|
41
|
+
hostConnectEndpoint,
|
|
42
|
+
hostConnectAgentID,
|
|
43
|
+
hostConnectAgentPassword,
|
|
44
|
+
},
|
|
45
|
+
payload = {},
|
|
46
|
+
callTourplan,
|
|
47
|
+
}) => {
|
|
48
|
+
assert(hostConnectEndpoint, 'Must provide token.hostConnectEndpoint for confirm booking');
|
|
49
|
+
assert(hostConnectAgentID, 'Must provide token.hostConnectAgentID for confirm booking');
|
|
50
|
+
assert(hostConnectAgentPassword, 'Must provide token.hostConnectAgentPassword for confirm booking');
|
|
51
|
+
const identifier = getBookingIdentifier(payload);
|
|
52
|
+
assert(identifier, 'Must provide booking id or reference for confirm booking');
|
|
53
|
+
|
|
54
|
+
const baseRequest = {
|
|
55
|
+
AgentID: hostConnectAgentID,
|
|
56
|
+
Password: hostConnectAgentPassword,
|
|
57
|
+
[identifier.field]: identifier.value,
|
|
58
|
+
};
|
|
59
|
+
const replyObj = await callTourplan({
|
|
60
|
+
model: {
|
|
61
|
+
QuoteToBookRequest: baseRequest,
|
|
62
|
+
},
|
|
63
|
+
endpoint: hostConnectEndpoint,
|
|
64
|
+
axios,
|
|
65
|
+
xmlOptions: hostConnectXmlOptions,
|
|
66
|
+
});
|
|
67
|
+
return normalizeConfirmBookingResponse(replyObj, payload, identifier);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
confirmBooking,
|
|
72
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const R = require('ramda');
|
|
2
|
+
const { escapeInvalidXmlChars } = require('./utils');
|
|
3
|
+
|
|
4
|
+
const getBookingIdentifier = payload => {
|
|
5
|
+
const bookingId = payload.bookingId || payload.id;
|
|
6
|
+
if (bookingId) {
|
|
7
|
+
return {
|
|
8
|
+
field: 'BookingId',
|
|
9
|
+
value: String(bookingId),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const bookingRef = payload.ref || payload.reference;
|
|
13
|
+
if (bookingRef) {
|
|
14
|
+
return {
|
|
15
|
+
field: 'Ref',
|
|
16
|
+
value: escapeInvalidXmlChars(String(bookingRef)),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const toServiceStatusArray = reply => {
|
|
23
|
+
let serviceStatuses = R.pathOr([], ['ServiceStatuses', 'ServiceStatus'], reply);
|
|
24
|
+
if (!Array.isArray(serviceStatuses)) serviceStatuses = [serviceStatuses];
|
|
25
|
+
return serviceStatuses.filter(Boolean);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getAggregateServiceStatus = reply => {
|
|
29
|
+
const statuses = toServiceStatusArray(reply)
|
|
30
|
+
.map(serviceStatus => R.path(['Status'], serviceStatus))
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.map(status => String(status).toUpperCase());
|
|
33
|
+
if (!statuses.length) return null;
|
|
34
|
+
const uniqueStatuses = [...new Set(statuses)];
|
|
35
|
+
if (uniqueStatuses.length === 1) return uniqueStatuses[0];
|
|
36
|
+
return 'MIXED';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const mapServiceStatusLines = reply => toServiceStatusArray(reply).map(serviceStatus => ({
|
|
40
|
+
ref: R.path(['Ref'], serviceStatus),
|
|
41
|
+
serviceLineId: R.path(['ServiceLineId'], serviceStatus),
|
|
42
|
+
date: R.path(['Date'], serviceStatus),
|
|
43
|
+
sequenceNumber: R.path(['SequenceNumber'], serviceStatus),
|
|
44
|
+
status: R.path(['Status'], serviceStatus),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
getBookingIdentifier,
|
|
49
|
+
toServiceStatusArray,
|
|
50
|
+
getAggregateServiceStatus,
|
|
51
|
+
mapServiceStatusLines,
|
|
52
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ti2-tourplan",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.124",
|
|
4
4
|
"description": "Tourplan's TI2 Plugin",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"jsonwebtoken": "^8.5.1",
|
|
40
40
|
"moment": "^2.29.1",
|
|
41
41
|
"ramda": "^0.27.1",
|
|
42
|
-
"ti2": "^1.0.
|
|
42
|
+
"ti2": "^1.0.118",
|
|
43
43
|
"underscore": "^1.13.4",
|
|
44
44
|
"xml-js": "^1.6.11",
|
|
45
45
|
"xml2js": "^0.4.23"
|