ti2-tourplan 1.0.28 → 1.0.30

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/index.js CHANGED
@@ -364,7 +364,14 @@ class Plugin {
364
364
  xmlOptions: hostConnectXmlOptions,
365
365
  };
366
366
  // use cache if we are getting the full list
367
- const replyObj = await this.callTourplan(payload);
367
+ const replyObj = optionId
368
+ ? await this.callTourplan(payload)
369
+ : await this.cache.getOrExec({
370
+ fnParams: [model],
371
+ fn: () => this.callTourplan(payload),
372
+ ttl: 60 * 60 * 24, // 24 hours
373
+ forceRefresh: Boolean(forceRefresh),
374
+ });
368
375
  let products = [];
369
376
  if (replyObj === 'useFixture') {
370
377
  products = require('./__fixtures__/fullacoptionlist.json');
@@ -396,74 +403,86 @@ class Plugin {
396
403
  },
397
404
  ), replyObj);
398
405
  }
399
- const productFields = [{
400
- id: 'productId',
401
- title: 'Supplier',
402
- type: 'extended-option',
403
- requiredForAvailability: true,
404
- requiredForBooking: true,
405
- // options: products.map(p => ({
406
- // value: p.productId,
407
- // label: p.productName,
408
- // })),
409
- }, {
410
- id: 'optionId',
411
- title: 'Service',
412
- type: 'extended-option',
413
- requiredForAvailability: true,
414
- requiredForBooking: true,
415
- filterableBy: 'productId',
416
- // options: R.chain(p => p.options.map(o => ({
417
- // label: o.optionName,
418
- // value: o.optionId,
419
- // productId: p.productId,
420
- // productName: p.productName,
421
- // })), products),
422
- }, {
423
- id: 'startDate',
424
- title: 'Date',
425
- type: 'date',
426
- requiredForAvailability: true,
427
- requiredForBooking: true,
428
- }, {
429
- id: 'paxConfigs',
430
- title: 'Pax',
431
- type: 'list_of_fields',
432
- requiredForAvailability: true,
433
- requiredForBooking: true,
434
- fields: [{
435
- id: 'adults',
436
- title: 'Adults',
437
- type: 'count',
438
- }, {
439
- id: 'children',
440
- title: 'Children',
441
- type: 'count',
442
- }, {
443
- id: 'infants',
444
- title: 'Infants',
445
- type: 'count',
446
- }, {
447
- id: 'roomType',
448
- title: 'Room Type',
449
- type: 'extended-option',
450
- options: [{ value: 'SG', label: 'Single' }, { value: 'DB', label: 'Double' }, { value: 'TW', label: 'Twin' }, { value: 'QD', label: 'Quad' }],
451
- }],
452
- requiredForCalendar: true,
453
- }, {
454
- id: 'chargeUnitQuanity', // secondary charge unit (SCU) quantity
455
- description: 'number of nights or days or hours depending on charge unit',
456
- title: 'Nights/Days',
457
- type: 'count',
458
- }];
459
406
  return {
460
407
  products,
461
- productFields,
408
+ productFields: [],
462
409
  ...configuration,
463
410
  };
464
411
  }
465
412
 
466
413
 
