Skip to content

Commit

Permalink
Merge pull request #668 from Travelport-Ukraine/master
Browse files Browse the repository at this point in the history
1.17.4 Conjunction coupons parsing fix
  • Loading branch information
dchertousov authored Nov 15, 2024
2 parents a972bbe + f6a6456 commit ea65d01
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 61 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "uapi-json",
"version": "1.17.3",
"version": "1.17.4",
"description": "Travelport Universal API",
"main": "src/",
"files": [
Expand Down
92 changes: 43 additions & 49 deletions src/Services/Air/AirParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,9 +585,8 @@ function getTicketFromEtr(etr, obj, allowNoProviderLocatorCodeRetrieval = false)
const exchangedTickets = [];

const allCoupons = ticketsList.map((ticket) => {
return Object.entries(ticket['air:Coupon']).map(([couponKey, coupon]) => {
return Object.values(ticket['air:Coupon']).map((coupon) => {
return {
key: couponKey,
ticketNumber: ticket.TicketNumber,
couponNumber: coupon.CouponNumber,
from: coupon.Origin,
Expand All @@ -607,59 +606,54 @@ function getTicketFromEtr(etr, obj, allowNoProviderLocatorCodeRetrieval = false)
return all.concat(nextChunk);
}, []);

const tickets = ticketsList.map(
(ticket) => {
if (ticket['air:ExchangedTicketInfo']) {
ticket['air:ExchangedTicketInfo'].forEach(
(t) => exchangedTickets.push(t.Number)
);
}
// Filling exchanged tickets array
ticketsList.forEach((ticket) => {
if (ticket['air:ExchangedTicketInfo']) {
ticket['air:ExchangedTicketInfo'].forEach(
(t) => exchangedTickets.push(t.Number)
);
}
});

const coupons = Object.keys(ticket['air:Coupon']).map(
(couponKey) => {
const allCouponsIndex = allCoupons.findIndex((ac) => ac.key === couponKey);
const coupon = allCoupons[allCouponsIndex];
const nextCoupon = allCoupons[allCouponsIndex + 1];

let bookingInfo = null;
// looking for fareInfo by it's fareBasis
// and for bookingInfo by correct FareInfoRef
if (airPricingInfo && airPricingInfo['air:FareInfo']) {
Object.keys(airPricingInfo['air:FareInfo']).forEach(
(fareKey) => {
const fare = airPricingInfo['air:FareInfo'][fareKey];
if (fare.FareBasis === coupon.FareBasis
&& airPricingInfo['air:BookingInfo']) {
const bInfo = airPricingInfo['air:BookingInfo'].find(
(info) => info.FareInfoRef === fareKey
);

if (bInfo) {
bookingInfo = bInfo;
}
}
}
// Getting ticket coupons
const ticketsObject = allCoupons.reduce((acc, coupon, index) => {
const { ticketNumber } = coupon;
const ticket = acc[ticketNumber] || { ticketNumber, coupons: [] };
const { coupons } = ticket;
const nextCoupon = allCoupons[index + 1];

let bookingInfo = null;
// looking for fareInfo by it's fareBasis
// and for bookingInfo by correct FareInfoRef
if (airPricingInfo && airPricingInfo['air:FareInfo']) {
Object.keys(airPricingInfo['air:FareInfo']).forEach(
(fareKey) => {
const fare = airPricingInfo['air:FareInfo'][fareKey];
if (fare.FareBasis === coupon.FareBasis && airPricingInfo['air:BookingInfo']) {
const bInfo = airPricingInfo['air:BookingInfo'].find(
(info) => info.FareInfoRef === fareKey
);
}

return {
...coupon,
stopover: (
nextCoupon
? nextCoupon.stopover
: true
),
...(bookingInfo !== null ? { serviceClass: bookingInfo.CabinClass } : null)
};
if (bInfo) {
bookingInfo = bInfo;
}
}
}
);

return {
ticketNumber: ticket.TicketNumber,
coupons,
};
}
);
const couponData = {
...coupon,
stopover: (
nextCoupon
? nextCoupon.stopover
: true
),
...(bookingInfo !== null ? { serviceClass: bookingInfo.CabinClass } : null)
};
return { ...acc, [ticketNumber]: { ...ticket, coupons: [...coupons, couponData] } };
}, {});

const tickets = Object.values(ticketsObject);

const taxes = (airPricingInfo && airPricingInfo['air:TaxInfo'])
? Object.keys(airPricingInfo['air:TaxInfo']).map(
Expand Down
38 changes: 28 additions & 10 deletions test/Air/AirParser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ describe('#AirParser', () => {
});

describe('getTicket', () => {
function testTicket(result) {
function testTicket(result, options = {}) {
const { allowNoProviderLocatorCodeRetrieval = false } = options;
expect(result).to.be.an('object');
expect(result).to.include.all.keys([
'uapi_ur_locator', 'uapi_reservation_locator', 'pnr', 'ticketNumber',
Expand All @@ -330,9 +331,11 @@ describe('#AirParser', () => {
}
);
}
expect(result.uapi_ur_locator).to.match(pnrRegExp);
expect(result.uapi_reservation_locator).to.match(pnrRegExp);
expect(result.pnr).to.match(pnrRegExp);
if (!allowNoProviderLocatorCodeRetrieval) {
expect(result.uapi_ur_locator).to.match(pnrRegExp);
expect(result.uapi_reservation_locator).to.match(pnrRegExp);
expect(result.pnr).to.match(pnrRegExp);
}
expect(result.ticketNumber).to.match(ticketRegExp);
expect(result.platingCarrier).to.match(/^[A-Z0-9]{2}$/i);
expect(result.ticketingPcc).to.match(/^[A-Z0-9]{3,4}$/i);
Expand Down Expand Up @@ -494,6 +497,23 @@ describe('#AirParser', () => {
});
});

it('should parse conjunction ticket with 3 parts and missing data', async () => {
const uParser = new Parser('air:AirRetrieveDocumentRsp', 'v52_0', {});
const parseFunction = airParser.AIR_GET_TICKET;
const xml = fs.readFileSync(`${xmlFolder}/get-ticket-conjunction.xml`).toString();
const json = await uParser.parse(xml);
const parsingOptions = { allowNoProviderLocatorCodeRetrieval: true };
const result = parseFunction.call(uParser, json, parsingOptions);

testTicket(result, parsingOptions);
result.tickets.forEach((ticket) => {
const { ticketNumber, coupons } = ticket;
coupons.forEach((coupon) => {
expect(coupon.ticketNumber).to.equal(ticketNumber);
});
});
});

it('should parse exchanged conjunction ticket', () => {
const uParser = new Parser('air:AirRetrieveDocumentRsp', 'v52_0', {});
const parseFunction = airParser.AIR_GET_TICKET;
Expand All @@ -503,15 +523,14 @@ describe('#AirParser', () => {
.then((json) => parseFunction.call(uParser, json))
.then((result) => {
testTicket(result);
const couponsStopover = [false, true, false, true];
const expectedStopovers = [false, true, false, true];
const coupons = result.tickets.reduce(
(acc, ticket) => acc.concat(ticket.coupons),
[]
);
console.log(coupons.map((c) => c.stopover));
coupons.forEach((coupon, index) => {
expect(coupon.stopover).to.be.equal(couponsStopover[index]);
});
const parsedStopovers = coupons.flatMap((coupon) => coupon.stopover);
expect(parsedStopovers).to.deep.equal(expectedStopovers);
expect(result.priceInfoDetailsAvailable).to.equal(true);
expect(result.exchangedTickets).to.have.length.above(0);
});
Expand Down Expand Up @@ -720,7 +739,7 @@ describe('#AirParser', () => {
expect(coupon).to.have.all.keys([
'couponNumber', 'from', 'to', 'departure', 'airline', 'flightNumber',
'fareBasisCode', 'status', 'notValidBefore', 'notValidAfter', 'bookingClass', 'stopover',
'key', 'ticketNumber',
'ticketNumber',
]);
expect(coupon.couponNumber).to.match(/\d+/i);
expect(coupon.from).to.match(/[A-Z]{3}/i);
Expand Down Expand Up @@ -789,7 +808,6 @@ describe('#AirParser', () => {
ticketNumber: '0809903654876',
coupons: [
{
key: 'SGNQhOBAAA/Bn2KYVCAAAA==',
ticketNumber: '0809903654876',
couponNumber: '1',
from: 'KBP',
Expand Down
48 changes: 48 additions & 0 deletions test/FakeResponses/Air/get-ticket-conjunction.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<SOAP:Envelope
xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body>
<air:AirRetrieveDocumentRsp TransactionId="E30219CD0A0EE00F5D97AF538AA4DED9" ResponseTime="416"
xmlns:air="http://www.travelport.com/schema/air_v52_0"
xmlns:common_v52_0="http://www.travelport.com/schema/common_v52_0">
<air:ETR Taxes="PLN2205.94" TourCode="PLS0011F" IssuedDate="2024-10-01T00:00:00.000+02:00" ProviderCode="1G" IATANumber="63210825" PseudoCityCode="5N14" CountryCode="PL" PlatingCarrier="VN">
<common_v52_0:BookingTraveler>
<common_v52_0:BookingTravelerName First="KATARZYNAMRS" Last="CYPRYNOWSKA"/>
</common_v52_0:BookingTraveler>
<common_v52_0:FormOfPayment Key="rb0W2KPAuDKAZGOEeaAAAA==" Type="Cash" Reusable="false" ProfileKey="VyWTtuxmQVG7+wwBcYO3tQ=="/>
<common_v52_0:Payment Key="rb0W2KPAuDKAaGOEeaAAAA==" Type="Itinerary" Amount="PLN5999.94" FormOfPaymentRef="rb0W2KPAuDKAZGOEeaAAAA=="/>
<air:FareCalc>WAW LO X/FRA VN SGN M/IT //HAN VN LPQ VN REP//PNH VN X/HAN VN X/LON LO WAW M/IT END ROE3.866426 XT 42.77DE109.71RA7.66C495.79JC57.47LA72.80O6114.95KX19.16L538.49T870.31UB1488.44YQ</air:FareCalc>
<air:Ticket TicketNumber="7385489198571" TicketStatus="N">
<air:Coupon CouponNumber="1" MarketingCarrier="LO" MarketingFlightNumber="381" Origin="WAW" Destination="FRA" DepartureTime="2024-10-16T07:40:00.000+02:00" StopoverCode="true" BookingClass="G" FareBasis="RL1YEUF" NotValidBefore="2024-10-16" NotValidAfter="2024-10-16" Status="F"/>
<air:Coupon CouponNumber="2" MarketingCarrier="VN" MarketingFlightNumber="30" Origin="FRA" Destination="SGN" DepartureTime="2024-10-16T14:35:00.000+02:00" StopoverCode="false" BookingClass="R" FareBasis="RL1YEUF" NotValidBefore="2024-10-16" NotValidAfter="2024-10-16" Status="F"/>
<air:Coupon CouponNumber="4" MarketingCarrier="VN" MarketingFlightNumber="931" Origin="HAN" Destination="LPQ" DepartureTime="2024-10-25T12:55:00.000+07:00" StopoverCode="true" BookingClass="B" FareBasis="LL1YEUF" NotValidBefore="2024-10-25" NotValidAfter="2024-10-25" Status="F"/>
<air:TourCode Value="PLS0011F"/>
</air:Ticket>
<air:Ticket TicketNumber="7385489198572" TicketStatus="N">
<air:Coupon CouponNumber="1" MarketingCarrier="VN" MarketingFlightNumber="931" Origin="LPQ" Destination="SAI" DepartureTime="2024-10-28T14:40:00.000+07:00" StopoverCode="true" BookingClass="B" FareBasis="LL1YEUF" NotValidBefore="2024-10-28" NotValidAfter="2024-10-28" Status="F"/>
<air:Coupon CouponNumber="3" MarketingCarrier="VN" MarketingFlightNumber="920" Origin="PNH" Destination="HAN" DepartureTime="2024-11-02T17:30:00.000+07:00" StopoverCode="true" BookingClass="B" FareBasis="LL1YEUF" NotValidBefore="2024-11-02" NotValidAfter="2024-11-02" Status="A"/>
<air:Coupon CouponNumber="4" MarketingCarrier="VN" MarketingFlightNumber="55" Origin="HAN" Destination="LHR" DepartureTime="2024-11-03T00:40:00.000+07:00" StopoverCode="false" BookingClass="L" FareBasis="LL1YEUF" NotValidBefore="2024-11-03" NotValidAfter="2024-11-03" Status="O"/>
</air:Ticket>
<air:Ticket TicketNumber="7385489198573" TicketStatus="N">
<air:Coupon CouponNumber="1" MarketingCarrier="LO" MarketingFlightNumber="282" Origin="LHR" Destination="WAW" DepartureTime="2024-11-03T10:25:00.000+00:00" StopoverCode="false" BookingClass="G" FareBasis="LL1YEUF" NotValidBefore="2024-11-03" NotValidAfter="2024-11-03" Status="O"/>
</air:Ticket>
<common_v52_0:Commission Level="Fare" Type="PercentBase" Percentage="0.01"/>
<air:AirPricingInfo Key="rb0W2KPAuDKAlUOEeaAAAA==" BasePrice="USD0.00" Taxes="PLN2205.94" PricingMethod="Auto" Ticketed="true">
<air:FareInfo Key="rb0W2KPAuDKApUOEeaAAAA==" FareBasis="RL1YEUF" PassengerTypeCode="ADT" Origin="WAW" Destination="FRA" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:FareInfo Key="rb0W2KPAuDKAqUOEeaAAAA==" FareBasis="RL1YEUF" PassengerTypeCode="ADT" Origin="FRA" Destination="SGN" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:FareInfo Key="rb0W2KPAuDKArUOEeaAAAA==" FareBasis="LL1YEUF" PassengerTypeCode="ADT" Origin="HAN" Destination="LPQ" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:FareInfo Key="rb0W2KPAuDKAsUOEeaAAAA==" FareBasis="LL1YEUF" PassengerTypeCode="ADT" Origin="LPQ" Destination="SAI" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:FareInfo Key="rb0W2KPAuDKAtUOEeaAAAA==" FareBasis="LL1YEUF" PassengerTypeCode="ADT" Origin="PNH" Destination="HAN" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:FareInfo Key="rb0W2KPAuDKAuUOEeaAAAA==" FareBasis="LL1YEUF" PassengerTypeCode="ADT" Origin="HAN" Destination="LHR" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:FareInfo Key="rb0W2KPAuDKAvUOEeaAAAA==" FareBasis="LL1YEUF" PassengerTypeCode="ADT" Origin="LHR" Destination="WAW" EffectiveDate="2024-10-01T00:00:00.000+02:00"/>
<air:TaxInfo Category="ND" Amount="PLN1.05" Key="rb0W2KPAuDKAmUOEeaAAAA=="/>
<air:TaxInfo Category="XW" Amount="PLN87.34" Key="rb0W2KPAuDKAnUOEeaAAAA=="/>
<air:TaxInfo Category="XT" Amount="PLN2117.55" Key="rb0W2KPAuDKAoUOEeaAAAA=="/>
<air:FareCalc>WAW LO X/FRA VN SGN M/IT //HAN VN LPQ VN REP//PNH VN X/HAN VN X/LON LO WAW M/IT END ROE3.866426 XT 42.77DE109.71RA7.66C495.79JC57.47LA72.80O6114.95KX19.16L538.49T870.31UB1488.44YQ</air:FareCalc>
<air:PassengerType Code="ADT">
<air:FareGuaranteeInfo GuaranteeType="Auto"/>
</air:PassengerType>
</air:AirPricingInfo>
</air:ETR>
</air:AirRetrieveDocumentRsp>
</SOAP:Body>
</SOAP:Envelope>

0 comments on commit ea65d01

Please sign in to comment.