shipflow 0.1.0 → 0.2.1
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 +139 -33
- package/dist/carriers/aramex/adapter.d.ts +82 -0
- package/dist/carriers/aramex/adapter.d.ts.map +1 -0
- package/dist/carriers/aramex/index.d.ts +9 -0
- package/dist/carriers/aramex/index.d.ts.map +1 -0
- package/dist/carriers/aramex/index.js +694 -0
- package/dist/carriers/aramex/index.js.map +12 -0
- package/dist/carriers/aramex/mappers.d.ts +76 -0
- package/dist/carriers/aramex/mappers.d.ts.map +1 -0
- package/dist/carriers/aramex/services.d.ts +74 -0
- package/dist/carriers/aramex/services.d.ts.map +1 -0
- package/dist/carriers/aramex/types.d.ts +302 -0
- package/dist/carriers/aramex/types.d.ts.map +1 -0
- package/dist/carriers/aymakan/adapter.d.ts +8 -2
- package/dist/carriers/aymakan/adapter.d.ts.map +1 -1
- package/dist/carriers/aymakan/index.js +138 -61
- package/dist/carriers/aymakan/index.js.map +4 -4
- package/dist/carriers/aymakan/mappers.d.ts +5 -0
- package/dist/carriers/aymakan/mappers.d.ts.map +1 -1
- package/dist/carriers/aymakan/types.d.ts +4 -6
- package/dist/carriers/aymakan/types.d.ts.map +1 -1
- package/dist/carriers/base.d.ts +2 -2
- package/dist/carriers/base.d.ts.map +1 -1
- package/dist/carriers/smsaexpress/adapter.d.ts +1 -1
- package/dist/carriers/smsaexpress/adapter.d.ts.map +1 -1
- package/dist/carriers/smsaexpress/index.js +32 -22
- package/dist/carriers/smsaexpress/index.js.map +4 -4
- package/dist/carriers/smsaexpress/mappers.d.ts.map +1 -1
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/schemas.d.ts +3 -3
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/{index-x8sk1kw9.js → index-qjtxhwzv.js} +5 -3
- package/dist/{index-x8sk1kw9.js.map → index-qjtxhwzv.js.map} +5 -5
- package/dist/index.js +1 -1
- package/package.json +8 -3
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
import {
|
|
2
|
+
APIError,
|
|
3
|
+
BaseCarrierAdapter,
|
|
4
|
+
HttpClient,
|
|
5
|
+
UnsupportedOperationError,
|
|
6
|
+
ValidationError,
|
|
7
|
+
validateCreateShipmentInput,
|
|
8
|
+
validatePickupRequest
|
|
9
|
+
} from "../../index-qjtxhwzv.js";
|
|
10
|
+
|
|
11
|
+
// src/carriers/aramex/services.ts
|
|
12
|
+
var AramexProductGroup = {
|
|
13
|
+
EXPRESS: "EXP",
|
|
14
|
+
DOMESTIC: "DOM"
|
|
15
|
+
};
|
|
16
|
+
var AramexProductType = {
|
|
17
|
+
DOMESTIC: "OND",
|
|
18
|
+
PRIORITY_DOCUMENT_EXPRESS: "PDX",
|
|
19
|
+
PRIORITY_PARCEL_EXPRESS: "PPX",
|
|
20
|
+
PRIORITY_LETTER_EXPRESS: "PLX",
|
|
21
|
+
DEFERRED_DOCUMENT_EXPRESS: "DDX",
|
|
22
|
+
DEFERRED_PARCEL_EXPRESS: "DPX",
|
|
23
|
+
GROUND_DOCUMENT_EXPRESS: "GDX",
|
|
24
|
+
GROUND_PARCEL_EXPRESS: "GPX",
|
|
25
|
+
ECONOMY_PARCEL_EXPRESS: "EPX",
|
|
26
|
+
RETURN: "RTN"
|
|
27
|
+
};
|
|
28
|
+
var AramexPaymentType = {
|
|
29
|
+
PREPAID: "P",
|
|
30
|
+
COLLECT: "C",
|
|
31
|
+
THIRD_PARTY: "3"
|
|
32
|
+
};
|
|
33
|
+
var AramexService = {
|
|
34
|
+
COD: "CODS",
|
|
35
|
+
INSURANCE: "INSR"
|
|
36
|
+
};
|
|
37
|
+
var AramexStatusCodes = {
|
|
38
|
+
SH001: "created",
|
|
39
|
+
SH002: "picked_up",
|
|
40
|
+
SH004: "at_warehouse",
|
|
41
|
+
SH005: "out_for_delivery",
|
|
42
|
+
SH014: "delivered",
|
|
43
|
+
SH060: "in_transit",
|
|
44
|
+
SH074: "exception",
|
|
45
|
+
SH159: "exception",
|
|
46
|
+
SH212: "returned",
|
|
47
|
+
SH235: "delivered"
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/carriers/aramex/mappers.ts
|
|
51
|
+
function buildClientInfo(credentials, opts) {
|
|
52
|
+
return {
|
|
53
|
+
UserName: credentials.userName,
|
|
54
|
+
Password: credentials.password,
|
|
55
|
+
Version: opts?.version ?? "v1.0",
|
|
56
|
+
AccountNumber: credentials.accountNumber,
|
|
57
|
+
AccountPin: credentials.accountPin,
|
|
58
|
+
AccountEntity: credentials.accountEntity,
|
|
59
|
+
AccountCountryCode: credentials.accountCountryCode,
|
|
60
|
+
Source: opts?.source ?? 24
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function mapAramexStatus(updateCode) {
|
|
64
|
+
const mapped = AramexStatusCodes[updateCode];
|
|
65
|
+
return mapped;
|
|
66
|
+
}
|
|
67
|
+
function statusFromDescription(description) {
|
|
68
|
+
if (!description)
|
|
69
|
+
return;
|
|
70
|
+
const d = description.toLowerCase();
|
|
71
|
+
if (/out for delivery|on delivery|with delivery courier/.test(d))
|
|
72
|
+
return "out_for_delivery";
|
|
73
|
+
if (/fail|unable|undeliver|exception|held|on hold|problem|refused|damaged/.test(d))
|
|
74
|
+
return "exception";
|
|
75
|
+
if (/deliver/.test(d))
|
|
76
|
+
return "delivered";
|
|
77
|
+
if (/return/.test(d))
|
|
78
|
+
return "returned";
|
|
79
|
+
if (/cancel/.test(d))
|
|
80
|
+
return "cancelled";
|
|
81
|
+
if (/picked up|collected|pickup/.test(d))
|
|
82
|
+
return "picked_up";
|
|
83
|
+
if (/transit|departed|forwarded|en route/.test(d))
|
|
84
|
+
return "in_transit";
|
|
85
|
+
if (/received at|arrived|facility|warehouse|sorting|hub/.test(d))
|
|
86
|
+
return "at_warehouse";
|
|
87
|
+
if (/created|information received|booked/.test(d))
|
|
88
|
+
return "created";
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
function parseAramexDate(value) {
|
|
92
|
+
if (!value)
|
|
93
|
+
return new Date(Number.NaN);
|
|
94
|
+
const wcf = /\/Date\((-?\d+)(?:[+-]\d{4})?\)\//.exec(value);
|
|
95
|
+
if (wcf?.[1])
|
|
96
|
+
return new Date(Number.parseInt(wcf[1], 10));
|
|
97
|
+
return new Date(value);
|
|
98
|
+
}
|
|
99
|
+
function toWcfDate(date) {
|
|
100
|
+
return `/Date(${date.getTime()})/`;
|
|
101
|
+
}
|
|
102
|
+
function round(value) {
|
|
103
|
+
return Math.round(value * 1000) / 1000;
|
|
104
|
+
}
|
|
105
|
+
var VALID_PRODUCT_TYPES = new Set(Object.values(AramexProductType));
|
|
106
|
+
function getMeta(input, key) {
|
|
107
|
+
const value = input.options?.metadata?.[key];
|
|
108
|
+
return typeof value === "string" ? value : undefined;
|
|
109
|
+
}
|
|
110
|
+
function resolveProductGroupAndType(input) {
|
|
111
|
+
const sameCountry = input.shipper.countryCode.trim().toUpperCase() === input.consignee.countryCode.trim().toUpperCase();
|
|
112
|
+
const metaGroup = getMeta(input, "productGroup")?.toUpperCase();
|
|
113
|
+
const productGroup = metaGroup === "EXP" || metaGroup === "DOM" ? metaGroup : sameCountry ? "DOM" : "EXP";
|
|
114
|
+
const candidate = getMeta(input, "productType") ?? input.serviceType;
|
|
115
|
+
const productType = candidate && VALID_PRODUCT_TYPES.has(candidate) ? candidate : productGroup === "DOM" ? AramexProductType.DOMESTIC : AramexProductType.ECONOMY_PARCEL_EXPRESS;
|
|
116
|
+
return { productGroup, productType };
|
|
117
|
+
}
|
|
118
|
+
function resolvePaymentType(input) {
|
|
119
|
+
const meta = getMeta(input, "paymentType");
|
|
120
|
+
if (meta === "P" || meta === "C" || meta === "3")
|
|
121
|
+
return meta;
|
|
122
|
+
return "P";
|
|
123
|
+
}
|
|
124
|
+
function aggregateWeight(input) {
|
|
125
|
+
const allLb = input.parcels.every((p) => p.weight.unit === "lb");
|
|
126
|
+
if (allLb) {
|
|
127
|
+
const value2 = input.parcels.reduce((s, p) => s + p.weight.value, 0);
|
|
128
|
+
return { Value: round(value2), Unit: "Lb" };
|
|
129
|
+
}
|
|
130
|
+
const value = input.parcels.reduce((s, p) => s + (p.weight.unit === "lb" ? p.weight.value * 0.453592 : p.weight.value), 0);
|
|
131
|
+
return { Value: round(value), Unit: "Kg" };
|
|
132
|
+
}
|
|
133
|
+
function mapDimensions(dims) {
|
|
134
|
+
if (!dims)
|
|
135
|
+
return null;
|
|
136
|
+
const factor = dims.unit === "in" ? 2.54 : 1;
|
|
137
|
+
return {
|
|
138
|
+
Length: round(dims.length * factor),
|
|
139
|
+
Width: round(dims.width * factor),
|
|
140
|
+
Height: round(dims.height * factor),
|
|
141
|
+
Unit: "CM"
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function buildPartyAddress(fields) {
|
|
145
|
+
return {
|
|
146
|
+
Line1: fields.line1,
|
|
147
|
+
Line2: fields.line2 ?? "",
|
|
148
|
+
Line3: fields.line3 ?? "",
|
|
149
|
+
City: fields.city,
|
|
150
|
+
StateOrProvinceCode: fields.state,
|
|
151
|
+
PostCode: fields.postCode ?? "",
|
|
152
|
+
CountryCode: fields.countryCode,
|
|
153
|
+
Longitude: fields.coordinates?.longitude,
|
|
154
|
+
Latitude: fields.coordinates?.latitude
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function mapAddress(addr) {
|
|
158
|
+
return buildPartyAddress({
|
|
159
|
+
line1: addr.line1,
|
|
160
|
+
line2: addr.line2,
|
|
161
|
+
line3: addr.neighbourhood,
|
|
162
|
+
city: addr.city,
|
|
163
|
+
state: addr.state,
|
|
164
|
+
postCode: addr.postalCode,
|
|
165
|
+
countryCode: addr.countryCode,
|
|
166
|
+
coordinates: addr.coordinates
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function buildContact(opts) {
|
|
170
|
+
return {
|
|
171
|
+
Department: "",
|
|
172
|
+
PersonName: opts.personName,
|
|
173
|
+
Title: "",
|
|
174
|
+
CompanyName: opts.companyName,
|
|
175
|
+
PhoneNumber1: opts.phone,
|
|
176
|
+
PhoneNumber1Ext: "",
|
|
177
|
+
PhoneNumber2: "",
|
|
178
|
+
CellPhone: opts.phone,
|
|
179
|
+
EmailAddress: opts.email ?? "",
|
|
180
|
+
Type: ""
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function mapContact(addr, fallbackCompany) {
|
|
184
|
+
return buildContact({
|
|
185
|
+
personName: addr.name,
|
|
186
|
+
companyName: addr.company ?? fallbackCompany ?? addr.name,
|
|
187
|
+
phone: addr.phone,
|
|
188
|
+
email: addr.email
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function mapParty(addr, accountNumber, fallbackCompany) {
|
|
192
|
+
return {
|
|
193
|
+
AccountNumber: accountNumber,
|
|
194
|
+
PartyAddress: mapAddress(addr),
|
|
195
|
+
Contact: mapContact(addr, fallbackCompany)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function mapShipmentDetails(input, productGroup, productType, paymentType) {
|
|
199
|
+
const numberOfPieces = input.parcels.reduce((s, p) => s + p.pieces, 0) || 1;
|
|
200
|
+
const description = input.parcels.map((p) => p.description).filter(Boolean).join(", ") || "Goods";
|
|
201
|
+
const services = [];
|
|
202
|
+
let cashOnDelivery = null;
|
|
203
|
+
if (input.cod?.enabled) {
|
|
204
|
+
cashOnDelivery = {
|
|
205
|
+
Value: input.cod.amount,
|
|
206
|
+
CurrencyCode: input.cod.currency
|
|
207
|
+
};
|
|
208
|
+
services.push(AramexService.COD);
|
|
209
|
+
}
|
|
210
|
+
const customsValue = input.declaredValue ? {
|
|
211
|
+
Value: input.declaredValue.amount,
|
|
212
|
+
CurrencyCode: input.declaredValue.currency
|
|
213
|
+
} : null;
|
|
214
|
+
const insuranceAmount = input.options?.isInsured && customsValue ? customsValue : null;
|
|
215
|
+
if (insuranceAmount)
|
|
216
|
+
services.push(AramexService.INSURANCE);
|
|
217
|
+
return {
|
|
218
|
+
Dimensions: mapDimensions(input.parcels[0]?.dimensions),
|
|
219
|
+
ActualWeight: aggregateWeight(input),
|
|
220
|
+
ChargeableWeight: null,
|
|
221
|
+
DescriptionOfGoods: description,
|
|
222
|
+
GoodsOriginCountry: input.shipper.countryCode,
|
|
223
|
+
NumberOfPieces: numberOfPieces,
|
|
224
|
+
ProductGroup: productGroup,
|
|
225
|
+
ProductType: productType,
|
|
226
|
+
PaymentType: paymentType,
|
|
227
|
+
PaymentOptions: "",
|
|
228
|
+
CustomsValueAmount: customsValue,
|
|
229
|
+
CashOnDeliveryAmount: cashOnDelivery,
|
|
230
|
+
InsuranceAmount: insuranceAmount,
|
|
231
|
+
Services: services.join(",") || undefined
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function mapCreateShipmentRequest(input, ctx) {
|
|
235
|
+
const { productGroup, productType } = resolveProductGroupAndType(input);
|
|
236
|
+
const paymentType = resolvePaymentType(input);
|
|
237
|
+
const now = new Date;
|
|
238
|
+
const due = new Date(now.getTime() + 24 * 60 * 60 * 1000);
|
|
239
|
+
return {
|
|
240
|
+
Reference1: input.reference,
|
|
241
|
+
Reference2: input.options?.customerTracking,
|
|
242
|
+
Shipper: mapParty(input.shipper, ctx.accountNumber, ctx.companyName),
|
|
243
|
+
Consignee: mapParty(input.consignee),
|
|
244
|
+
ThirdParty: null,
|
|
245
|
+
ShippingDateTime: toWcfDate(now),
|
|
246
|
+
DueDate: toWcfDate(due),
|
|
247
|
+
Comments: getMeta(input, "comments"),
|
|
248
|
+
PickupLocation: getMeta(input, "pickupLocation"),
|
|
249
|
+
Details: mapShipmentDetails(input, productGroup, productType, paymentType),
|
|
250
|
+
Attachments: null,
|
|
251
|
+
ForeignHAWB: null,
|
|
252
|
+
TransportType: 0,
|
|
253
|
+
Number: null
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function mapCalculateRateRequest(input, preferredCurrency) {
|
|
257
|
+
const { productGroup, productType } = resolveProductGroupAndType(input);
|
|
258
|
+
const paymentType = resolvePaymentType(input);
|
|
259
|
+
return {
|
|
260
|
+
OriginAddress: mapAddress(input.shipper),
|
|
261
|
+
DestinationAddress: mapAddress(input.consignee),
|
|
262
|
+
ShipmentDetails: mapShipmentDetails(input, productGroup, productType, paymentType),
|
|
263
|
+
PreferredCurrencyCode: preferredCurrency ?? input.cod?.currency ?? input.declaredValue?.currency
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function mapPickupRequest(input, ctx) {
|
|
267
|
+
const pickupDate = new Date(`${input.date}T00:00:00`);
|
|
268
|
+
const ready = new Date(`${input.date}T09:00:00`);
|
|
269
|
+
const closing = new Date(`${input.date}T17:00:00`);
|
|
270
|
+
return {
|
|
271
|
+
Reference1: input.trackingNumbers?.[0],
|
|
272
|
+
PickupAddress: buildPartyAddress({
|
|
273
|
+
line1: input.address,
|
|
274
|
+
city: input.city,
|
|
275
|
+
countryCode: ctx.countryCode
|
|
276
|
+
}),
|
|
277
|
+
PickupContact: buildContact({
|
|
278
|
+
personName: input.contactName,
|
|
279
|
+
companyName: ctx.companyName ?? input.contactName,
|
|
280
|
+
phone: input.contactPhone
|
|
281
|
+
}),
|
|
282
|
+
PickupLocation: "Reception",
|
|
283
|
+
PickupDate: toWcfDate(pickupDate),
|
|
284
|
+
ReadyTime: toWcfDate(ready),
|
|
285
|
+
LastPickupTime: toWcfDate(closing),
|
|
286
|
+
ClosingTime: toWcfDate(closing),
|
|
287
|
+
Comments: input.timeSlot,
|
|
288
|
+
Status: "Ready",
|
|
289
|
+
PickupItems: {
|
|
290
|
+
PickupItemDetail: [
|
|
291
|
+
{
|
|
292
|
+
ProductGroup: "DOM",
|
|
293
|
+
ProductType: AramexProductType.DOMESTIC,
|
|
294
|
+
Payment: "P",
|
|
295
|
+
NumberOfShipments: input.shipmentCount,
|
|
296
|
+
NumberOfPieces: input.shipmentCount,
|
|
297
|
+
ShipmentWeight: { Value: 1, Unit: "Kg" }
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function mapShipmentResponse(processed, input) {
|
|
304
|
+
const trackingNumber = processed.ID || processed.ShipmentNumber || "";
|
|
305
|
+
const labelUrl = processed.ShipmentLabel?.LabelURL || undefined;
|
|
306
|
+
return {
|
|
307
|
+
carrier: "aramex",
|
|
308
|
+
trackingNumber,
|
|
309
|
+
customerTracking: input?.options?.customerTracking,
|
|
310
|
+
reference: input?.reference,
|
|
311
|
+
status: "created",
|
|
312
|
+
statusLabel: "Created",
|
|
313
|
+
labelUrl,
|
|
314
|
+
pdfLabelUrl: labelUrl,
|
|
315
|
+
codAmount: input?.cod?.enabled ? input.cod.amount : undefined,
|
|
316
|
+
declaredValue: input?.declaredValue?.amount,
|
|
317
|
+
currency: input?.cod?.currency ?? input?.declaredValue?.currency ?? "SAR",
|
|
318
|
+
createdAt: new Date,
|
|
319
|
+
raw: processed
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function mapTrackingEvent(result) {
|
|
323
|
+
return {
|
|
324
|
+
timestamp: parseAramexDate(result.UpdateDateTime),
|
|
325
|
+
statusCode: result.UpdateCode,
|
|
326
|
+
status: mapAramexStatus(result.UpdateCode) ?? statusFromDescription(result.UpdateDescription) ?? "unknown",
|
|
327
|
+
description: result.UpdateDescription,
|
|
328
|
+
location: result.UpdateLocation || undefined
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function mapTrackingResult(waybill, results) {
|
|
332
|
+
const events = results.map(mapTrackingEvent).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
333
|
+
const latest = events[0];
|
|
334
|
+
const delivered = events.find((e) => e.status === "delivered");
|
|
335
|
+
const weightStr = [...results].reverse().find((r) => r.GrossWeight)?.GrossWeight;
|
|
336
|
+
const weight = weightStr ? Number.parseFloat(weightStr) : undefined;
|
|
337
|
+
return {
|
|
338
|
+
trackingNumber: waybill,
|
|
339
|
+
carrier: "aramex",
|
|
340
|
+
status: latest?.status ?? "unknown",
|
|
341
|
+
statusLabel: latest?.description ?? "Unknown",
|
|
342
|
+
events,
|
|
343
|
+
deliveryDate: delivered?.timestamp,
|
|
344
|
+
weight: weight != null && !Number.isNaN(weight) ? weight : undefined,
|
|
345
|
+
raw: results
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function normalizeTrackingResults(raw) {
|
|
349
|
+
if (!raw)
|
|
350
|
+
return {};
|
|
351
|
+
if (Array.isArray(raw)) {
|
|
352
|
+
const map = {};
|
|
353
|
+
for (const kv of raw) {
|
|
354
|
+
if (kv && typeof kv === "object" && "Key" in kv) {
|
|
355
|
+
map[kv.Key] = kv.Value ?? [];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return map;
|
|
359
|
+
}
|
|
360
|
+
return raw;
|
|
361
|
+
}
|
|
362
|
+
function mapNonExistingWaybill(waybill) {
|
|
363
|
+
return {
|
|
364
|
+
trackingNumber: waybill,
|
|
365
|
+
carrier: "aramex",
|
|
366
|
+
status: "unknown",
|
|
367
|
+
statusLabel: "Waybill not found",
|
|
368
|
+
events: [],
|
|
369
|
+
raw: { nonExisting: true, waybill }
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function mapRate(response, input) {
|
|
373
|
+
const { productType } = resolveProductGroupAndType(input);
|
|
374
|
+
return {
|
|
375
|
+
carrier: "aramex",
|
|
376
|
+
serviceType: productType,
|
|
377
|
+
serviceName: productType,
|
|
378
|
+
amount: response.TotalAmount?.Value ?? 0,
|
|
379
|
+
currency: response.TotalAmount?.CurrencyCode ?? input.cod?.currency ?? input.declaredValue?.currency ?? "SAR",
|
|
380
|
+
raw: response
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function mapPickupResponse(processed, input) {
|
|
384
|
+
return {
|
|
385
|
+
id: processed.GUID || processed.ID,
|
|
386
|
+
carrier: "aramex",
|
|
387
|
+
status: "pending",
|
|
388
|
+
date: input.date,
|
|
389
|
+
timeSlot: input.timeSlot,
|
|
390
|
+
city: input.city,
|
|
391
|
+
contactName: input.contactName,
|
|
392
|
+
contactPhone: input.contactPhone,
|
|
393
|
+
address: input.address,
|
|
394
|
+
shipmentCount: input.shipmentCount,
|
|
395
|
+
createdAt: new Date,
|
|
396
|
+
raw: processed
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function mapCity(name) {
|
|
400
|
+
return { nameEn: name };
|
|
401
|
+
}
|
|
402
|
+
function mapOffice(office) {
|
|
403
|
+
const addr = office.Address;
|
|
404
|
+
const lines = [addr?.Line1, addr?.Line2, addr?.Line3].filter(Boolean);
|
|
405
|
+
return {
|
|
406
|
+
id: office.EntityCode,
|
|
407
|
+
name: office.EntityName ?? office.EntityCode,
|
|
408
|
+
address: lines.length ? lines.join(", ") : undefined,
|
|
409
|
+
city: addr?.City,
|
|
410
|
+
latitude: addr?.Latitude,
|
|
411
|
+
longitude: addr?.Longitude
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/carriers/aramex/adapter.ts
|
|
416
|
+
var SHIPPING_SANDBOX_URL = "https://ws.dev.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc";
|
|
417
|
+
var SHIPPING_PRODUCTION_URL = "https://ws.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc";
|
|
418
|
+
var DEFAULT_LABEL_INFO = {
|
|
419
|
+
ReportID: 9201,
|
|
420
|
+
ReportType: "URL"
|
|
421
|
+
};
|
|
422
|
+
var EMPTY_TRANSACTION = {
|
|
423
|
+
Reference1: "",
|
|
424
|
+
Reference2: "",
|
|
425
|
+
Reference3: "",
|
|
426
|
+
Reference4: "",
|
|
427
|
+
Reference5: ""
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
class AramexAdapter extends BaseCarrierAdapter {
|
|
431
|
+
name = "aramex";
|
|
432
|
+
supportedCountries = [
|
|
433
|
+
"SA",
|
|
434
|
+
"AE",
|
|
435
|
+
"BH",
|
|
436
|
+
"KW",
|
|
437
|
+
"OM",
|
|
438
|
+
"QA",
|
|
439
|
+
"JO",
|
|
440
|
+
"EG",
|
|
441
|
+
"LB",
|
|
442
|
+
"IQ"
|
|
443
|
+
];
|
|
444
|
+
shippingHttp;
|
|
445
|
+
trackingHttp;
|
|
446
|
+
rateHttp;
|
|
447
|
+
locationHttp;
|
|
448
|
+
constructor(config) {
|
|
449
|
+
super(config);
|
|
450
|
+
const shippingBase = this.getBaseUrl();
|
|
451
|
+
const common = {
|
|
452
|
+
carrier: "aramex"
|
|
453
|
+
};
|
|
454
|
+
this.shippingHttp = new HttpClient({ ...common, baseUrl: shippingBase });
|
|
455
|
+
this.trackingHttp = new HttpClient({
|
|
456
|
+
...common,
|
|
457
|
+
baseUrl: this.serviceBase(shippingBase, "Tracking")
|
|
458
|
+
});
|
|
459
|
+
this.rateHttp = new HttpClient({
|
|
460
|
+
...common,
|
|
461
|
+
baseUrl: this.serviceBase(shippingBase, "RateCalculator")
|
|
462
|
+
});
|
|
463
|
+
this.locationHttp = new HttpClient({
|
|
464
|
+
...common,
|
|
465
|
+
baseUrl: config.locationBaseUrl ?? this.serviceBase(shippingBase, "Location")
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
getBaseUrl() {
|
|
469
|
+
return this.config.mode === "production" ? SHIPPING_PRODUCTION_URL : SHIPPING_SANDBOX_URL;
|
|
470
|
+
}
|
|
471
|
+
serviceBase(shippingBase, service) {
|
|
472
|
+
return shippingBase.replace("/Shipping/", `/${service}/`);
|
|
473
|
+
}
|
|
474
|
+
get aramexConfig() {
|
|
475
|
+
return this.config;
|
|
476
|
+
}
|
|
477
|
+
buildClientInfo() {
|
|
478
|
+
const cfg = this.aramexConfig;
|
|
479
|
+
return buildClientInfo(cfg.credentials, {
|
|
480
|
+
source: cfg.source,
|
|
481
|
+
version: cfg.version
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
static aramexErrorExtractor(json) {
|
|
485
|
+
const obj = json;
|
|
486
|
+
const notifications = obj?.Notifications ?? [];
|
|
487
|
+
const hasError = obj?.HasErrors === true;
|
|
488
|
+
const message = notifications.map((n) => n?.Message).filter(Boolean).join("; ") || (hasError ? "Aramex returned an error" : undefined);
|
|
489
|
+
return {
|
|
490
|
+
hasError,
|
|
491
|
+
message,
|
|
492
|
+
errors: notifications.length ? AramexAdapter.notificationsToErrors(notifications) : undefined
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
static notificationsToErrors(notifications) {
|
|
496
|
+
return {
|
|
497
|
+
_aramex: notifications.map((n) => `${n?.Code ?? ""}: ${n?.Message ?? ""}`.trim())
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
async executeCreateShipment(input) {
|
|
501
|
+
const cfg = this.aramexConfig;
|
|
502
|
+
const shipment = mapCreateShipmentRequest(input, {
|
|
503
|
+
accountNumber: cfg.credentials.accountNumber,
|
|
504
|
+
companyName: cfg.companyName
|
|
505
|
+
});
|
|
506
|
+
const response = await this.shippingHttp.post("/json/CreateShipments", {
|
|
507
|
+
ClientInfo: this.buildClientInfo(),
|
|
508
|
+
Transaction: EMPTY_TRANSACTION,
|
|
509
|
+
LabelInfo: DEFAULT_LABEL_INFO,
|
|
510
|
+
Shipments: [shipment]
|
|
511
|
+
}, { errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
512
|
+
const processed = response.Shipments?.[0];
|
|
513
|
+
if (!processed) {
|
|
514
|
+
throw new APIError("Aramex returned no shipment", {
|
|
515
|
+
carrier: "aramex",
|
|
516
|
+
raw: response
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (processed.HasErrors) {
|
|
520
|
+
const notifications = processed.Notifications ?? [];
|
|
521
|
+
throw new APIError(notifications.map((n) => n.Message).filter(Boolean).join("; ") || "Failed to create shipment", {
|
|
522
|
+
carrier: "aramex",
|
|
523
|
+
errors: AramexAdapter.notificationsToErrors(notifications),
|
|
524
|
+
raw: processed
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return mapShipmentResponse(processed, input);
|
|
528
|
+
}
|
|
529
|
+
async createBulkShipments(inputs) {
|
|
530
|
+
if (inputs.length === 0) {
|
|
531
|
+
throw new ValidationError("At least one shipment is required for bulk create", { raw: { count: 0 } });
|
|
532
|
+
}
|
|
533
|
+
for (const input of inputs)
|
|
534
|
+
validateCreateShipmentInput(input);
|
|
535
|
+
const cfg = this.aramexConfig;
|
|
536
|
+
const shipments = inputs.map((input) => mapCreateShipmentRequest(input, {
|
|
537
|
+
accountNumber: cfg.credentials.accountNumber,
|
|
538
|
+
companyName: cfg.companyName
|
|
539
|
+
}));
|
|
540
|
+
const response = await this.shippingHttp.post("/json/CreateShipments", {
|
|
541
|
+
ClientInfo: this.buildClientInfo(),
|
|
542
|
+
Transaction: EMPTY_TRANSACTION,
|
|
543
|
+
LabelInfo: DEFAULT_LABEL_INFO,
|
|
544
|
+
Shipments: shipments
|
|
545
|
+
}, { errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
546
|
+
const processed = response.Shipments ?? [];
|
|
547
|
+
if (processed.length === 0) {
|
|
548
|
+
throw new APIError("Aramex returned no shipments", {
|
|
549
|
+
carrier: "aramex",
|
|
550
|
+
raw: response
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
const failed = processed.filter((p) => p.HasErrors);
|
|
554
|
+
if (failed.length > 0) {
|
|
555
|
+
const notifications = failed.flatMap((p) => p.Notifications ?? []);
|
|
556
|
+
throw new APIError(notifications.map((n) => n.Message).filter(Boolean).join("; ") || `Failed to create ${failed.length} of ${processed.length} shipments`, {
|
|
557
|
+
carrier: "aramex",
|
|
558
|
+
errors: AramexAdapter.notificationsToErrors(notifications),
|
|
559
|
+
raw: response
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
return processed.map((p, i) => mapShipmentResponse(p, inputs[i]));
|
|
563
|
+
}
|
|
564
|
+
cancelShipment(_trackingNumber) {
|
|
565
|
+
throw new UnsupportedOperationError("aramex", "cancelShipment");
|
|
566
|
+
}
|
|
567
|
+
async getLabel(trackingNumber, _format) {
|
|
568
|
+
const response = await this.shippingHttp.post("/json/PrintLabel", {
|
|
569
|
+
ClientInfo: this.buildClientInfo(),
|
|
570
|
+
Transaction: EMPTY_TRANSACTION,
|
|
571
|
+
ShipmentNumber: trackingNumber,
|
|
572
|
+
LabelInfo: DEFAULT_LABEL_INFO
|
|
573
|
+
}, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
574
|
+
const url = response.ShipmentLabel?.LabelURL;
|
|
575
|
+
if (!url) {
|
|
576
|
+
throw new APIError("Failed to get label", {
|
|
577
|
+
carrier: "aramex",
|
|
578
|
+
raw: response
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
return url;
|
|
582
|
+
}
|
|
583
|
+
async track(trackingNumber) {
|
|
584
|
+
const results = await this.trackMultiple([trackingNumber]);
|
|
585
|
+
const result = results[0];
|
|
586
|
+
if (!result || result.status === "unknown" && result.events.length === 0) {
|
|
587
|
+
throw new APIError("Shipment not found", {
|
|
588
|
+
carrier: "aramex",
|
|
589
|
+
raw: result?.raw ?? { trackingNumber }
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
594
|
+
async trackMultiple(trackingNumbers) {
|
|
595
|
+
const response = await this.trackingHttp.post("/json/TrackShipments", {
|
|
596
|
+
ClientInfo: this.buildClientInfo(),
|
|
597
|
+
Transaction: EMPTY_TRANSACTION,
|
|
598
|
+
Shipments: trackingNumbers,
|
|
599
|
+
GetLastTrackingUpdateOnly: false
|
|
600
|
+
}, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
601
|
+
const map = normalizeTrackingResults(response.TrackingResults);
|
|
602
|
+
const found = Object.entries(map).map(([waybill, results]) => mapTrackingResult(waybill, results));
|
|
603
|
+
const nonExisting = (response.NonExistingWaybills ?? []).map(mapNonExistingWaybill);
|
|
604
|
+
return [...found, ...nonExisting];
|
|
605
|
+
}
|
|
606
|
+
async trackByReference(reference) {
|
|
607
|
+
const result = await this.track(reference);
|
|
608
|
+
return { ...result, reference };
|
|
609
|
+
}
|
|
610
|
+
async getRates(input) {
|
|
611
|
+
const rateRequest = mapCalculateRateRequest(input);
|
|
612
|
+
const response = await this.rateHttp.post("/json/CalculateRate", {
|
|
613
|
+
ClientInfo: this.buildClientInfo(),
|
|
614
|
+
Transaction: EMPTY_TRANSACTION,
|
|
615
|
+
...rateRequest
|
|
616
|
+
}, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
617
|
+
if (response.TotalAmount?.Value == null) {
|
|
618
|
+
throw new APIError("Aramex returned no rate for this shipment", {
|
|
619
|
+
carrier: "aramex",
|
|
620
|
+
raw: response
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return [mapRate(response, input)];
|
|
624
|
+
}
|
|
625
|
+
async createPickup(input) {
|
|
626
|
+
validatePickupRequest(input);
|
|
627
|
+
const cfg = this.aramexConfig;
|
|
628
|
+
const pickup = mapPickupRequest(input, {
|
|
629
|
+
accountNumber: cfg.credentials.accountNumber,
|
|
630
|
+
countryCode: cfg.credentials.accountCountryCode,
|
|
631
|
+
companyName: cfg.companyName
|
|
632
|
+
});
|
|
633
|
+
const response = await this.shippingHttp.post("/json/CreatePickup", {
|
|
634
|
+
ClientInfo: this.buildClientInfo(),
|
|
635
|
+
Transaction: EMPTY_TRANSACTION,
|
|
636
|
+
Pickup: pickup
|
|
637
|
+
}, { errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
638
|
+
const processed = response.ProcessedPickup;
|
|
639
|
+
if (!processed || !processed.GUID && !processed.ID) {
|
|
640
|
+
throw new APIError("Failed to create pickup", {
|
|
641
|
+
carrier: "aramex",
|
|
642
|
+
raw: response
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const failedShipments = (processed.ProcessedShipments ?? []).filter((s) => s.HasErrors);
|
|
646
|
+
if (failedShipments.length > 0) {
|
|
647
|
+
const notifications = failedShipments.flatMap((s) => s.Notifications ?? []);
|
|
648
|
+
throw new APIError(notifications.map((n) => n.Message).filter(Boolean).join("; ") || `Pickup created but ${failedShipments.length} shipment(s) failed`, {
|
|
649
|
+
carrier: "aramex",
|
|
650
|
+
errors: AramexAdapter.notificationsToErrors(notifications),
|
|
651
|
+
raw: response
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
return mapPickupResponse(processed, input);
|
|
655
|
+
}
|
|
656
|
+
async cancelPickup(pickupId) {
|
|
657
|
+
const response = await this.shippingHttp.post("/json/CancelPickup", {
|
|
658
|
+
ClientInfo: this.buildClientInfo(),
|
|
659
|
+
Transaction: EMPTY_TRANSACTION,
|
|
660
|
+
PickupGUID: String(pickupId)
|
|
661
|
+
}, { errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
662
|
+
return response.HasErrors !== true;
|
|
663
|
+
}
|
|
664
|
+
async getCities(countryCode) {
|
|
665
|
+
const response = await this.locationHttp.post("/json/FetchCities", {
|
|
666
|
+
ClientInfo: this.buildClientInfo(),
|
|
667
|
+
Transaction: EMPTY_TRANSACTION,
|
|
668
|
+
CountryCode: countryCode ?? this.aramexConfig.credentials.accountCountryCode
|
|
669
|
+
}, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
670
|
+
return (response.Cities ?? []).map(mapCity);
|
|
671
|
+
}
|
|
672
|
+
async getDropoffLocations(countryCode) {
|
|
673
|
+
const response = await this.locationHttp.post("/json/FetchOffices", {
|
|
674
|
+
ClientInfo: this.buildClientInfo(),
|
|
675
|
+
Transaction: EMPTY_TRANSACTION,
|
|
676
|
+
CountryCode: countryCode ?? this.aramexConfig.credentials.accountCountryCode
|
|
677
|
+
}, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
|
|
678
|
+
return (response.Offices ?? []).map(mapOffice);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
export {
|
|
682
|
+
statusFromDescription,
|
|
683
|
+
parseAramexDate,
|
|
684
|
+
mapAramexStatus,
|
|
685
|
+
buildClientInfo,
|
|
686
|
+
AramexStatusCodes,
|
|
687
|
+
AramexService,
|
|
688
|
+
AramexProductType,
|
|
689
|
+
AramexProductGroup,
|
|
690
|
+
AramexPaymentType,
|
|
691
|
+
AramexAdapter
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
//# debugId=BC16779A6E5B3D1F64756E2164756E21
|