@pvh-afl/graphql 1.0.23 → 1.0.25
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/dist/order-tracking/order-tracking.service.d.ts +32 -10
- package/dist/order-tracking/order-tracking.service.d.ts.map +1 -1
- package/dist/order-tracking/order-tracking.service.js +267 -125
- package/dist/order-tracking/order-tracking.service.js.map +1 -1
- package/dist/order-tracking/order-tracking.types.d.ts +102 -17
- package/dist/order-tracking/order-tracking.types.d.ts.map +1 -1
- package/dist/order-tracking/order-tracking.types.js +383 -69
- package/dist/order-tracking/order-tracking.types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,35 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConfigService } from '@pvh-afl/core';
|
|
2
2
|
import { OrderTrackingInput, OrderTrackingResult } from './order-tracking.types';
|
|
3
3
|
/**
|
|
4
4
|
* Order Tracking Service
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Fetches comprehensive order tracking data from Shopify Admin API.
|
|
7
7
|
*/
|
|
8
8
|
export declare class OrderTrackingService {
|
|
9
|
-
private readonly integrationFactory;
|
|
10
9
|
private readonly configService;
|
|
11
|
-
|
|
12
|
-
constructor(integrationFactory: IntegrationFactory, configService: ConfigService);
|
|
10
|
+
constructor(configService: ConfigService);
|
|
13
11
|
/**
|
|
14
|
-
* Get
|
|
12
|
+
* Get complete order tracking information from Shopify.
|
|
15
13
|
*/
|
|
16
14
|
getOrderTracking(brand: string, input: OrderTrackingInput): Promise<OrderTrackingResult>;
|
|
17
15
|
/**
|
|
18
|
-
* Fetch order
|
|
16
|
+
* Fetch complete order details from Shopify Admin API.
|
|
19
17
|
*/
|
|
20
|
-
private
|
|
18
|
+
private fetchFromShopify;
|
|
21
19
|
/**
|
|
22
|
-
*
|
|
20
|
+
* Get tracking URL from fulfillment.
|
|
23
21
|
*/
|
|
24
|
-
private
|
|
22
|
+
private getTrackingUrl;
|
|
25
23
|
/**
|
|
26
24
|
* Map Shopify order to OrderDetails type.
|
|
27
25
|
*/
|
|
28
26
|
private mapOrderDetails;
|
|
27
|
+
/**
|
|
28
|
+
* Map line items from Shopify order.
|
|
29
|
+
*/
|
|
30
|
+
private mapLineItems;
|
|
31
|
+
/**
|
|
32
|
+
* Map cancellation info if order was cancelled.
|
|
33
|
+
*/
|
|
34
|
+
private mapCancellation;
|
|
35
|
+
/**
|
|
36
|
+
* Map returns from Shopify order.
|
|
37
|
+
*/
|
|
38
|
+
private mapReturns;
|
|
39
|
+
/**
|
|
40
|
+
* Map refunds from Shopify order.
|
|
41
|
+
*/
|
|
42
|
+
private mapRefunds;
|
|
29
43
|
/**
|
|
30
44
|
* Build tracking timeline from Shopify fulfillment events.
|
|
31
45
|
*/
|
|
32
46
|
private buildTrackingTimeline;
|
|
47
|
+
/**
|
|
48
|
+
* Map Shopify money to OrderMoney type.
|
|
49
|
+
*/
|
|
50
|
+
private mapMoney;
|
|
51
|
+
/**
|
|
52
|
+
* Map Shopify address to OrderAddress type.
|
|
53
|
+
*/
|
|
54
|
+
private mapAddress;
|
|
33
55
|
/**
|
|
34
56
|
* Format payment gateway names to readable format.
|
|
35
57
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-tracking.service.d.ts","sourceRoot":"","sources":["../../src/order-tracking/order-tracking.service.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"order-tracking.service.d.ts","sourceRoot":"","sources":["../../src/order-tracking/order-tracking.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAU,MAAM,eAAe,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EAWpB,MAAM,wBAAwB,CAAC;AAiJhC;;;;GAIG;AACH,qBACa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEzD;;OAEG;IACG,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC;IAkB/B;;OAEG;YACW,gBAAgB;IAmJ9B;;OAEG;IACH,OAAO,CAAC,cAAc;IAKtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiEvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;OAEG;IACH,OAAO,CAAC,eAAe;IA4BvB;;OAEG;IACH,OAAO,CAAC,UAAU;IAyBlB;;OAEG;IACH,OAAO,CAAC,UAAU;IAalB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAwF7B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAOhB;;OAEG;IACH,OAAO,CAAC,UAAU;IAiBlB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,qBAAqB;CAiB9B"}
|
|
@@ -4,93 +4,43 @@ exports.OrderTrackingService = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const common_1 = require("@nestjs/common");
|
|
6
6
|
const core_1 = require("@pvh-afl/core");
|
|
7
|
+
// Cancellation window in minutes
|
|
8
|
+
const CANCELLATION_WINDOW_MINUTES = 30;
|
|
7
9
|
/**
|
|
8
10
|
* Order Tracking Service
|
|
9
11
|
*
|
|
10
|
-
*
|
|
12
|
+
* Fetches comprehensive order tracking data from Shopify Admin API.
|
|
11
13
|
*/
|
|
12
14
|
let OrderTrackingService = class OrderTrackingService {
|
|
13
|
-
integrationFactory;
|
|
14
15
|
configService;
|
|
15
|
-
|
|
16
|
-
constructor(integrationFactory, configService) {
|
|
17
|
-
this.integrationFactory = integrationFactory;
|
|
16
|
+
constructor(configService) {
|
|
18
17
|
this.configService = configService;
|
|
19
18
|
}
|
|
20
19
|
/**
|
|
21
|
-
* Get
|
|
20
|
+
* Get complete order tracking information from Shopify.
|
|
22
21
|
*/
|
|
23
22
|
async getOrderTracking(brand, input) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.fetchFromOmuni(brand, input),
|
|
27
|
-
this.fetchFromShopify(brand, input.orderId),
|
|
28
|
-
]);
|
|
29
|
-
// Combine responses
|
|
30
|
-
return {
|
|
31
|
-
// Omuni data
|
|
32
|
-
showLoginOTP: omuniData?.showLoginOTP ?? false,
|
|
33
|
-
statusTrackingUrl: omuniData?.statusTrackingUrl,
|
|
34
|
-
logisticsTrackingUrl: omuniData?.logisticsTrackingUrl,
|
|
35
|
-
itemStatuses: omuniData?.itemStatuses,
|
|
36
|
-
// Shopify data
|
|
37
|
-
orderDetails: shopifyData?.orderDetails,
|
|
38
|
-
trackingTimeline: shopifyData?.trackingTimeline,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Fetch order tracking data from Omuni OMS.
|
|
43
|
-
*/
|
|
44
|
-
async fetchFromOmuni(brand, input) {
|
|
45
|
-
const omsService = this.integrationFactory.get(brand, 'oms');
|
|
46
|
-
if (!omsService) {
|
|
47
|
-
core_1.logger.warn('OMS integration not configured, skipping Omuni tracking', { brand });
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const request = {
|
|
51
|
-
entityType: input.entityType ?? 'SHOPIFY',
|
|
52
|
-
marketPlaceOrderId: input.orderId,
|
|
53
|
-
showItemStatus: input.showItemStatus !== false ? 'YES' : 'NO',
|
|
54
|
-
};
|
|
55
|
-
try {
|
|
56
|
-
const response = await omsService.trackOrder(request);
|
|
57
|
-
if (!response.success || !response.data) {
|
|
58
|
-
core_1.logger.warn('Omuni tracking request failed', {
|
|
59
|
-
brand,
|
|
60
|
-
orderId: input.orderId,
|
|
61
|
-
error: response.error,
|
|
62
|
-
});
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
const data = response.data.data;
|
|
66
|
-
const itemStatuses = data.itemStatus?.map((item) => ({
|
|
67
|
-
itemId: item.itemId,
|
|
68
|
-
marketPlaceItemId: item.marketPlaceItemId,
|
|
69
|
-
status: item.itemStatus,
|
|
70
|
-
}));
|
|
23
|
+
const result = await this.fetchFromShopify(brand, input.orderId);
|
|
24
|
+
if (!result) {
|
|
71
25
|
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
26
|
+
trackingUrl: undefined,
|
|
27
|
+
orderDetails: undefined,
|
|
28
|
+
trackingTimeline: undefined,
|
|
29
|
+
lineItems: undefined,
|
|
30
|
+
cancellation: undefined,
|
|
31
|
+
returns: undefined,
|
|
32
|
+
refunds: undefined,
|
|
76
33
|
};
|
|
77
34
|
}
|
|
78
|
-
|
|
79
|
-
core_1.logger.error('Omuni tracking fetch failed', {
|
|
80
|
-
brand,
|
|
81
|
-
orderId: input.orderId,
|
|
82
|
-
error: error instanceof Error ? error.message : 'Unknown',
|
|
83
|
-
});
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
35
|
+
return result;
|
|
86
36
|
}
|
|
87
37
|
/**
|
|
88
|
-
* Fetch order details
|
|
38
|
+
* Fetch complete order details from Shopify Admin API.
|
|
89
39
|
*/
|
|
90
40
|
async fetchFromShopify(brand, orderId) {
|
|
91
41
|
const adminConfig = this.getShopifyAdminConfig(brand);
|
|
92
42
|
if (!adminConfig) {
|
|
93
|
-
core_1.logger.warn('Shopify Admin API not configured
|
|
43
|
+
core_1.logger.warn('Shopify Admin API not configured', { brand });
|
|
94
44
|
return null;
|
|
95
45
|
}
|
|
96
46
|
// Build query filter - support both order name (#1001) and order ID
|
|
@@ -105,40 +55,67 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
105
55
|
id
|
|
106
56
|
name
|
|
107
57
|
createdAt
|
|
58
|
+
updatedAt
|
|
59
|
+
processedAt
|
|
108
60
|
displayFinancialStatus
|
|
109
61
|
displayFulfillmentStatus
|
|
110
62
|
cancelledAt
|
|
63
|
+
cancelReason
|
|
111
64
|
closedAt
|
|
65
|
+
confirmed
|
|
112
66
|
paymentGatewayNames
|
|
67
|
+
channelInformation {
|
|
68
|
+
app { title }
|
|
69
|
+
channelDefinition { channelName }
|
|
70
|
+
}
|
|
71
|
+
subtotalPriceSet { shopMoney { amount currencyCode } }
|
|
72
|
+
totalShippingPriceSet { shopMoney { amount currencyCode } }
|
|
73
|
+
totalTaxSet { shopMoney { amount currencyCode } }
|
|
74
|
+
totalPriceSet { shopMoney { amount currencyCode } }
|
|
75
|
+
billingAddress {
|
|
76
|
+
name firstName lastName
|
|
77
|
+
address1 address2
|
|
78
|
+
city province provinceCode
|
|
79
|
+
country countryCode zip phone
|
|
80
|
+
}
|
|
81
|
+
shippingAddress {
|
|
82
|
+
name firstName lastName
|
|
83
|
+
address1 address2
|
|
84
|
+
city province provinceCode
|
|
85
|
+
country countryCode zip phone
|
|
86
|
+
}
|
|
113
87
|
shippingLines(first: 5) {
|
|
88
|
+
edges {
|
|
89
|
+
node { title code }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
lineItems(first: 50) {
|
|
114
93
|
edges {
|
|
115
94
|
node {
|
|
116
|
-
|
|
117
|
-
|
|
95
|
+
id name sku quantity variantTitle fulfillmentStatus
|
|
96
|
+
originalUnitPriceSet { shopMoney { amount currencyCode } }
|
|
97
|
+
originalTotalSet { shopMoney { amount currencyCode } }
|
|
98
|
+
image { url }
|
|
99
|
+
variant {
|
|
100
|
+
selectedOptions { name value }
|
|
101
|
+
}
|
|
118
102
|
}
|
|
119
103
|
}
|
|
120
104
|
}
|
|
121
105
|
fulfillments {
|
|
122
|
-
status
|
|
123
|
-
createdAt
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
trackingInfo {
|
|
127
|
-
number
|
|
128
|
-
company
|
|
129
|
-
url
|
|
130
|
-
}
|
|
131
|
-
events(first: 20) {
|
|
106
|
+
id status displayStatus
|
|
107
|
+
createdAt updatedAt deliveredAt inTransitAt estimatedDeliveryAt
|
|
108
|
+
trackingInfo { number company url }
|
|
109
|
+
events(first: 30) {
|
|
132
110
|
edges {
|
|
133
|
-
node {
|
|
134
|
-
status
|
|
135
|
-
happenedAt
|
|
136
|
-
city
|
|
137
|
-
province
|
|
138
|
-
}
|
|
111
|
+
node { status happenedAt city province message }
|
|
139
112
|
}
|
|
140
113
|
}
|
|
141
114
|
}
|
|
115
|
+
refunds {
|
|
116
|
+
id createdAt note
|
|
117
|
+
totalRefundedSet { shopMoney { amount currencyCode } }
|
|
118
|
+
}
|
|
142
119
|
}
|
|
143
120
|
}
|
|
144
121
|
}
|
|
@@ -176,8 +153,13 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
176
153
|
}
|
|
177
154
|
const order = orderEdge.node;
|
|
178
155
|
return {
|
|
156
|
+
trackingUrl: this.getTrackingUrl(order),
|
|
179
157
|
orderDetails: this.mapOrderDetails(order),
|
|
180
158
|
trackingTimeline: this.buildTrackingTimeline(order),
|
|
159
|
+
lineItems: this.mapLineItems(order),
|
|
160
|
+
cancellation: this.mapCancellation(order),
|
|
161
|
+
returns: this.mapReturns(order),
|
|
162
|
+
refunds: this.mapRefunds(order),
|
|
181
163
|
};
|
|
182
164
|
}
|
|
183
165
|
catch (error) {
|
|
@@ -189,6 +171,13 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
189
171
|
return null;
|
|
190
172
|
}
|
|
191
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Get tracking URL from fulfillment.
|
|
176
|
+
*/
|
|
177
|
+
getTrackingUrl(order) {
|
|
178
|
+
const fulfillment = order.fulfillments?.[0];
|
|
179
|
+
return fulfillment?.trackingInfo?.[0]?.url;
|
|
180
|
+
}
|
|
192
181
|
/**
|
|
193
182
|
* Map Shopify order to OrderDetails type.
|
|
194
183
|
*/
|
|
@@ -202,7 +191,7 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
202
191
|
orderStatus = 'cancelled';
|
|
203
192
|
}
|
|
204
193
|
else if (order.displayFulfillmentStatus === 'FULFILLED') {
|
|
205
|
-
orderStatus = 'fulfilled';
|
|
194
|
+
orderStatus = fulfillment?.deliveredAt ? 'delivered' : 'fulfilled';
|
|
206
195
|
}
|
|
207
196
|
else if (order.displayFulfillmentStatus === 'PARTIALLY_FULFILLED') {
|
|
208
197
|
orderStatus = 'partially_fulfilled';
|
|
@@ -210,22 +199,141 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
210
199
|
else if (order.displayFulfillmentStatus === 'IN_PROGRESS') {
|
|
211
200
|
orderStatus = 'processing';
|
|
212
201
|
}
|
|
213
|
-
|
|
214
|
-
|
|
202
|
+
else if (order.confirmed) {
|
|
203
|
+
orderStatus = 'confirmed';
|
|
204
|
+
}
|
|
205
|
+
// Check if order can be cancelled
|
|
206
|
+
const orderAgeMinutes = (Date.now() - new Date(order.createdAt).getTime()) / (1000 * 60);
|
|
207
|
+
const canCancel = !order.cancelledAt &&
|
|
208
|
+
order.displayFulfillmentStatus === 'UNFULFILLED' &&
|
|
209
|
+
orderAgeMinutes <= CANCELLATION_WINDOW_MINUTES;
|
|
210
|
+
let cancellationMessage;
|
|
211
|
+
if (!canCancel && !order.cancelledAt) {
|
|
212
|
+
if (order.displayFulfillmentStatus !== 'UNFULFILLED') {
|
|
213
|
+
cancellationMessage = 'We have already started the shipping process.';
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
cancellationMessage = `Unfortunately, the approximate ${CANCELLATION_WINDOW_MINUTES} minute window for cancelling this order has passed.`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Get channel name
|
|
220
|
+
const channelName = order.channelInformation?.channelDefinition?.channelName ||
|
|
221
|
+
order.channelInformation?.app?.title ||
|
|
222
|
+
'Online Store';
|
|
215
223
|
return {
|
|
216
224
|
orderId: order.id,
|
|
217
225
|
orderName: order.name,
|
|
218
226
|
orderStatus,
|
|
219
227
|
fulfillmentStatus: order.displayFulfillmentStatus?.toLowerCase(),
|
|
228
|
+
financialStatus: order.displayFinancialStatus?.toLowerCase(),
|
|
229
|
+
channelName,
|
|
220
230
|
shippingMethod: shippingLine?.title,
|
|
221
|
-
paymentMethod,
|
|
231
|
+
paymentMethod: this.formatPaymentMethod(order.paymentGatewayNames),
|
|
222
232
|
trackingNumber: trackingInfo?.number,
|
|
223
233
|
carrierName: trackingInfo?.company,
|
|
234
|
+
trackingUrl: trackingInfo?.url,
|
|
224
235
|
createdAt: order.createdAt,
|
|
225
|
-
|
|
236
|
+
estimatedDeliveryAt: fulfillment?.estimatedDeliveryAt,
|
|
226
237
|
deliveredAt: fulfillment?.deliveredAt,
|
|
238
|
+
subtotal: this.mapMoney(order.subtotalPriceSet.shopMoney),
|
|
239
|
+
shippingCost: this.mapMoney(order.totalShippingPriceSet.shopMoney),
|
|
240
|
+
totalTax: this.mapMoney(order.totalTaxSet.shopMoney),
|
|
241
|
+
totalAmount: this.mapMoney(order.totalPriceSet.shopMoney),
|
|
242
|
+
billingAddress: order.billingAddress ? this.mapAddress(order.billingAddress) : undefined,
|
|
243
|
+
shippingAddress: order.shippingAddress ? this.mapAddress(order.shippingAddress) : undefined,
|
|
244
|
+
canCancel,
|
|
245
|
+
cancellationMessage,
|
|
227
246
|
};
|
|
228
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Map line items from Shopify order.
|
|
250
|
+
*/
|
|
251
|
+
mapLineItems(order) {
|
|
252
|
+
return order.lineItems.edges.map(({ node }) => {
|
|
253
|
+
const variantOptions = node.variant?.selectedOptions?.map(opt => ({
|
|
254
|
+
name: opt.name,
|
|
255
|
+
value: opt.value,
|
|
256
|
+
})) || [];
|
|
257
|
+
return {
|
|
258
|
+
id: node.id,
|
|
259
|
+
name: node.name,
|
|
260
|
+
sku: node.sku,
|
|
261
|
+
quantity: node.quantity,
|
|
262
|
+
unitPrice: this.mapMoney(node.originalUnitPriceSet.shopMoney),
|
|
263
|
+
totalPrice: this.mapMoney(node.originalTotalSet.shopMoney),
|
|
264
|
+
imageUrl: node.image?.url,
|
|
265
|
+
variantTitle: node.variantTitle,
|
|
266
|
+
variantOptions,
|
|
267
|
+
fulfillmentStatus: node.fulfillmentStatus?.toLowerCase(),
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Map cancellation info if order was cancelled.
|
|
273
|
+
*/
|
|
274
|
+
mapCancellation(order) {
|
|
275
|
+
if (!order.cancelledAt) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
// Map cancel reason to human readable text
|
|
279
|
+
const reasonMap = {
|
|
280
|
+
CUSTOMER: 'Customer requested cancellation',
|
|
281
|
+
FRAUD: 'Suspected fraudulent order',
|
|
282
|
+
INVENTORY: 'Items out of stock',
|
|
283
|
+
DECLINED: 'Payment declined',
|
|
284
|
+
OTHER: 'Other reason',
|
|
285
|
+
};
|
|
286
|
+
const refund = order.refunds?.[0];
|
|
287
|
+
return {
|
|
288
|
+
cancelledAt: order.cancelledAt,
|
|
289
|
+
reason: order.cancelReason ? reasonMap[order.cancelReason] || order.cancelReason : undefined,
|
|
290
|
+
refund: refund ? {
|
|
291
|
+
id: refund.id,
|
|
292
|
+
createdAt: refund.createdAt,
|
|
293
|
+
totalRefunded: this.mapMoney(refund.totalRefundedSet.shopMoney),
|
|
294
|
+
note: refund.note,
|
|
295
|
+
} : undefined,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Map returns from Shopify order.
|
|
300
|
+
*/
|
|
301
|
+
mapReturns(order) {
|
|
302
|
+
const returns = order.returns?.edges;
|
|
303
|
+
if (!returns || returns.length === 0) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
return returns.map(({ node }) => {
|
|
307
|
+
const items = node.returnLineItems.edges.map(({ node: rli }) => ({
|
|
308
|
+
id: rli.id,
|
|
309
|
+
name: rli.fulfillmentLineItem?.lineItem?.name || 'Unknown Item',
|
|
310
|
+
quantity: rli.quantity,
|
|
311
|
+
imageUrl: rli.fulfillmentLineItem?.lineItem?.image?.url,
|
|
312
|
+
returnReason: rli.returnReason,
|
|
313
|
+
}));
|
|
314
|
+
return {
|
|
315
|
+
id: node.id,
|
|
316
|
+
status: node.status,
|
|
317
|
+
createdAt: node.createdAt,
|
|
318
|
+
itemCount: items.length,
|
|
319
|
+
items,
|
|
320
|
+
};
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Map refunds from Shopify order.
|
|
325
|
+
*/
|
|
326
|
+
mapRefunds(order) {
|
|
327
|
+
if (!order.refunds || order.refunds.length === 0) {
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
return order.refunds.map(refund => ({
|
|
331
|
+
id: refund.id,
|
|
332
|
+
createdAt: refund.createdAt,
|
|
333
|
+
totalRefunded: this.mapMoney(refund.totalRefundedSet.shopMoney),
|
|
334
|
+
note: refund.note,
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
229
337
|
/**
|
|
230
338
|
* Build tracking timeline from Shopify fulfillment events.
|
|
231
339
|
*/
|
|
@@ -233,7 +341,7 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
233
341
|
const timeline = [];
|
|
234
342
|
// Define the standard tracking stages
|
|
235
343
|
const stages = [
|
|
236
|
-
{ status: 'ordered', label: '
|
|
344
|
+
{ status: 'ordered', label: 'Order placed' },
|
|
237
345
|
{ status: 'confirmed', label: 'Confirmed' },
|
|
238
346
|
{ status: 'in_transit', label: 'Shipped' },
|
|
239
347
|
{ status: 'out_for_delivery', label: 'Out for Delivery' },
|
|
@@ -249,66 +357,99 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
249
357
|
// Map events by status (use latest event for each status)
|
|
250
358
|
const eventsByStatus = new Map();
|
|
251
359
|
for (const event of allEvents) {
|
|
252
|
-
const normalizedStatus = event.status.toLowerCase()
|
|
360
|
+
const normalizedStatus = event.status.toLowerCase();
|
|
253
361
|
const existing = eventsByStatus.get(normalizedStatus);
|
|
254
362
|
if (!existing || new Date(event.happenedAt) > new Date(existing.happenedAt)) {
|
|
255
363
|
eventsByStatus.set(normalizedStatus, event);
|
|
256
364
|
}
|
|
257
365
|
}
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
status: 'ordered',
|
|
261
|
-
label: 'Ordered',
|
|
262
|
-
happenedAt: order.createdAt,
|
|
263
|
-
isCompleted: true,
|
|
264
|
-
isCurrent: false,
|
|
265
|
-
};
|
|
266
|
-
timeline.push(orderedEvent);
|
|
267
|
-
// Determine current status
|
|
268
|
-
let currentStatusIndex = 0; // Default to "ordered"
|
|
366
|
+
// Determine current status index
|
|
367
|
+
let currentStatusIndex = 0;
|
|
269
368
|
const fulfillment = order.fulfillments?.[0];
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
369
|
+
if (order.cancelledAt) {
|
|
370
|
+
currentStatusIndex = -1; // Cancelled
|
|
371
|
+
}
|
|
372
|
+
else if (fulfillment) {
|
|
373
|
+
if (fulfillment.deliveredAt || eventsByStatus.has('delivered')) {
|
|
374
|
+
currentStatusIndex = 4;
|
|
274
375
|
}
|
|
275
376
|
else if (eventsByStatus.has('out_for_delivery')) {
|
|
276
377
|
currentStatusIndex = 3;
|
|
277
378
|
}
|
|
278
|
-
else if (eventsByStatus.has('in_transit')
|
|
379
|
+
else if (fulfillment.inTransitAt || eventsByStatus.has('in_transit')) {
|
|
279
380
|
currentStatusIndex = 2;
|
|
280
381
|
}
|
|
281
382
|
else if (eventsByStatus.has('confirmed') || fulfillment.createdAt) {
|
|
282
383
|
currentStatusIndex = 1;
|
|
283
384
|
}
|
|
284
385
|
}
|
|
285
|
-
|
|
286
|
-
|
|
386
|
+
else if (order.confirmed) {
|
|
387
|
+
currentStatusIndex = 0;
|
|
388
|
+
}
|
|
389
|
+
// Build timeline
|
|
390
|
+
for (let i = 0; i < stages.length; i++) {
|
|
287
391
|
const stage = stages[i];
|
|
288
392
|
const event = eventsByStatus.get(stage.status);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (stage.status === '
|
|
292
|
-
happenedAt =
|
|
393
|
+
let happenedAt;
|
|
394
|
+
// Determine timestamp for each stage
|
|
395
|
+
if (stage.status === 'ordered') {
|
|
396
|
+
happenedAt = order.createdAt;
|
|
293
397
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
happenedAt = fulfillment.deliveredAt;
|
|
398
|
+
else if (stage.status === 'confirmed') {
|
|
399
|
+
happenedAt = event?.happenedAt || fulfillment?.createdAt;
|
|
297
400
|
}
|
|
298
|
-
|
|
401
|
+
else if (stage.status === 'in_transit') {
|
|
402
|
+
happenedAt = event?.happenedAt || fulfillment?.inTransitAt;
|
|
403
|
+
}
|
|
404
|
+
else if (stage.status === 'out_for_delivery') {
|
|
405
|
+
happenedAt = event?.happenedAt;
|
|
406
|
+
}
|
|
407
|
+
else if (stage.status === 'delivered') {
|
|
408
|
+
happenedAt = event?.happenedAt || fulfillment?.deliveredAt;
|
|
409
|
+
}
|
|
410
|
+
const isCompleted = currentStatusIndex >= 0 && i <= currentStatusIndex;
|
|
299
411
|
const isCurrent = i === currentStatusIndex;
|
|
300
412
|
timeline.push({
|
|
301
413
|
status: stage.status,
|
|
302
414
|
label: stage.label,
|
|
303
|
-
happenedAt
|
|
415
|
+
happenedAt,
|
|
304
416
|
city: event?.city,
|
|
305
417
|
province: event?.province,
|
|
418
|
+
message: event?.message,
|
|
306
419
|
isCompleted,
|
|
307
420
|
isCurrent,
|
|
308
421
|
});
|
|
309
422
|
}
|
|
310
423
|
return timeline;
|
|
311
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Map Shopify money to OrderMoney type.
|
|
427
|
+
*/
|
|
428
|
+
mapMoney(money) {
|
|
429
|
+
return {
|
|
430
|
+
amount: money.amount,
|
|
431
|
+
currencyCode: money.currencyCode,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Map Shopify address to OrderAddress type.
|
|
436
|
+
*/
|
|
437
|
+
mapAddress(addr) {
|
|
438
|
+
return {
|
|
439
|
+
name: addr.name,
|
|
440
|
+
firstName: addr.firstName,
|
|
441
|
+
lastName: addr.lastName,
|
|
442
|
+
address1: addr.address1,
|
|
443
|
+
address2: addr.address2,
|
|
444
|
+
city: addr.city,
|
|
445
|
+
province: addr.province,
|
|
446
|
+
provinceCode: addr.provinceCode,
|
|
447
|
+
country: addr.country,
|
|
448
|
+
countryCode: addr.countryCode,
|
|
449
|
+
zip: addr.zip,
|
|
450
|
+
phone: addr.phone,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
312
453
|
/**
|
|
313
454
|
* Format payment gateway names to readable format.
|
|
314
455
|
*/
|
|
@@ -316,7 +457,6 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
316
457
|
if (!gatewayNames || gatewayNames.length === 0) {
|
|
317
458
|
return 'Unknown';
|
|
318
459
|
}
|
|
319
|
-
// Common payment gateway mappings
|
|
320
460
|
const gatewayMap = {
|
|
321
461
|
'cash_on_delivery': 'COD',
|
|
322
462
|
'cod': 'COD',
|
|
@@ -328,12 +468,15 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
328
468
|
'phonepe': 'PhonePe',
|
|
329
469
|
'upi': 'UPI',
|
|
330
470
|
'gokwik': 'GoKwik',
|
|
471
|
+
'ideal': 'iDEAL',
|
|
331
472
|
};
|
|
332
473
|
const gateway = gatewayNames[0].toLowerCase();
|
|
333
|
-
|
|
334
|
-
if (gateway.includes('upi')) {
|
|
474
|
+
if (gateway.includes('upi'))
|
|
335
475
|
return 'UPI';
|
|
336
|
-
|
|
476
|
+
if (gateway.includes('ideal'))
|
|
477
|
+
return 'iDEAL';
|
|
478
|
+
if (gateway.includes('credit') || gateway.includes('card'))
|
|
479
|
+
return 'Credit Card';
|
|
337
480
|
return gatewayMap[gateway] || this.capitalizeFirst(gatewayNames[0].replace(/_/g, ' '));
|
|
338
481
|
}
|
|
339
482
|
/**
|
|
@@ -365,7 +508,6 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
365
508
|
exports.OrderTrackingService = OrderTrackingService;
|
|
366
509
|
exports.OrderTrackingService = OrderTrackingService = tslib_1.__decorate([
|
|
367
510
|
(0, common_1.Injectable)(),
|
|
368
|
-
tslib_1.__metadata("design:paramtypes", [core_1.
|
|
369
|
-
core_1.ConfigService])
|
|
511
|
+
tslib_1.__metadata("design:paramtypes", [core_1.ConfigService])
|
|
370
512
|
], OrderTrackingService);
|
|
371
513
|
//# sourceMappingURL=order-tracking.service.js.map
|