sabre-client 0.5.1 → 0.7.0
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.es.md +265 -16
- package/README.md +266 -16
- package/dist/index.d.ts +354 -10
- package/dist/index.js +786 -181
- package/dist/index.js.map +1 -1
- package/dist/{reservation-history-BKRFlxpA.d.ts → reservation-history-BRKQBHsj.d.ts} +178 -3
- package/dist/testing.d.ts +127 -8
- package/dist/testing.js +811 -51
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,259 @@
|
|
|
1
1
|
// src/testing/queue-fake.ts
|
|
2
2
|
import { XMLParser as XMLParser3 } from "fast-xml-parser";
|
|
3
3
|
|
|
4
|
+
// src/testing/booking-fake.ts
|
|
5
|
+
var LOCATOR = /^[A-Z0-9]{6}$/;
|
|
6
|
+
var FULFILL_REASONS = /* @__PURE__ */ new Set([
|
|
7
|
+
"UNABLE_TO_RETRIEVE_TICKETS",
|
|
8
|
+
"FULFILLMENT_NOT_CONFIRMED",
|
|
9
|
+
"INTERNAL_SERVER_TIMEOUT",
|
|
10
|
+
"DOWNLINE_SERVICE_ERROR",
|
|
11
|
+
"PAYMENT_CARD_ISSUE",
|
|
12
|
+
"NOT_COMMITTED",
|
|
13
|
+
"TRANSPORT",
|
|
14
|
+
"GATEWAY_5XX",
|
|
15
|
+
"UNPARSEABLE",
|
|
16
|
+
"ABORTED"
|
|
17
|
+
]);
|
|
18
|
+
var ISSUES_BY_DEFAULT = /* @__PURE__ */ new Set(["UNABLE_TO_RETRIEVE_TICKETS", "INTERNAL_SERVER_TIMEOUT", "NOT_COMMITTED", "TRANSPORT", "GATEWAY_5XX", "UNPARSEABLE", "ABORTED"]);
|
|
19
|
+
var VOID_REASONS = /* @__PURE__ */ new Set(["END_TRANSACTION_PROBLEM", "TRANSPORT", "GATEWAY_5XX", "UNPARSEABLE", "ABORTED"]);
|
|
20
|
+
var DOWNLINE_TEXT = {
|
|
21
|
+
INVALID_TICKET_STOCK: "The AirTicketRQ service returned an error: (DesignatePrinterLLSRQ: INVALID TICKET STOCK-0096).",
|
|
22
|
+
COMMISSION_REQUIRED: "The AirTicketRQ service returned an error: (AirTicketLLS failed for /Ticketing[1] with Cause: AirTicketLLSRQ: COMMISSION REQUIRED-0260, TicketingDocumentServicesRQ: No new tickets have been issued).",
|
|
23
|
+
HARDCOPY_PRINTER: "The AirTicketRQ service returned an error: (AirTicketLLS failed for /Ticketing[1] with Cause: AirTicketLLSRQ: ASSIGN HARDCOPY PRINTER FOR ETR DOCUMENTS-USE PTR/-0052, TicketingDocumentServicesRQ: No new tickets have been issued).",
|
|
24
|
+
SECURE_FLIGHT: "The AirTicketRQ service returned an error: (AirTicketLLS failed for /Ticketing[1] with Cause: AirTicketLLSRQ: PASSENGER SECURITY DATA REQUIRED PLEASE UPDATE AND RETRY-1829, TicketingDocumentServicesRQ: No new tickets have been issued)."
|
|
25
|
+
};
|
|
26
|
+
var UNCERTAIN_TEXT = {
|
|
27
|
+
UNABLE_TO_RETRIEVE_TICKETS: ["APPLICATION_ERROR", "Tickets might have been issued successfully but application could not retrieve them. Verify booking details and/or audit trail reports (DQB)."],
|
|
28
|
+
FULFILLMENT_NOT_CONFIRMED: ["WARNING", "The fulfillment operation cannot be confirmed. No new tickets were found in the booking."],
|
|
29
|
+
INTERNAL_SERVER_TIMEOUT: ["APPLICATION_ERROR", "The downline ticketing service exceeded its processing time and the request timed out. Please retry the fulfillment attempt."],
|
|
30
|
+
DOWNLINE_SERVICE_ERROR: ["APPLICATION_ERROR", "The AirTicketRQ service returned an error: (AirTicketLLSRQ: UNABLE TO PROCESS)."],
|
|
31
|
+
PAYMENT_CARD_ISSUE: ["APPLICATION_ERROR", "At least one fulfillment operation was not processed correctly. The transaction was declined due to an invalid card number."]
|
|
32
|
+
};
|
|
33
|
+
function fail(message) {
|
|
34
|
+
throw new TypeError(`sabre-client/testing: ${message}`);
|
|
35
|
+
}
|
|
36
|
+
var json = (status, body) => new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } });
|
|
37
|
+
var appError = (type, description, category = "APPLICATION_ERROR") => ({ category, type, description });
|
|
38
|
+
function toTicket(value, label, today) {
|
|
39
|
+
if (typeof value !== "object" || value === null) fail(`${label} debe ser un objeto`);
|
|
40
|
+
const ticket = value;
|
|
41
|
+
if (typeof ticket.number !== "string" || !/^[0-9A-Z]{13}$/.test(ticket.number)) fail(`${label}.number debe ser un n\xFAmero de boleto de 13 caracteres`);
|
|
42
|
+
if (ticket.ticketStatusCode !== void 0 && ticket.ticketStatusCode !== "TE" && ticket.ticketStatusCode !== "TV") fail(`${label}.ticketStatusCode debe ser TE o TV`);
|
|
43
|
+
return { number: ticket.number, ticketStatusCode: ticket.ticketStatusCode ?? "TE", date: ticket.date ?? today, committed: true };
|
|
44
|
+
}
|
|
45
|
+
function validateFulfillOutcome(value, label) {
|
|
46
|
+
if (value === "issued" || value === "partial") return value;
|
|
47
|
+
if (typeof value !== "object" || value === null) fail(`${label} debe ser 'issued', 'partial' o un objeto`);
|
|
48
|
+
const outcome = value;
|
|
49
|
+
if ("uncertain" in outcome) {
|
|
50
|
+
if (!FULFILL_REASONS.has(String(outcome.uncertain))) fail(`${label}.uncertain no es una reason de tickets.fulfill`);
|
|
51
|
+
} else if ("downline" in outcome) {
|
|
52
|
+
if (!(String(outcome.downline) in DOWNLINE_TEXT)) fail(`${label}.downline debe ser ${Object.keys(DOWNLINE_TEXT).join(", ")}`);
|
|
53
|
+
} else if (typeof outcome.error !== "string" || outcome.error === "") {
|
|
54
|
+
fail(`${label} debe tener uncertain, downline o error`);
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
function validateVoidOutcome(value, label) {
|
|
59
|
+
if (value === "voided") return value;
|
|
60
|
+
if (typeof value !== "object" || value === null || !VOID_REASONS.has(String(value.uncertain))) {
|
|
61
|
+
fail(`${label} debe ser 'voided' o { uncertain: ${[...VOID_REASONS].join(" | ")} }`);
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
function createBookingHandler(options, now, record) {
|
|
66
|
+
const today = () => now().toISOString().slice(0, 10);
|
|
67
|
+
const bookings = /* @__PURE__ */ new Map();
|
|
68
|
+
for (const [locator, seed] of Object.entries(options.bookings ?? {})) {
|
|
69
|
+
if (!LOCATOR.test(locator)) fail(`bookings['${locator}']: la clave debe ser un localizador de 6 caracteres alfanum\xE9ricos en may\xFAscula`);
|
|
70
|
+
if (typeof seed !== "object" || seed === null) fail(`bookings['${locator}'] debe ser un objeto`);
|
|
71
|
+
if (seed.flights !== void 0 && (!Number.isInteger(seed.flights) || seed.flights < 0)) fail(`bookings['${locator}'].flights debe ser un entero >= 0`);
|
|
72
|
+
if (seed.tickets !== void 0 && !Array.isArray(seed.tickets)) fail(`bookings['${locator}'].tickets debe ser una lista`);
|
|
73
|
+
bookings.set(locator, {
|
|
74
|
+
flights: seed.flights ?? 1,
|
|
75
|
+
tickets: (seed.tickets ?? []).map((ticket, index) => toTicket(ticket, `bookings['${locator}'].tickets[${index}]`, today()))
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const ticketing = options.ticketing ?? {};
|
|
79
|
+
if (typeof ticketing !== "object" || ticketing === null) fail("ticketing debe ser un objeto");
|
|
80
|
+
if (ticketing.fulfill !== void 0 && !Array.isArray(ticketing.fulfill)) fail("ticketing.fulfill debe ser una lista");
|
|
81
|
+
if (ticketing.void !== void 0 && !Array.isArray(ticketing.void)) fail("ticketing.void debe ser una lista");
|
|
82
|
+
const fulfillQueue = (ticketing.fulfill ?? []).map((outcome, index) => validateFulfillOutcome(outcome, `ticketing.fulfill[${index}]`));
|
|
83
|
+
const voidQueue = (ticketing.void ?? []).map((outcome, index) => validateVoidOutcome(outcome, `ticketing.void[${index}]`));
|
|
84
|
+
let created = 0;
|
|
85
|
+
let issuedCount = 0;
|
|
86
|
+
const nextLocator = () => {
|
|
87
|
+
let locator;
|
|
88
|
+
do {
|
|
89
|
+
created += 1;
|
|
90
|
+
locator = `FAKE${String.fromCharCode(64 + Math.ceil(created / 26))}${String.fromCharCode(64 + ((created - 1) % 26 + 1))}`;
|
|
91
|
+
} while (bookings.has(locator));
|
|
92
|
+
return locator;
|
|
93
|
+
};
|
|
94
|
+
const nextTicket = () => {
|
|
95
|
+
issuedCount += 1;
|
|
96
|
+
return { number: `999${String(issuedCount).padStart(10, "0")}`, ticketStatusCode: "TE", date: today(), committed: true };
|
|
97
|
+
};
|
|
98
|
+
const ticketJson = (ticket) => ({
|
|
99
|
+
number: ticket.number,
|
|
100
|
+
date: ticket.date,
|
|
101
|
+
airlineCode: "XX",
|
|
102
|
+
travelerIndex: 1,
|
|
103
|
+
flightCoupons: [],
|
|
104
|
+
payment: { total: "100.00", currencyCode: "USD" },
|
|
105
|
+
ticketStatusName: ticket.ticketStatusCode === "TE" ? "Issued" : "Voided",
|
|
106
|
+
ticketStatusCode: ticket.ticketStatusCode
|
|
107
|
+
});
|
|
108
|
+
const bookingJson = (locator, booking) => ({
|
|
109
|
+
bookingId: locator,
|
|
110
|
+
startDate: today(),
|
|
111
|
+
isTicketed: booking.tickets.some((ticket) => ticket.committed),
|
|
112
|
+
flights: Array.from({ length: booking.flights }, (_, index) => ({ itemId: String(index + 1), flightNumber: 100 + index, airlineCode: "XX" })),
|
|
113
|
+
travelers: [{ givenName: "TEST", surname: "PASSENGER" }],
|
|
114
|
+
flightTickets: booking.tickets.filter((ticket) => ticket.committed).map(ticketJson)
|
|
115
|
+
});
|
|
116
|
+
const notFound = () => json(200, { errors: [appError("BOOKING_NOT_FOUND", "Booking cannot be found.", "RESOURCE_NOT_FOUND")] });
|
|
117
|
+
const parse = (body) => {
|
|
118
|
+
try {
|
|
119
|
+
const value = JSON.parse(body);
|
|
120
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
121
|
+
} catch {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const fulfill = (request) => {
|
|
126
|
+
const locator = String(request.confirmationId ?? "");
|
|
127
|
+
record("fulfill", locator);
|
|
128
|
+
const booking = bookings.get(locator);
|
|
129
|
+
if (booking === void 0) return notFound();
|
|
130
|
+
const outcome = fulfillQueue.shift() ?? "issued";
|
|
131
|
+
const echo = { ...request };
|
|
132
|
+
const issue = (committed = true) => {
|
|
133
|
+
const ticket = { ...nextTicket(), committed };
|
|
134
|
+
booking.tickets.push(ticket);
|
|
135
|
+
return [ticket];
|
|
136
|
+
};
|
|
137
|
+
const ticketsOut = (tickets2) => tickets2.map((ticket) => ({ ...ticketJson(ticket), isCommitted: ticket.committed, travelerGivenName: "TEST", travelerSurname: "PASSENGER" }));
|
|
138
|
+
if (typeof outcome === "object" && "error" in outcome) {
|
|
139
|
+
return json(200, { request: echo, errors: [appError(outcome.error, outcome.description ?? `No new tickets have been issued (${outcome.error}).`)] });
|
|
140
|
+
}
|
|
141
|
+
if (typeof outcome === "object" && "downline" in outcome) {
|
|
142
|
+
return json(200, { request: echo, errors: [appError("DOWNLINE_SERVICE_ERROR", DOWNLINE_TEXT[outcome.downline])] });
|
|
143
|
+
}
|
|
144
|
+
if (outcome === "issued" || outcome === "partial") {
|
|
145
|
+
if (booking.tickets.some((ticket) => ticket.ticketStatusCode === "TE")) {
|
|
146
|
+
return json(200, { request: echo, errors: [appError("BOOKING_FULFILLED", "The fulfillment operation cannot be processed. The booking is already fulfilled.")] });
|
|
147
|
+
}
|
|
148
|
+
const tickets2 = issue();
|
|
149
|
+
return json(200, {
|
|
150
|
+
timestamp: now().toISOString().slice(0, 19),
|
|
151
|
+
tickets: ticketsOut(tickets2),
|
|
152
|
+
request: echo,
|
|
153
|
+
...outcome === "partial" ? { warnings: [appError("PARTIAL_FULFILLMENT", "At least one fulfillment operation was not processed correctly. Ticket issuance was unsuccessful due to a missing form of identification (FOID).", "WARNING")] } : {}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const { uncertain: reason } = outcome;
|
|
157
|
+
const issues = outcome.issued ?? ISSUES_BY_DEFAULT.has(reason);
|
|
158
|
+
const tickets = issues ? issue(reason !== "NOT_COMMITTED") : [];
|
|
159
|
+
switch (reason) {
|
|
160
|
+
case "TRANSPORT":
|
|
161
|
+
return "transport";
|
|
162
|
+
case "ABORTED":
|
|
163
|
+
return "abort";
|
|
164
|
+
case "GATEWAY_5XX":
|
|
165
|
+
return json(503, { errorCode: "ERR.2SG.PROVIDER.ERROR", message: "sabre-client/testing: 503 forzado" });
|
|
166
|
+
case "UNPARSEABLE":
|
|
167
|
+
return new Response("<html>sabre-client/testing: cuerpo ilegible forzado</html>", { status: 200 });
|
|
168
|
+
case "NOT_COMMITTED":
|
|
169
|
+
return json(200, { timestamp: now().toISOString().slice(0, 19), tickets: ticketsOut(tickets), request: echo });
|
|
170
|
+
default: {
|
|
171
|
+
const [category, description] = UNCERTAIN_TEXT[reason];
|
|
172
|
+
const item = appError(reason, description, category);
|
|
173
|
+
return json(200, { request: echo, ...tickets.length > 0 ? { tickets: ticketsOut(tickets) } : {}, ...category === "WARNING" ? { warnings: [item] } : { errors: [item] } });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const cancel = (request) => {
|
|
178
|
+
const locator = String(request.confirmationId ?? "");
|
|
179
|
+
record("cancelBooking", locator);
|
|
180
|
+
const booking = bookings.get(locator);
|
|
181
|
+
if (booking === void 0) return notFound();
|
|
182
|
+
const echo = { errorHandlingPolicy: "HALT_ON_ERROR", ...request };
|
|
183
|
+
const live = booking.tickets.filter((ticket) => ticket.ticketStatusCode === "TE");
|
|
184
|
+
if (request.flightTicketOperation === "VOID") {
|
|
185
|
+
if (live.length === 0) {
|
|
186
|
+
return json(200, {
|
|
187
|
+
request: echo,
|
|
188
|
+
errors: [
|
|
189
|
+
appError("NO_ITEMS_CANCELLED", "Nothing was cancelled - cancellation was interrupted due to errors", "WARNING"),
|
|
190
|
+
appError("UNABLE_TO_VOID_TICKET", "Unable to void ticket.", "CANCELLATION_ERROR")
|
|
191
|
+
]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const outcome = voidQueue.shift() ?? "voided";
|
|
195
|
+
const voids = outcome === "voided" || outcome.voided !== false;
|
|
196
|
+
if (voids) {
|
|
197
|
+
for (const ticket of live) ticket.ticketStatusCode = "TV";
|
|
198
|
+
if (request.cancelAll === true) booking.flights = 0;
|
|
199
|
+
}
|
|
200
|
+
const voidedTickets = voids ? live.map((ticket) => ticket.number) : [];
|
|
201
|
+
if (outcome !== "voided") {
|
|
202
|
+
switch (outcome.uncertain) {
|
|
203
|
+
case "TRANSPORT":
|
|
204
|
+
return "transport";
|
|
205
|
+
case "ABORTED":
|
|
206
|
+
return "abort";
|
|
207
|
+
case "GATEWAY_5XX":
|
|
208
|
+
return json(503, { errorCode: "ERR.2SG.PROVIDER.ERROR", message: "sabre-client/testing: 503 forzado" });
|
|
209
|
+
case "UNPARSEABLE":
|
|
210
|
+
return new Response("<html>sabre-client/testing: cuerpo ilegible forzado</html>", { status: 200 });
|
|
211
|
+
case "END_TRANSACTION_PROBLEM":
|
|
212
|
+
return json(200, { request: echo, voidedTickets, errors: [appError("END_TRANSACTION_PROBLEM", "Confirmation is pending. Redisplay the booking", "WARNING")] });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return json(200, {
|
|
216
|
+
timestamp: now().toISOString().slice(0, 19),
|
|
217
|
+
request: echo,
|
|
218
|
+
voidedTickets,
|
|
219
|
+
...request.retrieveBooking === true ? { booking: bookingJson(locator, booking) } : {}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (request.cancelAll === true) booking.flights = 0;
|
|
223
|
+
return json(200, { timestamp: now().toISOString().slice(0, 19), request: echo, ...request.retrieveBooking === true ? { booking: bookingJson(locator, booking) } : {} });
|
|
224
|
+
};
|
|
225
|
+
return (url, body) => {
|
|
226
|
+
const path = new URL(url, "https://fake.invalid").pathname;
|
|
227
|
+
if (path.endsWith("/auth/token")) {
|
|
228
|
+
record("token");
|
|
229
|
+
return json(200, { access_token: "FAKE-TOKEN", token_type: "bearer", expires_in: 604800 });
|
|
230
|
+
}
|
|
231
|
+
if (!path.startsWith("/v1/trip/orders/")) return void 0;
|
|
232
|
+
const request = parse(body);
|
|
233
|
+
switch (path.slice("/v1/trip/orders/".length)) {
|
|
234
|
+
case "createBooking": {
|
|
235
|
+
const locator = nextLocator();
|
|
236
|
+
record("createBooking", locator);
|
|
237
|
+
const flights = (request.flightDetails?.flights ?? []).length;
|
|
238
|
+
bookings.set(locator, { flights: flights > 0 ? flights : 1, tickets: [] });
|
|
239
|
+
return json(200, { timestamp: now().toISOString().slice(0, 19), confirmationId: locator, request });
|
|
240
|
+
}
|
|
241
|
+
case "getBooking": {
|
|
242
|
+
const locator = String(request.confirmationId ?? "");
|
|
243
|
+
record("getBooking", locator);
|
|
244
|
+
const booking = bookings.get(locator);
|
|
245
|
+
return booking === void 0 ? notFound() : json(200, bookingJson(locator, booking));
|
|
246
|
+
}
|
|
247
|
+
case "cancelBooking":
|
|
248
|
+
return cancel(request);
|
|
249
|
+
case "fulfillFlightTickets":
|
|
250
|
+
return fulfill(request);
|
|
251
|
+
default:
|
|
252
|
+
return json(404, { errorCode: "ERR.2SG.NOT_FOUND", message: `sabre-client/testing: ${path} no est\xE1 simulado` });
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
4
257
|
// src/queue/queue-access.ts
|
|
5
258
|
import { XMLParser as XMLParser2 } from "fast-xml-parser";
|
|
6
259
|
|
|
@@ -126,6 +379,304 @@ function lineFields(value) {
|
|
|
126
379
|
};
|
|
127
380
|
}
|
|
128
381
|
|
|
382
|
+
// src/queue/host-date.ts
|
|
383
|
+
var MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
384
|
+
var DAY_MS = 864e5;
|
|
385
|
+
var HALF_YEAR_DAYS = 184;
|
|
386
|
+
function resolveHostDate(text2, referenceDate, options = {}) {
|
|
387
|
+
const reference = referenceDay(referenceDate);
|
|
388
|
+
const direction = options.direction ?? "past";
|
|
389
|
+
if (direction !== "past" && direction !== "nearest") {
|
|
390
|
+
throw new TypeError("sabre-client: direction debe ser 'past' o 'nearest'");
|
|
391
|
+
}
|
|
392
|
+
if (typeof text2 !== "string") return void 0;
|
|
393
|
+
const parsed = parseHostDate(text2.trim().toUpperCase());
|
|
394
|
+
if (parsed === void 0) return void 0;
|
|
395
|
+
const year = parsed.year ?? resolveYear(parsed.month, parsed.day, reference, direction);
|
|
396
|
+
if (year === void 0 || !isValidDate(year, parsed.month, parsed.day)) return void 0;
|
|
397
|
+
const date = `${pad(year, 4)}-${pad(parsed.month, 2)}-${pad(parsed.day, 2)}`;
|
|
398
|
+
return parsed.time === void 0 ? date : `${date}T${parsed.time}`;
|
|
399
|
+
}
|
|
400
|
+
function parseHostDate(text2) {
|
|
401
|
+
let match = /^(\d{1,2})([A-Z]{3})(\d{2})?$/.exec(text2);
|
|
402
|
+
if (match !== null) {
|
|
403
|
+
const month2 = MONTHS.indexOf(match[2]) + 1;
|
|
404
|
+
if (month2 === 0) return void 0;
|
|
405
|
+
return {
|
|
406
|
+
...match[3] !== void 0 && { year: 2e3 + Number(match[3]) },
|
|
407
|
+
month: month2,
|
|
408
|
+
day: Number(match[1])
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
match = /^(?:(\d{4})-)?(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/.exec(text2);
|
|
412
|
+
if (match === null) return void 0;
|
|
413
|
+
const [, year, month, day, hours, minutes] = match;
|
|
414
|
+
if (hours !== void 0 && (Number(hours) > 23 || Number(minutes) > 59)) return void 0;
|
|
415
|
+
return {
|
|
416
|
+
...year !== void 0 && { year: Number(year) },
|
|
417
|
+
month: Number(month),
|
|
418
|
+
day: Number(day),
|
|
419
|
+
...hours !== void 0 && { time: `${hours}:${minutes}` }
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function resolveYear(month, day, reference, direction) {
|
|
423
|
+
const year = new Date(reference).getUTCFullYear();
|
|
424
|
+
if (direction === "past") {
|
|
425
|
+
for (let candidate = year; candidate >= year - 8; candidate--) {
|
|
426
|
+
if (isValidDate(candidate, month, day) && dayNumber(candidate, month, day) <= reference) return candidate;
|
|
427
|
+
}
|
|
428
|
+
return void 0;
|
|
429
|
+
}
|
|
430
|
+
let best;
|
|
431
|
+
for (const candidate of [year + 1, year, year - 1]) {
|
|
432
|
+
if (!isValidDate(candidate, month, day)) continue;
|
|
433
|
+
const distance = Math.abs(dayNumber(candidate, month, day) - reference);
|
|
434
|
+
if (best === void 0 || distance < best.distance) best = { year: candidate, distance };
|
|
435
|
+
}
|
|
436
|
+
return best !== void 0 && best.distance <= HALF_YEAR_DAYS * DAY_MS ? best.year : void 0;
|
|
437
|
+
}
|
|
438
|
+
function referenceDay(referenceDate) {
|
|
439
|
+
if (referenceDate instanceof Date) {
|
|
440
|
+
const time = referenceDate.getTime();
|
|
441
|
+
if (Number.isNaN(time)) throw new TypeError("sabre-client: referenceDate es un Date inv\xE1lido");
|
|
442
|
+
return time - (time % DAY_MS + DAY_MS) % DAY_MS;
|
|
443
|
+
}
|
|
444
|
+
if (typeof referenceDate === "string") {
|
|
445
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})(?:T.*)?$/.exec(referenceDate.trim());
|
|
446
|
+
if (match !== null) {
|
|
447
|
+
const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
448
|
+
if (isValidDate(year, month, day)) return dayNumber(year, month, day);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
throw new TypeError("sabre-client: referenceDate es obligatoria, como 'YYYY-MM-DD' o un Date v\xE1lido");
|
|
452
|
+
}
|
|
453
|
+
function dayNumber(year, month, day) {
|
|
454
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
455
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
456
|
+
return date.getTime();
|
|
457
|
+
}
|
|
458
|
+
function isValidDate(year, month, day) {
|
|
459
|
+
if (month < 1 || month > 12 || day < 1) return false;
|
|
460
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
461
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
462
|
+
return date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
463
|
+
}
|
|
464
|
+
function pad(value, length) {
|
|
465
|
+
return String(value).padStart(length, "0");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/queue/queue-paragraph.ts
|
|
469
|
+
var MESSAGE = /^\s*(\d{3})\s{2,}(\S.*?)\s*$/;
|
|
470
|
+
var PASSENGER_LINE = /^\s*\d+\.\d+[A-Z]/;
|
|
471
|
+
var PASSENGER_SPLIT = /\s+(?=\d+\.\d+[A-Z])/;
|
|
472
|
+
var PASSENGER = /^(\d+\.\d+)([^/*]+?)\s*(?:\/([^*]*?))?\s*(?:\*(.*?))?\s*$/;
|
|
473
|
+
var AIR_SEGMENT = /^\s*(\d{1,2})\s+([A-Z0-9]{2})\s*(\d{1,4})([A-Z])\s+(\d{2}[A-Z]{3})\s+[A-Z0-9]\s+([A-Z]{3})([A-Z]{3})\s+([A-Z]{2})(\d{1,3})(?=\s|$)(.*)$/;
|
|
474
|
+
var SEGMENT_TIMES = /^\s+(\d{3,4}[APNM]?)\s+(\d{3,4}[APNM]?)(?=\s|$)/;
|
|
475
|
+
var OTHER_SEGMENT = /^\s*(\d{1,2})\s+([A-Z]{3})\s+([A-Z0-9]{2})\s+(\d{2}[A-Z]{3})\s+[A-Z0-9]\s+([A-Z]{2})(\d{1,3})(?=\s|$)/;
|
|
476
|
+
var NO_ITINERARY = /^\s*NO ITIN\s*$/;
|
|
477
|
+
var TICKETING_HEADER = /^\s*TKT\/TIME LIMIT\s*$/;
|
|
478
|
+
var TICKETING_ENTRY = /^\s*(\d+)\.(.*?)\s*$/;
|
|
479
|
+
var TICKETED = /^T-(\d{2}[A-Z]{3})?(?:-([A-Z0-9]{3,4})\*([A-Z0-9]+))?(?:\/.*)?$/;
|
|
480
|
+
var ARRANGEMENT = /^(TA[WCMTX])([A-Z0-9]{3,4})?(\d{2}[A-Z]{3})?\d{0,3}\/.*$/;
|
|
481
|
+
var TICKET = /^(TE|TK)\s+(\d{13})\b/;
|
|
482
|
+
var RECEIVED = /^\s*RECEIVED FROM -\s?(.*?)\s*$/;
|
|
483
|
+
var SIGNATURE = /^\s*([A-Z0-9]{3,4})\.(\S+)\s+(\d{2})(\d{2})\/(\d{2}[A-Z]{3}\d{2})\s+([A-Z0-9]{6})\s+H\s*$/;
|
|
484
|
+
var Phase = { Messages: 0, Passengers: 1, Segments: 2, Body: 3 };
|
|
485
|
+
function parseQueueParagraph(lines, options) {
|
|
486
|
+
if (!Array.isArray(lines)) throw new TypeError("sabre-client: lines debe ser un array de strings");
|
|
487
|
+
if (typeof options !== "object" || options === null) {
|
|
488
|
+
throw new TypeError("sabre-client: referenceDate es obligatoria, como 'YYYY-MM-DD' o un Date v\xE1lido");
|
|
489
|
+
}
|
|
490
|
+
const { referenceDate } = options;
|
|
491
|
+
referenceDay(referenceDate);
|
|
492
|
+
const result = { messages: [], passengers: [], segments: [], unparsed: [] };
|
|
493
|
+
const signatureIndex = lastSignatureIndex(lines);
|
|
494
|
+
let phase = Phase.Messages;
|
|
495
|
+
let inTicketing = false;
|
|
496
|
+
lines.forEach((value, index) => {
|
|
497
|
+
const line = typeof value === "string" ? value : "";
|
|
498
|
+
if (line.trim() === "") return;
|
|
499
|
+
if (index === signatureIndex) {
|
|
500
|
+
result.receivedFrom = { ...result.receivedFrom, ...parseSignature(line, referenceDate) };
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (inTicketing) {
|
|
504
|
+
const entry = TICKETING_ENTRY.exec(line);
|
|
505
|
+
if (entry !== null) {
|
|
506
|
+
const parsed = parseTicketingEntry(Number(entry[1]), entry[2], referenceDate);
|
|
507
|
+
if (parsed === void 0) result.unparsed.push(line);
|
|
508
|
+
else result.ticketing.entries.push(parsed);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
inTicketing = false;
|
|
512
|
+
}
|
|
513
|
+
if (TICKETING_HEADER.test(line)) {
|
|
514
|
+
phase = Phase.Body;
|
|
515
|
+
inTicketing = true;
|
|
516
|
+
result.ticketing ??= { entries: [], issued: false };
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const received = RECEIVED.exec(line);
|
|
520
|
+
if (received !== null) {
|
|
521
|
+
phase = Phase.Body;
|
|
522
|
+
result.receivedFrom = { ...result.receivedFrom, ...received[1] !== "" && { name: received[1] } };
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (phase === Phase.Messages) {
|
|
526
|
+
const message = MESSAGE.exec(line);
|
|
527
|
+
if (message !== null) {
|
|
528
|
+
result.messages.push({ instructionCode: Number(message[1]), text: message[2] });
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (phase <= Phase.Passengers && PASSENGER_LINE.test(line)) {
|
|
533
|
+
const passengers = parsePassengers(line);
|
|
534
|
+
if (passengers !== void 0) {
|
|
535
|
+
phase = Phase.Passengers;
|
|
536
|
+
result.passengers.push(...passengers);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (phase <= Phase.Segments) {
|
|
541
|
+
if (NO_ITINERARY.test(line)) {
|
|
542
|
+
phase = Phase.Segments;
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const segment = parseSegment(line, referenceDate);
|
|
546
|
+
if (segment !== void 0) {
|
|
547
|
+
phase = Phase.Segments;
|
|
548
|
+
result.segments.push(segment);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
result.unparsed.push(line);
|
|
553
|
+
});
|
|
554
|
+
if (result.ticketing !== void 0) {
|
|
555
|
+
result.ticketing.issued = result.ticketing.entries.some((entry) => entry.ticketNumber !== void 0);
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
function parsePassengers(line) {
|
|
560
|
+
const passengers = [];
|
|
561
|
+
for (const part of line.trim().split(PASSENGER_SPLIT)) {
|
|
562
|
+
const match = PASSENGER.exec(part);
|
|
563
|
+
if (match === null) return void 0;
|
|
564
|
+
const [, nameNumber, surname, givenName, nameRemark] = match;
|
|
565
|
+
passengers.push({
|
|
566
|
+
nameNumber,
|
|
567
|
+
surname: surname.trim(),
|
|
568
|
+
...givenName !== void 0 && givenName !== "" && { givenName },
|
|
569
|
+
...nameRemark !== void 0 && nameRemark !== "" && { nameRemark },
|
|
570
|
+
text: part.slice(nameNumber.length).trim()
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
return passengers;
|
|
574
|
+
}
|
|
575
|
+
function parseSegment(line, referenceDate) {
|
|
576
|
+
const air = AIR_SEGMENT.exec(line);
|
|
577
|
+
if (air !== null) {
|
|
578
|
+
const [, number2, airlineCode, flightNumber, bookingClass, date2, from, to, status2, quantity2, rest] = air;
|
|
579
|
+
const times = SEGMENT_TIMES.exec(rest);
|
|
580
|
+
const departureTime = times === null ? void 0 : hostTime(times[1]);
|
|
581
|
+
const arrivalTime = times === null ? void 0 : hostTime(times[2]);
|
|
582
|
+
return {
|
|
583
|
+
kind: "air",
|
|
584
|
+
number: Number(number2),
|
|
585
|
+
...withDate(date2, referenceDate),
|
|
586
|
+
status: status2,
|
|
587
|
+
quantity: Number(quantity2),
|
|
588
|
+
airlineCode,
|
|
589
|
+
flightNumber: Number(flightNumber),
|
|
590
|
+
bookingClass,
|
|
591
|
+
fromAirportCode: from,
|
|
592
|
+
toAirportCode: to,
|
|
593
|
+
...departureTime !== void 0 && { departureTime },
|
|
594
|
+
...arrivalTime !== void 0 && { arrivalTime },
|
|
595
|
+
text: line.trim()
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
const other = OTHER_SEGMENT.exec(line);
|
|
599
|
+
if (other === null) return void 0;
|
|
600
|
+
const [, number, segmentType, vendorCode, date, status, quantity] = other;
|
|
601
|
+
return {
|
|
602
|
+
kind: "other",
|
|
603
|
+
number: Number(number),
|
|
604
|
+
...withDate(date, referenceDate),
|
|
605
|
+
status,
|
|
606
|
+
quantity: Number(quantity),
|
|
607
|
+
segmentType,
|
|
608
|
+
vendorCode,
|
|
609
|
+
text: line.trim()
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function parseTicketingEntry(number, text2, referenceDate) {
|
|
613
|
+
const ticketed = TICKETED.exec(text2);
|
|
614
|
+
if (ticketed !== null) {
|
|
615
|
+
const [, date, pseudoCityCode, agentSine] = ticketed;
|
|
616
|
+
return {
|
|
617
|
+
number,
|
|
618
|
+
code: "T-",
|
|
619
|
+
...date !== void 0 && withDate(date, referenceDate),
|
|
620
|
+
...pseudoCityCode !== void 0 && { pseudoCityCode },
|
|
621
|
+
...agentSine !== void 0 && { agentSine },
|
|
622
|
+
text: text2
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
const arrangement = ARRANGEMENT.exec(text2);
|
|
626
|
+
if (arrangement !== null) {
|
|
627
|
+
const [, code, pseudoCityCode, date] = arrangement;
|
|
628
|
+
return {
|
|
629
|
+
number,
|
|
630
|
+
code,
|
|
631
|
+
...date !== void 0 && withDate(date, referenceDate),
|
|
632
|
+
...pseudoCityCode !== void 0 && { pseudoCityCode },
|
|
633
|
+
text: text2
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const ticket = TICKET.exec(text2);
|
|
637
|
+
if (ticket !== null) return { number, code: ticket[1], ticketNumber: ticket[2], text: text2 };
|
|
638
|
+
return void 0;
|
|
639
|
+
}
|
|
640
|
+
function lastSignatureIndex(lines) {
|
|
641
|
+
for (let index = lines.length - 1; index >= 0; index--) {
|
|
642
|
+
const line = lines[index];
|
|
643
|
+
if (typeof line === "string" && SIGNATURE.test(line)) return index;
|
|
644
|
+
}
|
|
645
|
+
return -1;
|
|
646
|
+
}
|
|
647
|
+
function parseSignature(line, referenceDate) {
|
|
648
|
+
const [, pseudoCityCode, sign, hours, minutes, date, locator] = SIGNATURE.exec(line);
|
|
649
|
+
const agentSine = /\*([A-Z0-9]+)$/.exec(sign)?.[1];
|
|
650
|
+
const day = Number(hours) <= 23 && Number(minutes) <= 59 ? resolveHostDate(date, referenceDate) : void 0;
|
|
651
|
+
return {
|
|
652
|
+
pseudoCityCode,
|
|
653
|
+
...agentSine !== void 0 && { agentSine },
|
|
654
|
+
...day !== void 0 && { receivedAt: `${day}T${hours}:${minutes}` },
|
|
655
|
+
locator
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function withDate(text2, referenceDate) {
|
|
659
|
+
const date = resolveHostDate(text2, referenceDate, { direction: "nearest" });
|
|
660
|
+
return date === void 0 ? {} : { date };
|
|
661
|
+
}
|
|
662
|
+
function hostTime(text2) {
|
|
663
|
+
const match = /^(\d{1,2})(\d{2})([APNM])?$/.exec(text2);
|
|
664
|
+
if (match === null) return void 0;
|
|
665
|
+
let hours = Number(match[1]);
|
|
666
|
+
const minutes = Number(match[2]);
|
|
667
|
+
const suffix = match[3];
|
|
668
|
+
if (minutes > 59) return void 0;
|
|
669
|
+
if (suffix === void 0) {
|
|
670
|
+
if (hours > 23) return void 0;
|
|
671
|
+
} else {
|
|
672
|
+
if (hours < 1 || hours > 12) return void 0;
|
|
673
|
+
if (suffix === "A" || suffix === "M") hours %= 12;
|
|
674
|
+
else if (suffix === "P") hours = hours % 12 + 12;
|
|
675
|
+
else if (hours !== 12) return void 0;
|
|
676
|
+
}
|
|
677
|
+
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
|
678
|
+
}
|
|
679
|
+
|
|
129
680
|
// src/queue/queue-place.ts
|
|
130
681
|
var SIGNATURE_LINE = /\s\d{4}\/\d{2}[A-Z]{3}\d{2}\s+([A-Z0-9]{6})\s+H\s*$/;
|
|
131
682
|
function signatureLocator(lines) {
|
|
@@ -140,9 +691,18 @@ function signatureLocator(lines) {
|
|
|
140
691
|
var SWS_NAMESPACE = "http://webservices.sabre.com/sabreXML/2011/10";
|
|
141
692
|
var STL_NAMESPACE = "http://services.sabre.com/STL/v01";
|
|
142
693
|
var SIGNATURE_DATE = "1200/01JAN26";
|
|
143
|
-
var
|
|
694
|
+
var LOCATOR2 = /^[A-Z0-9]{6}$/;
|
|
144
695
|
var NAVIGATION = { I: "ignore", QR: "remove", QXI: "exit" };
|
|
145
|
-
|
|
696
|
+
var QUEUE_FAKE_SELECTION_FUNCTIONS = ["QD", "AK", "AL", "PI", "AC"];
|
|
697
|
+
var LIST_SELECTION_FUNCTIONS = ["QD", "AK"];
|
|
698
|
+
var MONTHS2 = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
699
|
+
function placedOn(placedAt, criteria) {
|
|
700
|
+
const date = /^(\d{1,2})([A-Z]{3})$/.exec(criteria.toUpperCase());
|
|
701
|
+
const placed = /(\d{2})-(\d{2})T/.exec(placedAt ?? "");
|
|
702
|
+
if (date === null || placed === null) return false;
|
|
703
|
+
return Number(placed[1]) === MONTHS2.indexOf(date[2]) + 1 && Number(placed[2]) === Number(date[1]);
|
|
704
|
+
}
|
|
705
|
+
function fail2(message) {
|
|
146
706
|
throw new TypeError(`sabre-client/testing: ${message}`);
|
|
147
707
|
}
|
|
148
708
|
var parser2 = new XMLParser3({
|
|
@@ -188,18 +748,18 @@ function refOfKey(key) {
|
|
|
188
748
|
if (key.startsWith(NAME_PREFIX)) return { name: key.slice(NAME_PREFIX.length) };
|
|
189
749
|
return /^\d+$/.test(key) ? Number(key) : key;
|
|
190
750
|
}
|
|
191
|
-
function keyAttribute(key,
|
|
751
|
+
function keyAttribute(key, pad2 = false) {
|
|
192
752
|
if (key.startsWith(NAME_PREFIX)) return `Name="${escape(key.slice(NAME_PREFIX.length))}"`;
|
|
193
|
-
return `Number="${escape(
|
|
753
|
+
return `Number="${escape(pad2 && /^\d+$/.test(key) ? key.padStart(4, "0") : key)}"`;
|
|
194
754
|
}
|
|
195
755
|
function validateKey(key) {
|
|
196
756
|
if (/^\d+$/.test(key)) {
|
|
197
757
|
const number = Number(key);
|
|
198
|
-
if (number < MIN_QUEUE_NUMBER || number > MAX_QUEUE_NUMBER)
|
|
758
|
+
if (number < MIN_QUEUE_NUMBER || number > MAX_QUEUE_NUMBER) fail2(`la cola ${key} est\xE1 fuera de 0\u2013511`);
|
|
199
759
|
return;
|
|
200
760
|
}
|
|
201
761
|
if (key.startsWith(NAME_PREFIX) ? key.slice(NAME_PREFIX.length).trim() === "" : key.trim() === "") {
|
|
202
|
-
|
|
762
|
+
fail2("el nombre de una cola no puede estar vac\xEDo");
|
|
203
763
|
}
|
|
204
764
|
}
|
|
205
765
|
function shortDate(date) {
|
|
@@ -211,26 +771,26 @@ function longDate(date) {
|
|
|
211
771
|
function toMessage(item, index) {
|
|
212
772
|
const label = `queues['${MESSAGE_QUEUE}'][${index}]`;
|
|
213
773
|
if (!Array.isArray(item) || item.some((line) => typeof line !== "string")) {
|
|
214
|
-
|
|
774
|
+
fail2(`${label}: la S es la cola de mensajes; cada elemento es el texto de un mensaje (string[])`);
|
|
215
775
|
}
|
|
216
776
|
const lines = item;
|
|
217
|
-
if (!lines.some((line) => line.trim() !== ""))
|
|
777
|
+
if (!lines.some((line) => line.trim() !== "")) fail2(`${label}: el mensaje no puede estar vac\xEDo`);
|
|
218
778
|
return [...lines];
|
|
219
779
|
}
|
|
220
780
|
function toPnr(item, label) {
|
|
221
781
|
if (Array.isArray(item)) {
|
|
222
782
|
const lines2 = item;
|
|
223
783
|
const locator = signatureLocator(lines2);
|
|
224
|
-
if (locator === void 0)
|
|
784
|
+
if (locator === void 0) fail2(`${label}: el p\xE1rrafo no tiene l\xEDnea de firma; pasa { locator, paragraph }`);
|
|
225
785
|
return { locator, paragraph: [...lines2], primaryPassenger: void 0, placedAt: void 0, placedBy: void 0 };
|
|
226
786
|
}
|
|
227
787
|
const pnr = item;
|
|
228
|
-
if (typeof pnr !== "object" || pnr === null || typeof pnr.locator !== "string" || !
|
|
229
|
-
|
|
788
|
+
if (typeof pnr !== "object" || pnr === null || typeof pnr.locator !== "string" || !LOCATOR2.test(pnr.locator)) {
|
|
789
|
+
fail2(`${label}: locator debe ser de 6 caracteres alfanum\xE9ricos en may\xFAscula`);
|
|
230
790
|
}
|
|
231
791
|
const lines = [...pnr.paragraph ?? []];
|
|
232
792
|
const signed = signatureLocator(lines);
|
|
233
|
-
if (signed !== void 0 && signed !== pnr.locator)
|
|
793
|
+
if (signed !== void 0 && signed !== pnr.locator) fail2(`${label}: la l\xEDnea de firma es de ${signed}, no de ${pnr.locator}`);
|
|
234
794
|
if (signed === void 0) lines.push(`XXXX.XXXX*AAA ${SIGNATURE_DATE} ${pnr.locator} H`);
|
|
235
795
|
return {
|
|
236
796
|
locator: pnr.locator,
|
|
@@ -281,22 +841,22 @@ var ACTIVITY_COUNTERS = [
|
|
|
281
841
|
["unableToReach", "UnableToReach"]
|
|
282
842
|
];
|
|
283
843
|
function toActivity(value, label) {
|
|
284
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
844
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) fail2(`${label} debe ser un objeto de contadores`);
|
|
285
845
|
const known = new Set(ACTIVITY_COUNTERS.map(([key]) => key));
|
|
286
846
|
const result = {};
|
|
287
847
|
for (const [key, count] of Object.entries(value)) {
|
|
288
|
-
if (key === "numMessages")
|
|
289
|
-
if (!known.has(key))
|
|
290
|
-
if (!Number.isInteger(count) || count < 0)
|
|
848
|
+
if (key === "numMessages") fail2(`${label}.numMessages sale de la cola del fake: no se siembra`);
|
|
849
|
+
if (!known.has(key)) fail2(`${label}.${key} no es un contador de queue.activity`);
|
|
850
|
+
if (!Number.isInteger(count) || count < 0) fail2(`${label}.${key} debe ser un entero de 0 en adelante`);
|
|
291
851
|
result[key] = count;
|
|
292
852
|
}
|
|
293
853
|
return result;
|
|
294
854
|
}
|
|
295
855
|
function toHistory(value, label) {
|
|
296
|
-
if (!Array.isArray(value))
|
|
856
|
+
if (!Array.isArray(value)) fail2(`${label} debe ser una lista de transacciones`);
|
|
297
857
|
return value.map((transaction, index) => {
|
|
298
858
|
const at = `${label}[${index}]`;
|
|
299
|
-
if (typeof transaction !== "object" || transaction === null)
|
|
859
|
+
if (typeof transaction !== "object" || transaction === null) fail2(`${at} debe ser un objeto`);
|
|
300
860
|
const t = transaction;
|
|
301
861
|
for (const [list, name] of [
|
|
302
862
|
[t.flightSegments, "flightSegments"],
|
|
@@ -304,12 +864,12 @@ function toHistory(value, label) {
|
|
|
304
864
|
[t.remarks, "remarks"]
|
|
305
865
|
]) {
|
|
306
866
|
if (list === void 0) continue;
|
|
307
|
-
if (!Array.isArray(list))
|
|
867
|
+
if (!Array.isArray(list)) fail2(`${at}.${name} debe ser una lista`);
|
|
308
868
|
list.forEach((entry, position) => {
|
|
309
|
-
if (typeof entry?.action !== "string" || entry.action.trim() === "")
|
|
869
|
+
if (typeof entry?.action !== "string" || entry.action.trim() === "") fail2(`${at}.${name}[${position}].action debe ser un string no vac\xEDo`);
|
|
310
870
|
});
|
|
311
871
|
}
|
|
312
|
-
if (t.accounting !== void 0 && !Array.isArray(t.accounting))
|
|
872
|
+
if (t.accounting !== void 0 && !Array.isArray(t.accounting)) fail2(`${at}.accounting debe ser una lista`);
|
|
313
873
|
return t;
|
|
314
874
|
});
|
|
315
875
|
}
|
|
@@ -352,11 +912,57 @@ function historicalInfo(t) {
|
|
|
352
912
|
"</HistoricalInfo>"
|
|
353
913
|
].join("");
|
|
354
914
|
}
|
|
915
|
+
var DAILY_SALES_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
916
|
+
function toDailySalesDay(value, label) {
|
|
917
|
+
const day = Array.isArray(value) ? { transactions: value } : value;
|
|
918
|
+
if (typeof day !== "object" || day === null || !Array.isArray(day.transactions)) fail2(`${label} debe ser una lista de transacciones o { transactions }`);
|
|
919
|
+
if (day.status !== void 0 && !["complete", "incomplete", "deferred"].includes(day.status)) fail2(`${label}.status debe ser complete, incomplete o deferred`);
|
|
920
|
+
for (const [index, t] of day.transactions.entries()) {
|
|
921
|
+
const at = `${label}.transactions[${index}]`;
|
|
922
|
+
if (typeof t !== "object" || t === null) fail2(`${at} debe ser un objeto`);
|
|
923
|
+
for (const field of ["documentType", "transactionCode", "settlementType", "documentNumber", "agentSine", "transactionDateTime"]) {
|
|
924
|
+
if (typeof t[field] !== "string") fail2(`${at}.${field} debe ser un string`);
|
|
925
|
+
}
|
|
926
|
+
if (typeof t.airline?.code !== "string") fail2(`${at}.airline.code debe ser un string`);
|
|
927
|
+
}
|
|
928
|
+
return day;
|
|
929
|
+
}
|
|
930
|
+
function transactionXml(t) {
|
|
931
|
+
const el = (name, value) => value === void 0 ? "" : `<asr:${name}>${escape(value)}</asr:${name}>`;
|
|
932
|
+
const payments = t.payments === void 0 ? "" : `<asr:Payments>${el("CurrencyCode", t.payments.currencyCode)}<asr:DocumentPayment>${el("PaymentTotal", t.payments.total)}${t.payments.details.map((d) => `<asr:PaymentDetail${attrs([["fop", d.fop], ["category", d.category], ["cardType", d.cardType]])}>${el("PaymentAmount", d.amount)}</asr:PaymentDetail>`).join("")}</asr:DocumentPayment></asr:Payments>`;
|
|
933
|
+
const flags = attrs([
|
|
934
|
+
["nonInteractive", t.nonInteractive === void 0 ? void 0 : String(t.nonInteractive)],
|
|
935
|
+
["electronicDocument", t.electronicDocument === void 0 ? void 0 : String(t.electronicDocument)],
|
|
936
|
+
["itineraryType", t.itineraryType],
|
|
937
|
+
["automatedRefund", t.automatedRefund === void 0 ? void 0 : String(t.automatedRefund)]
|
|
938
|
+
]);
|
|
939
|
+
return [
|
|
940
|
+
`<asr:Transaction${flags}>`,
|
|
941
|
+
el("DocumentType", t.documentType),
|
|
942
|
+
el("SettlementType", t.settlementType),
|
|
943
|
+
el("PnrLocator", t.pnrLocator),
|
|
944
|
+
el("PassengerName", t.passengerName),
|
|
945
|
+
"<asr:DocumentDetails>",
|
|
946
|
+
el("DocumentNumber", t.documentNumber),
|
|
947
|
+
el("LastConjunctiveDocumentNumber", t.lastConjunctiveDocumentNumber),
|
|
948
|
+
el("StockType", t.stockType),
|
|
949
|
+
el("DocumentStatusCode", t.documentStatusCode),
|
|
950
|
+
el("TransactionCode", t.transactionCode),
|
|
951
|
+
"</asr:DocumentDetails>",
|
|
952
|
+
`<asr:AirlineCode${attrs([["AirlineNumber", t.airline.number]])}>${escape(t.airline.code)}</asr:AirlineCode>`,
|
|
953
|
+
t.commission === void 0 ? "" : `<asr:Commission>${el("Amount", t.commission.amount)}${el("Percent", t.commission.percent)}</asr:Commission>`,
|
|
954
|
+
payments,
|
|
955
|
+
el("AgentSine", t.agentSine),
|
|
956
|
+
el("AgentPseudoCity", t.agentPseudoCity),
|
|
957
|
+
el("TransactionDateTime", t.transactionDateTime),
|
|
958
|
+
"</asr:Transaction>"
|
|
959
|
+
].join("");
|
|
960
|
+
}
|
|
355
961
|
var NOT_ALLOWED_INSIDE_QUEUE = "NAK6 -NOT ALLOWED- FINISH OR IGNORE OTHER ACTIVITY";
|
|
356
962
|
function createQueueFake(options = {}) {
|
|
357
|
-
if (typeof options !== "object" || options === null)
|
|
963
|
+
if (typeof options !== "object" || options === null) fail2("options debe ser un objeto");
|
|
358
964
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
359
|
-
if (typeof now !== "function")
|
|
965
|
+
if (typeof now !== "function") fail2("now debe ser una funci\xF3n");
|
|
360
966
|
const pnrs = /* @__PURE__ */ new Map();
|
|
361
967
|
const queues = /* @__PURE__ */ new Map();
|
|
362
968
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -366,7 +972,7 @@ function createQueueFake(options = {}) {
|
|
|
366
972
|
const pnr = toPnr(item, label);
|
|
367
973
|
const known = pnrs.get(pnr.locator);
|
|
368
974
|
if (known !== void 0 && JSON.stringify(known.paragraph) !== JSON.stringify(pnr.paragraph)) {
|
|
369
|
-
|
|
975
|
+
fail2(`${label}: el PNR ${pnr.locator} aparece con dos p\xE1rrafos distintos`);
|
|
370
976
|
}
|
|
371
977
|
if (known === void 0) {
|
|
372
978
|
pnrs.set(pnr.locator, { locator: pnr.locator, paragraph: pnr.paragraph, primaryPassenger: pnr.primaryPassenger });
|
|
@@ -376,7 +982,7 @@ function createQueueFake(options = {}) {
|
|
|
376
982
|
for (const [optionName, items] of Object.entries(options.queues ?? {})) {
|
|
377
983
|
const key = optionKey(optionName);
|
|
378
984
|
validateKey(key);
|
|
379
|
-
if (!Array.isArray(items))
|
|
985
|
+
if (!Array.isArray(items)) fail2(`queues['${key}'] debe ser una lista`);
|
|
380
986
|
if (key === MESSAGE_QUEUE) {
|
|
381
987
|
queues.set(key, items.map((item, index) => ({ locator: "", placedAt: void 0, placedBy: void 0, message: toMessage(item, index) })));
|
|
382
988
|
continue;
|
|
@@ -396,9 +1002,25 @@ function createQueueFake(options = {}) {
|
|
|
396
1002
|
validateKey(key);
|
|
397
1003
|
activity.set(key, toActivity(counters, `activity['${key}']`));
|
|
398
1004
|
}
|
|
1005
|
+
const dailySalesTimeZone = options.dailySalesTimeZone ?? "UTC";
|
|
1006
|
+
let dailySalesDay;
|
|
1007
|
+
try {
|
|
1008
|
+
dailySalesDay = new Intl.DateTimeFormat("en-CA", { timeZone: dailySalesTimeZone, year: "numeric", month: "2-digit", day: "2-digit" });
|
|
1009
|
+
} catch {
|
|
1010
|
+
fail2(`dailySalesTimeZone: '${dailySalesTimeZone}' no es una zona IANA v\xE1lida`);
|
|
1011
|
+
}
|
|
1012
|
+
const dailySales = /* @__PURE__ */ new Map();
|
|
1013
|
+
for (const [pccKey, days] of Object.entries(options.dailySales ?? {})) {
|
|
1014
|
+
if (!/^[A-Z0-9]{3,4}$/.test(pccKey)) fail2(`dailySales['${pccKey}']: la clave debe ser un PCC de 3 o 4 letras may\xFAsculas o d\xEDgitos`);
|
|
1015
|
+
if (typeof days !== "object" || days === null) fail2(`dailySales['${pccKey}'] debe ser un objeto con las fechas`);
|
|
1016
|
+
for (const [date, day] of Object.entries(days)) {
|
|
1017
|
+
if (!DAILY_SALES_DATE.test(date)) fail2(`dailySales['${pccKey}']['${date}']: la fecha debe tener la forma 'YYYY-MM-DD'`);
|
|
1018
|
+
dailySales.set(`${pccKey} ${date}`, toDailySalesDay(day, `dailySales['${pccKey}']['${date}']`));
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
399
1021
|
const histories = /* @__PURE__ */ new Map();
|
|
400
1022
|
for (const [locator, transactions] of Object.entries(options.histories ?? {})) {
|
|
401
|
-
if (!
|
|
1023
|
+
if (!LOCATOR2.test(locator)) fail2(`histories['${locator}']: la clave debe ser un localizador de 6 caracteres alfanum\xE9ricos en may\xFAscula`);
|
|
402
1024
|
histories.set(locator, toHistory(transactions, `histories['${locator}']`));
|
|
403
1025
|
}
|
|
404
1026
|
const queue = (key) => {
|
|
@@ -438,7 +1060,43 @@ function createQueueFake(options = {}) {
|
|
|
438
1060
|
const placeRS = (inner) => `<QueuePlaceRS xmlns="${SWS_NAMESPACE}" xmlns:stl="${STL_NAMESPACE}" Version="2.0.4">${inner}</QueuePlaceRS>`;
|
|
439
1061
|
const accessError = (message) => accessRS(results("NotProcessed", hostResult("Error", message)));
|
|
440
1062
|
const placeError = (message) => placeRS(results("NotProcessed", hostResult("Error", message)));
|
|
1063
|
+
const matchesFilter = (entry, fn, criteria) => {
|
|
1064
|
+
if (entry.message !== void 0) return false;
|
|
1065
|
+
const wanted = criteria.trim().toUpperCase();
|
|
1066
|
+
const paragraph = () => parseQueueParagraph(pnrs.get(entry.locator)?.paragraph ?? [], { referenceDate: now() });
|
|
1067
|
+
switch (fn.toUpperCase()) {
|
|
1068
|
+
case "QD":
|
|
1069
|
+
return placedOn(entry.placedAt, wanted);
|
|
1070
|
+
case "AK":
|
|
1071
|
+
return `${entry.placedBy?.pseudoCityCode ?? ""}${entry.placedBy?.agentSine ?? ""}`.toUpperCase() === wanted;
|
|
1072
|
+
case "AL":
|
|
1073
|
+
return paragraph().segments.some((segment) => segment.kind === "air" && segment.airlineCode === wanted);
|
|
1074
|
+
case "AC":
|
|
1075
|
+
return paragraph().segments.some((segment) => segment.status.startsWith(wanted));
|
|
1076
|
+
case "PI": {
|
|
1077
|
+
const code = Number(wanted);
|
|
1078
|
+
if (entry.instructionCode !== void 0) return entry.instructionCode === code;
|
|
1079
|
+
return paragraph().messages.some((message) => message.instructionCode === code);
|
|
1080
|
+
}
|
|
1081
|
+
default:
|
|
1082
|
+
return void 0;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
const selectionOf = (rq, allowed) => {
|
|
1086
|
+
const filters = nodes(rq.Selection).map((selection) => ({ fn: (attr(selection, "Function") ?? "").toUpperCase(), criteria: attr(selection, "Criteria") ?? "" }));
|
|
1087
|
+
if (filters.length === 0) return {};
|
|
1088
|
+
const unsupported = filters.find((filter) => !allowed.includes(filter.fn));
|
|
1089
|
+
if (unsupported !== void 0) return { error: `sabre-client/testing: selection ${unsupported.fn} no est\xE1 simulado (simula ${allowed.join(", ")})` };
|
|
1090
|
+
return { matches: (entry) => filters.every((filter) => matchesFilter(entry, filter.fn, filter.criteria) === true) };
|
|
1091
|
+
};
|
|
1092
|
+
const advance = (session) => {
|
|
1093
|
+
const inside = session.inside;
|
|
1094
|
+
if (inside?.matches === void 0) return;
|
|
1095
|
+
const entries = queue(inside.key);
|
|
1096
|
+
while (inside.index < entries.length && !inside.matches(entries[inside.index])) inside.index += 1;
|
|
1097
|
+
};
|
|
441
1098
|
const step = (session) => {
|
|
1099
|
+
advance(session);
|
|
442
1100
|
const inside = session.inside;
|
|
443
1101
|
const entry = inside === void 0 ? void 0 : queue(inside.key)[inside.index];
|
|
444
1102
|
if (entry === void 0) {
|
|
@@ -452,19 +1110,51 @@ function createQueueFake(options = {}) {
|
|
|
452
1110
|
return accessRS(`${results("Complete", success)}${lineXml(entry)}<Paragraph>${textLines2(pnr.paragraph)}</Paragraph>`);
|
|
453
1111
|
};
|
|
454
1112
|
const waiting = (session, key, entries) => entries.length - (session.inside?.key === key && entries[session.inside.index] !== void 0 ? 1 : 0);
|
|
1113
|
+
const countRS = (inner) => `<QueueCountRS xmlns="${SWS_NAMESPACE}" xmlns:stl="${STL_NAMESPACE}" Version="2.2.1">${inner}</QueueCountRS>`;
|
|
455
1114
|
const count = (session, rq) => {
|
|
456
|
-
const
|
|
1115
|
+
const identifier = node2(node2(rq.QueueInfo)?.QueueIdentifier);
|
|
1116
|
+
const pcc = attr(identifier, "PseudoCityCode") ?? "";
|
|
1117
|
+
const countError = (message) => countRS(results("NotProcessed", hostResult("Error", message)));
|
|
1118
|
+
const range = node2(identifier?.Range);
|
|
1119
|
+
const requested = requestedKey(identifier);
|
|
1120
|
+
const { matches, error } = selectionOf(rq, QUEUE_FAKE_SELECTION_FUNCTIONS);
|
|
1121
|
+
if (matches !== void 0 || error !== void 0) {
|
|
1122
|
+
if (range !== void 0) return countError("KEYS NOT ALLOWED");
|
|
1123
|
+
if (requested === void 0) return countError("QUEUE SELECTED NOT VALID OR CLOSED");
|
|
1124
|
+
if (error !== void 0) return countError(error);
|
|
1125
|
+
}
|
|
1126
|
+
const info = (identifiers2) => `${results("Complete", success)}<QueueInfo DateTime="${shortDate(now())}" PseudoCityCode="${escape(pcc)}">${identifiers2.join("")}</QueueInfo>`;
|
|
1127
|
+
const totalsXml = (messages2, pnrs2) => `<Totals Count="${messages2}" Type="MESSAGES"/><Totals Count="0" Type="SPECIALS"/><Totals Count="${pnrs2}" Type="PNRS"/>`;
|
|
1128
|
+
const waitingIn = (key) => waiting(session, key, queues.get(key) ?? []);
|
|
1129
|
+
if (range !== void 0) {
|
|
1130
|
+
const start = Number(attr(range, "Start"));
|
|
1131
|
+
const end = Number(attr(range, "End"));
|
|
1132
|
+
const identifiers2 = [];
|
|
1133
|
+
let pnrsTotal2 = 0;
|
|
1134
|
+
for (let number = start; number <= end; number++) {
|
|
1135
|
+
const total = waitingIn(String(number));
|
|
1136
|
+
identifiers2.push(`<QueueIdentifier Count="${total}" Number="${number}"/>`);
|
|
1137
|
+
pnrsTotal2 += total;
|
|
1138
|
+
}
|
|
1139
|
+
return countRS(`${info(identifiers2)}${totalsXml(0, pnrsTotal2)}`);
|
|
1140
|
+
}
|
|
1141
|
+
if (requested !== void 0) {
|
|
1142
|
+
if (matches === void 0) return countRS(info([`<QueueIdentifier Count="${waitingIn(requested)}" ${keyAttribute(requested)}/>`]));
|
|
1143
|
+
const onScreen = session.inside?.key === requested ? queue(requested)[session.inside.index] : void 0;
|
|
1144
|
+
const matching = (queues.get(requested) ?? []).filter((entry) => entry !== onScreen && matches(entry)).length;
|
|
1145
|
+
return countRS(`${info([`<QueueIdentifier Count="${matching}" ${keyAttribute(requested)}/>`])}<Totals Count="${waitingIn(requested)}"/>`);
|
|
1146
|
+
}
|
|
457
1147
|
let pnrsTotal = 0;
|
|
458
1148
|
let messages = 0;
|
|
459
1149
|
const identifiers = [];
|
|
460
|
-
for (const [
|
|
461
|
-
const total = waiting(session,
|
|
1150
|
+
for (const [queueKey2, entries] of queues) {
|
|
1151
|
+
const total = waiting(session, queueKey2, entries);
|
|
462
1152
|
if (total <= 0) continue;
|
|
463
|
-
identifiers.push(`<QueueIdentifier Count="${total}" ${keyAttribute(
|
|
464
|
-
if (ALPHA_KEY.test(
|
|
1153
|
+
identifiers.push(`<QueueIdentifier Count="${total}" ${keyAttribute(queueKey2)}/>`);
|
|
1154
|
+
if (ALPHA_KEY.test(queueKey2)) messages += total;
|
|
465
1155
|
else pnrsTotal += total;
|
|
466
1156
|
}
|
|
467
|
-
return
|
|
1157
|
+
return countRS(`${info(identifiers)}${totalsXml(messages, pnrsTotal)}`);
|
|
468
1158
|
};
|
|
469
1159
|
const access = (session, rq) => {
|
|
470
1160
|
const navigation = attr(rq.Navigation, "Action");
|
|
@@ -474,15 +1164,20 @@ function createQueueFake(options = {}) {
|
|
|
474
1164
|
if (key === void 0) return accessError("sabre-client/testing: QueueIdentifier sin Number ni Name");
|
|
475
1165
|
const isList = node2(identifier?.List) !== void 0;
|
|
476
1166
|
calls.push({ command: isList ? "list" : "access", queue: refOfKey(key) });
|
|
477
|
-
if (nodes(rq.Selection).
|
|
1167
|
+
if (isList && nodes(rq.Selection).some((selection) => !LIST_SELECTION_FUNCTIONS.includes((attr(selection, "Function") ?? "").toUpperCase()))) {
|
|
1168
|
+
return accessError("VALID SELECTION CODE FOR QUEUE LIST ARE QD OR AK");
|
|
1169
|
+
}
|
|
1170
|
+
const { matches, error } = selectionOf(rq, QUEUE_FAKE_SELECTION_FUNCTIONS);
|
|
1171
|
+
if (error !== void 0) return accessError(error);
|
|
478
1172
|
const entries = queue(key);
|
|
479
1173
|
if (entries.length === 0) return accessError(QUEUE_EMPTY);
|
|
480
1174
|
if (isList) {
|
|
481
1175
|
const passenger = attr(identifier?.List, "PrimaryPassenger") === "true";
|
|
482
|
-
const
|
|
1176
|
+
const listed = matches === void 0 ? entries : entries.filter(matches);
|
|
1177
|
+
const lines = listed.map((entry, index) => lineXml(entry, { number: index + 1, passenger })).join("");
|
|
483
1178
|
return accessRS(`${results("Complete", success)}${lines}`);
|
|
484
1179
|
}
|
|
485
|
-
session.inside = { key, index: 0 };
|
|
1180
|
+
session.inside = { key, index: 0, ...matches === void 0 ? {} : { matches } };
|
|
486
1181
|
return step(session);
|
|
487
1182
|
};
|
|
488
1183
|
const navigate = (session, rq, action) => {
|
|
@@ -532,10 +1227,12 @@ function createQueueFake(options = {}) {
|
|
|
532
1227
|
const add = (locator, target) => {
|
|
533
1228
|
const entries = queue(target.key);
|
|
534
1229
|
if (entries.some((entry) => entry.locator === locator)) return;
|
|
1230
|
+
const code = Number(target.instructionCode);
|
|
535
1231
|
entries.push({
|
|
536
1232
|
locator,
|
|
537
1233
|
placedAt: longDate(at),
|
|
538
|
-
placedBy: target.pcc === void 0 ? void 0 : { pseudoCityCode: target.pcc }
|
|
1234
|
+
placedBy: target.pcc === void 0 ? void 0 : { pseudoCityCode: target.pcc },
|
|
1235
|
+
...Number.isInteger(code) ? { instructionCode: code } : {}
|
|
539
1236
|
});
|
|
540
1237
|
};
|
|
541
1238
|
if (moving) {
|
|
@@ -543,6 +1240,7 @@ function createQueueFake(options = {}) {
|
|
|
543
1240
|
if (current === void 0) return placeError("sabre-client/testing: no hay PNR en pantalla");
|
|
544
1241
|
queue(inside.key).splice(inside.index, 1);
|
|
545
1242
|
for (const target of targets) add(current.locator, target);
|
|
1243
|
+
advance(session);
|
|
546
1244
|
const next = queue(inside.key)[inside.index];
|
|
547
1245
|
if (next === void 0) {
|
|
548
1246
|
session.inside = void 0;
|
|
@@ -565,22 +1263,37 @@ function createQueueFake(options = {}) {
|
|
|
565
1263
|
};
|
|
566
1264
|
const analyze = (session, rq) => {
|
|
567
1265
|
calls.push({ command: "activity" });
|
|
568
|
-
const
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
const identifiers = [];
|
|
572
|
-
for (const key of /* @__PURE__ */ new Set([...queues.keys(), ...activity.keys()])) {
|
|
1266
|
+
const identifier = node2(node2(rq.QueueInfo)?.QueueIdentifier);
|
|
1267
|
+
const pcc = attr(identifier, "PseudoCityCode") ?? "";
|
|
1268
|
+
const countersOf = (key) => {
|
|
573
1269
|
const seeded = activity.get(key) ?? {};
|
|
574
1270
|
const counters = { NumMessages: waiting(session, key, queues.get(key) ?? []) };
|
|
575
1271
|
for (const [name, element2] of ACTIVITY_COUNTERS) counters[element2] = seeded[name] ?? 0;
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
1272
|
+
return counters;
|
|
1273
|
+
};
|
|
1274
|
+
const countersXml = (counters) => Object.entries(counters).map(([element2, value]) => `<${element2} Count="${value}"/>`).join("");
|
|
1275
|
+
let keys;
|
|
1276
|
+
let withTotals = true;
|
|
1277
|
+
const range = node2(identifier?.Range);
|
|
1278
|
+
const requested = requestedKey(identifier);
|
|
1279
|
+
if (range !== void 0) {
|
|
1280
|
+
keys = [];
|
|
1281
|
+
for (let number = Number(attr(range, "Start")); number <= Number(attr(range, "End")); number++) keys.push(String(number));
|
|
1282
|
+
} else if (requested !== void 0) {
|
|
1283
|
+
keys = [requested];
|
|
1284
|
+
withTotals = false;
|
|
1285
|
+
} else {
|
|
1286
|
+
keys = [.../* @__PURE__ */ new Set([...queues.keys(), ...activity.keys()])].filter((key) => Object.values(countersOf(key)).some((value) => value !== 0));
|
|
581
1287
|
}
|
|
582
|
-
const
|
|
583
|
-
|
|
1288
|
+
const totals = { NumMessages: 0 };
|
|
1289
|
+
for (const [, element2] of ACTIVITY_COUNTERS) totals[element2] = 0;
|
|
1290
|
+
const identifiers = keys.map((key) => {
|
|
1291
|
+
const counters = countersOf(key);
|
|
1292
|
+
for (const [element2, value] of Object.entries(counters)) totals[element2] = (totals[element2] ?? 0) + value;
|
|
1293
|
+
return `<QueueIdentifier ${keyAttribute(key)}>${countersXml(counters)}</QueueIdentifier>`;
|
|
1294
|
+
});
|
|
1295
|
+
const totalsXml = withTotals ? `<Totals>${countersXml(totals)}</Totals>` : "";
|
|
1296
|
+
return `<QueueAnalysisRS xmlns="${SWS_NAMESPACE}" xmlns:stl="${STL_NAMESPACE}" Version="2.0.0">${results("Complete", success)}<QueueInfo DateTime="${shortDate(now())}" PseudoCityCode="${escape(pcc)}">${identifiers.join("")}</QueueInfo>${totalsXml}</QueueAnalysisRS>`;
|
|
584
1297
|
};
|
|
585
1298
|
const history = (session, rq) => {
|
|
586
1299
|
const locator = attr(rq.UniqueID, "ID") ?? "";
|
|
@@ -596,6 +1309,40 @@ function createQueueFake(options = {}) {
|
|
|
596
1309
|
const created = { rph: 1, createdAt: longDate(now()), agent: "AAA", pcc: "XXXX", receivedFrom: "SABRE-CLIENT/TESTING" };
|
|
597
1310
|
return historyRS(`${results("Complete", success)}${historicalInfo(created)}`);
|
|
598
1311
|
};
|
|
1312
|
+
const dailySalesSummary = (rq, sessionPcc) => {
|
|
1313
|
+
const criteria = node2(rq.SelectionCriteria) ?? {};
|
|
1314
|
+
const requestedPcc = text(criteria.PseudoCityCode);
|
|
1315
|
+
const requestedDate = text(criteria.ReportDate);
|
|
1316
|
+
calls.push({ command: "dailySalesSummary", ...requestedPcc === void 0 ? {} : { pcc: requestedPcc }, ...requestedDate === void 0 ? {} : { date: requestedDate } });
|
|
1317
|
+
const pcc = requestedPcc ?? sessionPcc;
|
|
1318
|
+
const date = requestedDate ?? dailySalesDay.format(now());
|
|
1319
|
+
const day = dailySales.get(`${pcc} ${date}`) ?? { transactions: [] };
|
|
1320
|
+
const namespaces = 'xmlns:asr="http://www.sabre.com/ns/Ticketing/AsrServices/1.0" xmlns:stl="http://services.sabre.com/STL/v01"';
|
|
1321
|
+
const resultsXml = (inner) => `<asr:Header><stl:Results>${inner}</stl:Results></asr:Header>`;
|
|
1322
|
+
const errorXml = (status, code, message) => `<stl:Error type="BusinessLogic" status="${escape(status)}"><stl:System>SABRE-CLIENT-TESTING</stl:System><stl:ErrorCode>${escape(code)}</stl:ErrorCode><stl:Message>${escape(message)}</stl:Message></stl:Error>`;
|
|
1323
|
+
if (day.errorCode !== void 0) {
|
|
1324
|
+
return `<asr:DailySalesSummaryRS ${namespaces} version="1.2.2">${resultsXml(errorXml("NotProcessed", day.errorCode, `sabre-client/testing: error ${day.errorCode} forzado`))}</asr:DailySalesSummaryRS>`;
|
|
1325
|
+
}
|
|
1326
|
+
const wanted = text(criteria.DocumentType);
|
|
1327
|
+
const transactions = day.transactions.filter(
|
|
1328
|
+
(t) => wanted === void 0 || t.documentType === wanted || wanted === "RFND" && t.documentType === "RFD"
|
|
1329
|
+
);
|
|
1330
|
+
const deferred = day.status === "deferred" ? { reportId: "FAKE-REPORT", queryStillRunning: true, ...day.deferred } : void 0;
|
|
1331
|
+
const rootAttrs = attrs([
|
|
1332
|
+
["isQueryStillRunning", String(deferred?.queryStillRunning ?? false)],
|
|
1333
|
+
["reportId", deferred?.reportId],
|
|
1334
|
+
["chunkNumber", deferred?.chunkNumber],
|
|
1335
|
+
["maxChunks", deferred?.maxChunks]
|
|
1336
|
+
]);
|
|
1337
|
+
const incomplete = day.status === "incomplete" ? errorXml("Incomplete", "0", `The following are the failed transactions : [${(day.failedDocuments ?? []).join(", ")}]`) : "";
|
|
1338
|
+
return [
|
|
1339
|
+
`<asr:DailySalesSummaryRS ${namespaces} version="1.2.2"${rootAttrs}>`,
|
|
1340
|
+
resultsXml(`<stl:Success><stl:System>SABRE-CLIENT-TESTING</stl:System></stl:Success>${incomplete}`),
|
|
1341
|
+
`<asr:DSHeader><asr:PseudoCityCode>${escape(pcc)}</asr:PseudoCityCode>${day.agencyName === void 0 ? "" : `<asr:AgencyName>${escape(day.agencyName)}</asr:AgencyName>`}</asr:DSHeader>`,
|
|
1342
|
+
`<asr:DailySalesReport><asr:ReportDate>${escape(date)}</asr:ReportDate>${transactions.map(transactionXml).join("")}</asr:DailySalesReport>`,
|
|
1343
|
+
"</asr:DailySalesSummaryRS>"
|
|
1344
|
+
].join("");
|
|
1345
|
+
};
|
|
599
1346
|
const handle = (raw) => {
|
|
600
1347
|
let document;
|
|
601
1348
|
try {
|
|
@@ -655,6 +1402,8 @@ function createQueueFake(options = {}) {
|
|
|
655
1402
|
return reply("QueueAnalysisLLSRS", session, token, analyze(session, rq));
|
|
656
1403
|
case "TravelItineraryHistoryLLSRQ":
|
|
657
1404
|
return reply("TravelItineraryHistoryLLSRS", session, token, history(session, rq));
|
|
1405
|
+
case "TKT_TravelAgencyReportsRQ":
|
|
1406
|
+
return reply("TKT_TravelAgencyReportsRS", session, token, dailySalesSummary(rq, text(messageHeader?.CPAId) ?? ""));
|
|
658
1407
|
default:
|
|
659
1408
|
return {
|
|
660
1409
|
status: 500,
|
|
@@ -667,10 +1416,20 @@ function createQueueFake(options = {}) {
|
|
|
667
1416
|
};
|
|
668
1417
|
}
|
|
669
1418
|
};
|
|
1419
|
+
const rest = createBookingHandler(
|
|
1420
|
+
{ ...options.bookings === void 0 ? {} : { bookings: options.bookings }, ...options.ticketing === void 0 ? {} : { ticketing: options.ticketing } },
|
|
1421
|
+
now,
|
|
1422
|
+
(command, locator) => calls.push({ command, ...locator === void 0 ? {} : { locator } })
|
|
1423
|
+
);
|
|
670
1424
|
const fakeFetch = async (input, init) => {
|
|
671
1425
|
const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
|
|
672
1426
|
signal?.throwIfAborted();
|
|
673
1427
|
const raw = typeof init?.body === "string" ? init.body : input instanceof Request ? await input.text() : String(init?.body ?? "");
|
|
1428
|
+
const url = input instanceof Request ? input.url : String(input);
|
|
1429
|
+
const restReply = rest(url, raw);
|
|
1430
|
+
if (restReply === "transport") throw new TypeError("fetch failed (sabre-client/testing: fallo de red forzado)");
|
|
1431
|
+
if (restReply === "abort") throw new DOMException("The operation was aborted. (sabre-client/testing: abort forzado)", "AbortError");
|
|
1432
|
+
if (restReply !== void 0) return restReply;
|
|
674
1433
|
const { status, body } = handle(raw);
|
|
675
1434
|
return new Response(body, { status, headers: { "Content-Type": "text/xml;charset=UTF-8" } });
|
|
676
1435
|
};
|
|
@@ -3267,11 +4026,11 @@ var queue_production_walk_default = {
|
|
|
3267
4026
|
};
|
|
3268
4027
|
|
|
3269
4028
|
// src/testing/fixtures.ts
|
|
3270
|
-
function fixture(
|
|
4029
|
+
function fixture(json2) {
|
|
3271
4030
|
return Object.freeze({
|
|
3272
|
-
recordedAt:
|
|
4031
|
+
recordedAt: json2.recordedAt,
|
|
3273
4032
|
responses: Object.freeze(
|
|
3274
|
-
|
|
4033
|
+
json2.responses.map(({ operation, status, body }) => Object.freeze({ operation, status, body }))
|
|
3275
4034
|
)
|
|
3276
4035
|
});
|
|
3277
4036
|
}
|
|
@@ -3283,6 +4042,7 @@ var queueFixtures = Object.freeze({
|
|
|
3283
4042
|
placeWithoutInstructionCodeInside: fixture(queue_production_place_sin_pic_adentro_default)
|
|
3284
4043
|
});
|
|
3285
4044
|
export {
|
|
4045
|
+
QUEUE_FAKE_SELECTION_FUNCTIONS,
|
|
3286
4046
|
createAnonymizer,
|
|
3287
4047
|
createQueueFake,
|
|
3288
4048
|
isSafeHostLine,
|