@pvh-afl/graphql 1.0.22 → 1.0.24
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 +283 -124
- 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/dist/product/product.service.d.ts +5 -0
- package/dist/product/product.service.d.ts.map +1 -1
- package/dist/product/product.service.js +11 -0
- package/dist/product/product.service.js.map +1 -1
- package/dist/product/product.types.d.ts +1 -0
- package/dist/product/product.types.d.ts.map +1 -1
- package/dist/product/product.types.js +8 -0
- package/dist/product/product.types.js.map +1 -1
- package/dist/product-content/product-content.service.d.ts +4 -0
- package/dist/product-content/product-content.service.d.ts.map +1 -1
- package/dist/product-content/product-content.service.js +75 -0
- package/dist/product-content/product-content.service.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;IAoK9B;;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,36 +55,80 @@ 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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
111
|
+
node { status happenedAt city province message }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
refunds {
|
|
116
|
+
id createdAt note
|
|
117
|
+
totalRefundedSet { shopMoney { amount currencyCode } }
|
|
118
|
+
}
|
|
119
|
+
returns(first: 10) {
|
|
120
|
+
edges {
|
|
121
|
+
node {
|
|
122
|
+
id status createdAt
|
|
123
|
+
returnLineItems(first: 20) {
|
|
124
|
+
edges {
|
|
125
|
+
node {
|
|
126
|
+
id quantity returnReason
|
|
127
|
+
fulfillmentLineItem {
|
|
128
|
+
lineItem { name image { url } }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
138
132
|
}
|
|
139
133
|
}
|
|
140
134
|
}
|
|
@@ -176,8 +170,13 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
176
170
|
}
|
|
177
171
|
const order = orderEdge.node;
|
|
178
172
|
return {
|
|
173
|
+
trackingUrl: this.getTrackingUrl(order),
|
|
179
174
|
orderDetails: this.mapOrderDetails(order),
|
|
180
175
|
trackingTimeline: this.buildTrackingTimeline(order),
|
|
176
|
+
lineItems: this.mapLineItems(order),
|
|
177
|
+
cancellation: this.mapCancellation(order),
|
|
178
|
+
returns: this.mapReturns(order),
|
|
179
|
+
refunds: this.mapRefunds(order),
|
|
181
180
|
};
|
|
182
181
|
}
|
|
183
182
|
catch (error) {
|
|
@@ -189,6 +188,13 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
189
188
|
return null;
|
|
190
189
|
}
|
|
191
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Get tracking URL from fulfillment.
|
|
193
|
+
*/
|
|
194
|
+
getTrackingUrl(order) {
|
|
195
|
+
const fulfillment = order.fulfillments?.[0];
|
|
196
|
+
return fulfillment?.trackingInfo?.[0]?.url;
|
|
197
|
+
}
|
|
192
198
|
/**
|
|
193
199
|
* Map Shopify order to OrderDetails type.
|
|
194
200
|
*/
|
|
@@ -202,7 +208,7 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
202
208
|
orderStatus = 'cancelled';
|
|
203
209
|
}
|
|
204
210
|
else if (order.displayFulfillmentStatus === 'FULFILLED') {
|
|
205
|
-
orderStatus = 'fulfilled';
|
|
211
|
+
orderStatus = fulfillment?.deliveredAt ? 'delivered' : 'fulfilled';
|
|
206
212
|
}
|
|
207
213
|
else if (order.displayFulfillmentStatus === 'PARTIALLY_FULFILLED') {
|
|
208
214
|
orderStatus = 'partially_fulfilled';
|
|
@@ -210,22 +216,141 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
210
216
|
else if (order.displayFulfillmentStatus === 'IN_PROGRESS') {
|
|
211
217
|
orderStatus = 'processing';
|
|
212
218
|
}
|
|
213
|
-
|
|
214
|
-
|
|
219
|
+
else if (order.confirmed) {
|
|
220
|
+
orderStatus = 'confirmed';
|
|
221
|
+
}
|
|
222
|
+
// Check if order can be cancelled
|
|
223
|
+
const orderAgeMinutes = (Date.now() - new Date(order.createdAt).getTime()) / (1000 * 60);
|
|
224
|
+
const canCancel = !order.cancelledAt &&
|
|
225
|
+
order.displayFulfillmentStatus === 'UNFULFILLED' &&
|
|
226
|
+
orderAgeMinutes <= CANCELLATION_WINDOW_MINUTES;
|
|
227
|
+
let cancellationMessage;
|
|
228
|
+
if (!canCancel && !order.cancelledAt) {
|
|
229
|
+
if (order.displayFulfillmentStatus !== 'UNFULFILLED') {
|
|
230
|
+
cancellationMessage = 'We have already started the shipping process.';
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
cancellationMessage = `Unfortunately, the approximate ${CANCELLATION_WINDOW_MINUTES} minute window for cancelling this order has passed.`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Get channel name
|
|
237
|
+
const channelName = order.channelInformation?.channelDefinition?.channelName ||
|
|
238
|
+
order.channelInformation?.app?.title ||
|
|
239
|
+
'Online Store';
|
|
215
240
|
return {
|
|
216
241
|
orderId: order.id,
|
|
217
242
|
orderName: order.name,
|
|
218
243
|
orderStatus,
|
|
219
244
|
fulfillmentStatus: order.displayFulfillmentStatus?.toLowerCase(),
|
|
245
|
+
financialStatus: order.displayFinancialStatus?.toLowerCase(),
|
|
246
|
+
channelName,
|
|
220
247
|
shippingMethod: shippingLine?.title,
|
|
221
|
-
paymentMethod,
|
|
248
|
+
paymentMethod: this.formatPaymentMethod(order.paymentGatewayNames),
|
|
222
249
|
trackingNumber: trackingInfo?.number,
|
|
223
250
|
carrierName: trackingInfo?.company,
|
|
251
|
+
trackingUrl: trackingInfo?.url,
|
|
224
252
|
createdAt: order.createdAt,
|
|
225
|
-
|
|
253
|
+
estimatedDeliveryAt: fulfillment?.estimatedDeliveryAt,
|
|
226
254
|
deliveredAt: fulfillment?.deliveredAt,
|
|
255
|
+
subtotal: this.mapMoney(order.subtotalPriceSet.shopMoney),
|
|
256
|
+
shippingCost: this.mapMoney(order.totalShippingPriceSet.shopMoney),
|
|
257
|
+
totalTax: this.mapMoney(order.totalTaxSet.shopMoney),
|
|
258
|
+
totalAmount: this.mapMoney(order.totalPriceSet.shopMoney),
|
|
259
|
+
billingAddress: order.billingAddress ? this.mapAddress(order.billingAddress) : undefined,
|
|
260
|
+
shippingAddress: order.shippingAddress ? this.mapAddress(order.shippingAddress) : undefined,
|
|
261
|
+
canCancel,
|
|
262
|
+
cancellationMessage,
|
|
227
263
|
};
|
|
228
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Map line items from Shopify order.
|
|
267
|
+
*/
|
|
268
|
+
mapLineItems(order) {
|
|
269
|
+
return order.lineItems.edges.map(({ node }) => {
|
|
270
|
+
const variantOptions = node.variant?.selectedOptions?.map(opt => ({
|
|
271
|
+
name: opt.name,
|
|
272
|
+
value: opt.value,
|
|
273
|
+
})) || [];
|
|
274
|
+
return {
|
|
275
|
+
id: node.id,
|
|
276
|
+
name: node.name,
|
|
277
|
+
sku: node.sku,
|
|
278
|
+
quantity: node.quantity,
|
|
279
|
+
unitPrice: this.mapMoney(node.originalUnitPriceSet.shopMoney),
|
|
280
|
+
totalPrice: this.mapMoney(node.originalTotalSet.shopMoney),
|
|
281
|
+
imageUrl: node.image?.url,
|
|
282
|
+
variantTitle: node.variantTitle,
|
|
283
|
+
variantOptions,
|
|
284
|
+
fulfillmentStatus: node.fulfillmentStatus?.toLowerCase(),
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Map cancellation info if order was cancelled.
|
|
290
|
+
*/
|
|
291
|
+
mapCancellation(order) {
|
|
292
|
+
if (!order.cancelledAt) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
// Map cancel reason to human readable text
|
|
296
|
+
const reasonMap = {
|
|
297
|
+
CUSTOMER: 'Customer requested cancellation',
|
|
298
|
+
FRAUD: 'Suspected fraudulent order',
|
|
299
|
+
INVENTORY: 'Items out of stock',
|
|
300
|
+
DECLINED: 'Payment declined',
|
|
301
|
+
OTHER: 'Other reason',
|
|
302
|
+
};
|
|
303
|
+
const refund = order.refunds?.[0];
|
|
304
|
+
return {
|
|
305
|
+
cancelledAt: order.cancelledAt,
|
|
306
|
+
reason: order.cancelReason ? reasonMap[order.cancelReason] || order.cancelReason : undefined,
|
|
307
|
+
refund: refund ? {
|
|
308
|
+
id: refund.id,
|
|
309
|
+
createdAt: refund.createdAt,
|
|
310
|
+
totalRefunded: this.mapMoney(refund.totalRefundedSet.shopMoney),
|
|
311
|
+
note: refund.note,
|
|
312
|
+
} : undefined,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Map returns from Shopify order.
|
|
317
|
+
*/
|
|
318
|
+
mapReturns(order) {
|
|
319
|
+
const returns = order.returns?.edges;
|
|
320
|
+
if (!returns || returns.length === 0) {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
return returns.map(({ node }) => {
|
|
324
|
+
const items = node.returnLineItems.edges.map(({ node: rli }) => ({
|
|
325
|
+
id: rli.id,
|
|
326
|
+
name: rli.fulfillmentLineItem.lineItem.name,
|
|
327
|
+
quantity: rli.quantity,
|
|
328
|
+
imageUrl: rli.fulfillmentLineItem.lineItem.image?.url,
|
|
329
|
+
returnReason: rli.returnReason,
|
|
330
|
+
}));
|
|
331
|
+
return {
|
|
332
|
+
id: node.id,
|
|
333
|
+
status: node.status,
|
|
334
|
+
createdAt: node.createdAt,
|
|
335
|
+
itemCount: items.length,
|
|
336
|
+
items,
|
|
337
|
+
};
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Map refunds from Shopify order.
|
|
342
|
+
*/
|
|
343
|
+
mapRefunds(order) {
|
|
344
|
+
if (!order.refunds || order.refunds.length === 0) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
return order.refunds.map(refund => ({
|
|
348
|
+
id: refund.id,
|
|
349
|
+
createdAt: refund.createdAt,
|
|
350
|
+
totalRefunded: this.mapMoney(refund.totalRefundedSet.shopMoney),
|
|
351
|
+
note: refund.note,
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
229
354
|
/**
|
|
230
355
|
* Build tracking timeline from Shopify fulfillment events.
|
|
231
356
|
*/
|
|
@@ -233,7 +358,7 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
233
358
|
const timeline = [];
|
|
234
359
|
// Define the standard tracking stages
|
|
235
360
|
const stages = [
|
|
236
|
-
{ status: 'ordered', label: '
|
|
361
|
+
{ status: 'ordered', label: 'Order placed' },
|
|
237
362
|
{ status: 'confirmed', label: 'Confirmed' },
|
|
238
363
|
{ status: 'in_transit', label: 'Shipped' },
|
|
239
364
|
{ status: 'out_for_delivery', label: 'Out for Delivery' },
|
|
@@ -249,66 +374,99 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
249
374
|
// Map events by status (use latest event for each status)
|
|
250
375
|
const eventsByStatus = new Map();
|
|
251
376
|
for (const event of allEvents) {
|
|
252
|
-
const normalizedStatus = event.status.toLowerCase()
|
|
377
|
+
const normalizedStatus = event.status.toLowerCase();
|
|
253
378
|
const existing = eventsByStatus.get(normalizedStatus);
|
|
254
379
|
if (!existing || new Date(event.happenedAt) > new Date(existing.happenedAt)) {
|
|
255
380
|
eventsByStatus.set(normalizedStatus, event);
|
|
256
381
|
}
|
|
257
382
|
}
|
|
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"
|
|
383
|
+
// Determine current status index
|
|
384
|
+
let currentStatusIndex = 0;
|
|
269
385
|
const fulfillment = order.fulfillments?.[0];
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
386
|
+
if (order.cancelledAt) {
|
|
387
|
+
currentStatusIndex = -1; // Cancelled
|
|
388
|
+
}
|
|
389
|
+
else if (fulfillment) {
|
|
390
|
+
if (fulfillment.deliveredAt || eventsByStatus.has('delivered')) {
|
|
391
|
+
currentStatusIndex = 4;
|
|
274
392
|
}
|
|
275
393
|
else if (eventsByStatus.has('out_for_delivery')) {
|
|
276
394
|
currentStatusIndex = 3;
|
|
277
395
|
}
|
|
278
|
-
else if (eventsByStatus.has('in_transit')
|
|
396
|
+
else if (fulfillment.inTransitAt || eventsByStatus.has('in_transit')) {
|
|
279
397
|
currentStatusIndex = 2;
|
|
280
398
|
}
|
|
281
399
|
else if (eventsByStatus.has('confirmed') || fulfillment.createdAt) {
|
|
282
400
|
currentStatusIndex = 1;
|
|
283
401
|
}
|
|
284
402
|
}
|
|
285
|
-
|
|
286
|
-
|
|
403
|
+
else if (order.confirmed) {
|
|
404
|
+
currentStatusIndex = 0;
|
|
405
|
+
}
|
|
406
|
+
// Build timeline
|
|
407
|
+
for (let i = 0; i < stages.length; i++) {
|
|
287
408
|
const stage = stages[i];
|
|
288
409
|
const event = eventsByStatus.get(stage.status);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (stage.status === '
|
|
292
|
-
happenedAt =
|
|
410
|
+
let happenedAt;
|
|
411
|
+
// Determine timestamp for each stage
|
|
412
|
+
if (stage.status === 'ordered') {
|
|
413
|
+
happenedAt = order.createdAt;
|
|
414
|
+
}
|
|
415
|
+
else if (stage.status === 'confirmed') {
|
|
416
|
+
happenedAt = event?.happenedAt || fulfillment?.createdAt;
|
|
417
|
+
}
|
|
418
|
+
else if (stage.status === 'in_transit') {
|
|
419
|
+
happenedAt = event?.happenedAt || fulfillment?.inTransitAt;
|
|
293
420
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
happenedAt = fulfillment.deliveredAt;
|
|
421
|
+
else if (stage.status === 'out_for_delivery') {
|
|
422
|
+
happenedAt = event?.happenedAt;
|
|
297
423
|
}
|
|
298
|
-
|
|
424
|
+
else if (stage.status === 'delivered') {
|
|
425
|
+
happenedAt = event?.happenedAt || fulfillment?.deliveredAt;
|
|
426
|
+
}
|
|
427
|
+
const isCompleted = currentStatusIndex >= 0 && i <= currentStatusIndex;
|
|
299
428
|
const isCurrent = i === currentStatusIndex;
|
|
300
429
|
timeline.push({
|
|
301
430
|
status: stage.status,
|
|
302
431
|
label: stage.label,
|
|
303
|
-
happenedAt
|
|
432
|
+
happenedAt,
|
|
304
433
|
city: event?.city,
|
|
305
434
|
province: event?.province,
|
|
435
|
+
message: event?.message,
|
|
306
436
|
isCompleted,
|
|
307
437
|
isCurrent,
|
|
308
438
|
});
|
|
309
439
|
}
|
|
310
440
|
return timeline;
|
|
311
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* Map Shopify money to OrderMoney type.
|
|
444
|
+
*/
|
|
445
|
+
mapMoney(money) {
|
|
446
|
+
return {
|
|
447
|
+
amount: money.amount,
|
|
448
|
+
currencyCode: money.currencyCode,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Map Shopify address to OrderAddress type.
|
|
453
|
+
*/
|
|
454
|
+
mapAddress(addr) {
|
|
455
|
+
return {
|
|
456
|
+
name: addr.name,
|
|
457
|
+
firstName: addr.firstName,
|
|
458
|
+
lastName: addr.lastName,
|
|
459
|
+
address1: addr.address1,
|
|
460
|
+
address2: addr.address2,
|
|
461
|
+
city: addr.city,
|
|
462
|
+
province: addr.province,
|
|
463
|
+
provinceCode: addr.provinceCode,
|
|
464
|
+
country: addr.country,
|
|
465
|
+
countryCode: addr.countryCode,
|
|
466
|
+
zip: addr.zip,
|
|
467
|
+
phone: addr.phone,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
312
470
|
/**
|
|
313
471
|
* Format payment gateway names to readable format.
|
|
314
472
|
*/
|
|
@@ -316,7 +474,6 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
316
474
|
if (!gatewayNames || gatewayNames.length === 0) {
|
|
317
475
|
return 'Unknown';
|
|
318
476
|
}
|
|
319
|
-
// Common payment gateway mappings
|
|
320
477
|
const gatewayMap = {
|
|
321
478
|
'cash_on_delivery': 'COD',
|
|
322
479
|
'cod': 'COD',
|
|
@@ -328,12 +485,15 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
328
485
|
'phonepe': 'PhonePe',
|
|
329
486
|
'upi': 'UPI',
|
|
330
487
|
'gokwik': 'GoKwik',
|
|
488
|
+
'ideal': 'iDEAL',
|
|
331
489
|
};
|
|
332
490
|
const gateway = gatewayNames[0].toLowerCase();
|
|
333
|
-
|
|
334
|
-
if (gateway.includes('upi')) {
|
|
491
|
+
if (gateway.includes('upi'))
|
|
335
492
|
return 'UPI';
|
|
336
|
-
|
|
493
|
+
if (gateway.includes('ideal'))
|
|
494
|
+
return 'iDEAL';
|
|
495
|
+
if (gateway.includes('credit') || gateway.includes('card'))
|
|
496
|
+
return 'Credit Card';
|
|
337
497
|
return gatewayMap[gateway] || this.capitalizeFirst(gatewayNames[0].replace(/_/g, ' '));
|
|
338
498
|
}
|
|
339
499
|
/**
|
|
@@ -365,7 +525,6 @@ let OrderTrackingService = class OrderTrackingService {
|
|
|
365
525
|
exports.OrderTrackingService = OrderTrackingService;
|
|
366
526
|
exports.OrderTrackingService = OrderTrackingService = tslib_1.__decorate([
|
|
367
527
|
(0, common_1.Injectable)(),
|
|
368
|
-
tslib_1.__metadata("design:paramtypes", [core_1.
|
|
369
|
-
core_1.ConfigService])
|
|
528
|
+
tslib_1.__metadata("design:paramtypes", [core_1.ConfigService])
|
|
370
529
|
], OrderTrackingService);
|
|
371
530
|
//# sourceMappingURL=order-tracking.service.js.map
|