414
+ async searchAvailability({
415
+ axios,
416
+ token: {
417
+ hostConnectEndpoint,
418
+ hostConnectAgentID,
419
+ hostConnectAgentPassword,
420
+ },
421
+ payload: {
422
+ optionId,
423
+ startDate,
424
+ reference,
425
+ /*
426
+ paxConfigs: [{ roomType: 'DB', adults: 2 }, { roomType: 'TW', children: 2 }]
427
+ */
428
+ paxConfigs,
429
+ // passengers,
430
+ /*
431
+ The number of second charge units required (second charge units are discussed
432
+ in the OptionInfo section). Should only be specified for options that have SCUs.
433
+ Defaults to 1.
434
+ */
435
+ chargeUnitQuanity,
436
+ },
437
+ }) {
438
+ const model = {
439
+ OptionInfoRequest: {
440
+ Opt: optionId,
441
+ Info: 'A',
442
+ DateFrom: startDate,
443
+ DateTo: startDate,
444
+ RoomConfigs: paxConfigs.map(obj => {
445
+ const RoomConfig = {
446
+ Adults: obj.adults || 0,
447
+ Children: obj.children || 0,
448
+ Infants: obj.infants || 0,
449
+ };
450
+ const RoomType = ({
451
+ Single: 'SG',
452
+ Double: 'DB',
453
+ Twin: 'TW',
454
+ Triple: 'TR',
455
+ Quad: 'QU',
456
+ })[obj.roomType];
457
+ if (RoomType) RoomConfig.RoomType = RoomType;
458
+ return { RoomConfig };
459
+ }),
460
+ AgentID: hostConnectAgentID,
461
+ Password: hostConnectAgentPassword,
462
+ },
463
+ };
464
+ const replyObj = await this.callTourplan({
465
+ model,
466
+ endpoint: hostConnectEndpoint,
467
+ axios,
468
+ xmlOptions: hostConnectXmlOptions,
469
+ });
470
+ const optAvail = R.path(['OptionInfoReply', 'Option', 'OptAvail'], replyObj);
471
+ /*
472
+ FROM TP DOCS:
473
+ Each integer in the list gives the availability for one of the days in the range requested,
474
+ from the start date through to the end date. The integer values are to be interpreted as
475
+ follows:
476
+ Greater than 0 means that inventory is available, with the integer specifying the
477
+ number of units available. For options with a service type of Y , the inventory is in
478
+ units of rooms. For other service types, the inventory is in units of pax.
479
+ -1 Not available.
480
+ -2 Available on free sell.
481
+ -3 Available on request.
482
+ Note: A return value of 0 or something less than -3 is impossible.
483
+ */
484
+ }
485
+
467
486
  async searchQuote({
468
487
  axios,
469
488
  token: {
@@ -473,6 +492,7 @@ class Plugin {
473
492
  },
474
493
  payload: {
475
494
  quoteName,
495
+ rateId,
476
496
  quoteId,
477
497
  // existingQuoteId,
478
498
  // existingLineId,
@@ -483,13 +503,15 @@ class Plugin {
483
503
  paxConfigs: [{ roomType: 'DB', adults: 2 }, { roomType: 'TW', children: 2 }]
484
504
  */
485
505
  paxConfigs,
486
- // passengers,
487
506
  /*
488
507
  The number of second charge units required (second charge units are discussed
489
508
  in the OptionInfo section). Should only be specified for options that have SCUs.
490
509
  Defaults to 1.
491
510
  */
492
511
  chargeUnitQuanity,
512
+ extras,
513
+ puInfo,
514
+ doInfo,
493
515
  },
494
516
  }) {
495
517
  const model = {
@@ -501,25 +523,72 @@ class Plugin {
501
523
  } : {
502
524
  NewBookingInfo: { Name: quoteName, QB: 'Q' },
503
525
  }),
526
+ ...(rateId ? {
527
+ RateId: rateId,
528
+ } : {}),
529
+ ...(puInfo ? {
530
+ puTime: puInfo.time,
531
+ puRemark: `Time: ${puInfo.time || 'NA'},
532
+ Location: ${puInfo.location || 'NA'},
533
+ Flight: ${puInfo.flightDetails || 'NA'},
534
+ `,
535
+ } : {}),
536
+ ...(doInfo ? {
537
+ doTime: doInfo.time,
538
+ doRemark: `Time: ${doInfo.time || 'NA'},
539
+ Location: ${doInfo.location || 'NA'},
540
+ Flight: ${doInfo.flightDetails || 'NA'},
541
+ `,
542
+ } : {}),
543
+ ...(extras && extras.length ? {
544
+ Remarks: extras.map((e, i) => `Extra ${i + 1}: ${e.name} x ${e.quantity}`).join(`\n`),
545
+ } : {}),
504
546
  Opt: optionId,
505
547
  DateFrom: startDate,
506
548
  RateId: 'Default',
507
549
  SCUqty: chargeUnitQuanity || 1,
508
550
  AgentRef: reference,
509
- RoomConfigs: paxConfigs.map(obj => {
510
- const RoomConfig = {
511
- Adults: obj.adults || 0,
512
- Children: obj.children || 0,
513
- Infants: obj.infants || 0,
514
- };
551
+ RoomConfigs: paxConfigs.map(({ roomType, passengers }) => {
552
+ const RoomConfig = passengers.reduce((acc, p) => {
553
+ if (p.passengerType === 'Adult') {
554
+ acc.Adults += 1;
555
+ }
556
+ if (p.passengerType === 'Child') {
557
+ acc.Children += 1;
558
+ }
559
+ if (p.passengerType === 'Infant') {
560
+ acc.Infants += 1;
561
+ }
562
+ return acc;
563
+ }, {
564
+ Adults: 0,
565
+ Children: 0,
566
+ Infants: 0,
567
+ });
515
568
  const RoomType = ({
516
569
  Single: 'SG',
517
570
  Double: 'DB',
518
571
  Twin: 'TW',
519
572
  Triple: 'TR',
520
573
  Quad: 'QU',
521
- })[obj.roomType];
574
+ })[roomType];
522
575
  if (RoomType) RoomConfig.RoomType = RoomType;
576
+ if (passengers && passengers.length) {
577
+ RoomConfig.PaxList = passengers.map(p => ({
578
+ PaxDetails: {
579
+ Forename: p.firstName,
580
+ Surname: p.lastName,
581
+ PaxType: {
582
+ Adult: 'A',
583
+ Child: 'C',
584
+ Infant: 'I',
585
+ }[p.passengerType],
586
+ Age: p.age,
587
+ DateOfBirth: p.dob,
588
+ Title: p.salutation,
589
+ },
590
+ }));
591
+ }
523
592
  return { RoomConfig };
524
593
  }),
525
594
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ti2-tourplan",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Tourplan's TI2 Plugin",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -49,6 +49,38 @@ const translateTPOption = ({ optionsGroupedBySupplierId, supplierData }) => {
49
49
  }`,
50
50
  // Guides, Accommodation, Transfers, Entrance Fees, Meals, Other
51
51
  serviceType: R.path(['OptGeneral', 'ButtonName'], option),
52
+ restrictions: {
53
+ MPFCU: R.path(['OptGeneral', 'MPFCU'], option),
54
+ roomTypeRequired: ['Y', 'P'].includes(R.path(['OptGeneral', 'SType'], option)),
55
+ Adult: {
56
+ allowed: R.path(['OptGeneral', 'AdultsAllowed'], option) === 'Y',
57
+ minAge: R.path(['OptGeneral', 'Adult_From'], option),
58
+ maxAge: R.path(['OptGeneral', 'Adult_To'], option),
59
+ },
60
+ Child: {
61
+ allowed: R.path(['OptGeneral', 'ChildrenAllowed'], option) === 'Y',
62
+ minAge: R.path(['OptGeneral', 'Child_From'], option),
63
+ maxAge: R.path(['OptGeneral', 'Child_To'], option),
64
+ },
65
+ Infant: {
66
+ allowed: R.path(['OptGeneral', 'InfantsAllowed'], option) === 'Y',
67
+ minAge: R.path(['OptGeneral', 'Infant_From'], option),
68
+ maxAge: R.path(['OptGeneral', 'Infant_To'], option),
69
+ },
70
+ ...['Single', 'Twin', 'Double', 'Quad'].reduce((acc, roomType) => {
71
+ const unitAvail = R.path(['OptGeneral', `${roomType}_Avail`], option);
72
+ const unitMax = R.path(['OptGeneral', `${roomType}_Max`], option);
73
+ const unitAdMax = R.path(['OptGeneral', `${roomType}_Ad_Max`], option);
74
+ return {
75
+ ...acc,
76
+ [roomType]: {
77
+ allowed: unitAvail === 'Y',
78
+ maxPax: unitMax,
79
+ maxAdults: unitAdMax,
80
+ },
81
+ };
82
+ }, {}),
83
+ },
52
84
  };
53
85
  /*
54
86
  SType: One character that specifies the service type of the
@@ -68,7 +100,6 @@ const translateTPOption = ({ optionsGroupedBySupplierId, supplierData }) => {
68
100
  unitName: 'Adults',
69
101
  pricing: [],
70
102
  restrictions: {
71
- paxCount: R.path(['OptGeneral', 'MPFCU'], option),
72
103
  minAge: R.path(['OptGeneral', 'Adult_From'], option),
73
104
  maxAge: R.path(['OptGeneral', 'Adult_To'], option),
74
105
  },
@@ -90,14 +121,14 @@ const translateTPOption = ({ optionsGroupedBySupplierId, supplierData }) => {
90
121
  },
91
122
  } : null].filter(Boolean);
92
123
  }
93
- if (R.path(['OptGeneral', 'SType'], option) === 'Y') {
94
- return [['SG', 'Single'], ['TW', 'Twin'], ['DB', 'Double'], ['QD', 'Quad']]
95
- .filter(([_, unitName]) => R.path(['OptGeneral', `${unitName}_Avail`], option) === 'Y')
124
+ if (R.path(['OptGeneral', 'SType'], option) === 'Y' || R.path(['OptGeneral', 'SType'], option) === 'P') {
125
+ return ['Single', 'Twin', 'Double', 'Quad']
96
126
  .map(([unitId, unitName]) => ({
97
127
  unitId,
98
128
  unitName,
99
129
  pricing: [],
100
130
  restrictions: {
131
+ allowed: R.path(['OptGeneral', `${unitName}_Avail`], option) === 'Y',
101
132
  paxCount: R.path(['OptGeneral', `${unitName}_Max`], option)
102
133
  || R.path(['OptGeneral', `${unitName}_Ad_Max`], option),
103
134
  },