@pvh-afl/graphql 1.0.23 → 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.
@@ -1,35 +1,57 @@
1
- import { IntegrationFactory, ConfigService } from '@pvh-afl/core';
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
- * Combines data from Omuni OMS and Shopify Admin API for unified tracking.
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
- private readonly SHOPIFY_API_VERSION;
12
- constructor(integrationFactory: IntegrationFactory, configService: ConfigService);
10
+ constructor(configService: ConfigService);
13
11
  /**
14
- * Get unified order tracking information from Omuni and Shopify.
12
+ * Get complete order tracking information from Shopify.
15
13
  */
16
14
  getOrderTracking(brand: string, input: OrderTrackingInput): Promise<OrderTrackingResult>;
17
15
  /**
18
- * Fetch order tracking data from Omuni OMS.
16
+ * Fetch complete order details from Shopify Admin API.
19
17
  */
20
- private fetchFromOmuni;
18
+ private fetchFromShopify;
21
19
  /**
22
- * Fetch order details and fulfillment events from Shopify Admin API.
20
+ * Get tracking URL from fulfillment.
23
21
  */
24
- private fetchFromShopify;
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,EACL,kBAAkB,EAGlB,aAAa,EAEd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EAIpB,MAAM,wBAAwB,CAAC;AAwDhC;;;;GAIG;AACH,qBACa,oBAAoB;IAI7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAJhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAa;gBAG9B,kBAAkB,EAAE,kBAAkB,EACtC,aAAa,EAAE,aAAa;IAG/C;;OAEG;IACG,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC;IAqB/B;;OAEG;YACW,cAAc;IA2D5B;;OAEG;YACW,gBAAgB;IAqH9B;;OAEG;IACH,OAAO,CAAC,eAAe;IAmCvB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAyF7B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA6B3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,qBAAqB;CAiB9B"}
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
- * Combines data from Omuni OMS and Shopify Admin API for unified tracking.
12
+ * Fetches comprehensive order tracking data from Shopify Admin API.
11
13
  */
12
14
  let OrderTrackingService = class OrderTrackingService {
13
- integrationFactory;
14
15
  configService;
15
- SHOPIFY_API_VERSION = '2024-01';
16
- constructor(integrationFactory, configService) {
17
- this.integrationFactory = integrationFactory;
16
+ constructor(configService) {
18
17
  this.configService = configService;
19
18
  }
20
19
  /**
21
- * Get unified order tracking information from Omuni and Shopify.
20
+ * Get complete order tracking information from Shopify.
22
21
  */
23
22
  async getOrderTracking(brand, input) {
24
- // Fetch from both sources in parallel
25
- const [omuniData, shopifyData] = await Promise.all([
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
- showLoginOTP: data.showLoginOTP ?? false,
73
- statusTrackingUrl: data.statusTrackingUrl,
74
- logisticsTrackingUrl: data.logisticsTrackingUrl,
75
- itemStatuses,
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
- catch (error) {
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 and fulfillment events from Shopify Admin API.
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, skipping Shopify data', { brand });
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
- title
117
- code
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
- deliveredAt
125
- estimatedDeliveryAt
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
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
- // Format payment method
214
- const paymentMethod = this.formatPaymentMethod(order.paymentGatewayNames);
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
- expectedDeliveryAt: fulfillment?.estimatedDeliveryAt,
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: 'Ordered' },
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().replace(/_/g, '_');
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
- // Always add "Ordered" as first event (from order creation)
259
- const orderedEvent = {
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 (fulfillment) {
271
- const fulfillmentStatus = fulfillment.status?.toLowerCase();
272
- if (fulfillmentStatus === 'delivered' || fulfillment.deliveredAt) {
273
- currentStatusIndex = 4; // delivered
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') || fulfillmentStatus === '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
- // Build remaining timeline stages
286
- for (let i = 1; i < stages.length; i++) {
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
- // For "confirmed" stage, use fulfillment creation if no explicit event
290
- let happenedAt = event?.happenedAt;
291
- if (stage.status === 'confirmed' && !happenedAt && fulfillment?.createdAt) {
292
- happenedAt = fulfillment.createdAt;
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
- // For "delivered" stage, use deliveredAt if available
295
- if (stage.status === 'delivered' && !happenedAt && fulfillment?.deliveredAt) {
296
- happenedAt = fulfillment.deliveredAt;
421
+ else if (stage.status === 'out_for_delivery') {
422
+ happenedAt = event?.happenedAt;
297
423
  }
298
- const isCompleted = i <= currentStatusIndex;
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: 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
- // Check for UPI in gateway name
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.IntegrationFactory,
369
- core_1.ConfigService])
528
+ tslib_1.__metadata("design:paramtypes", [core_1.ConfigService])
370
529
  ], OrderTrackingService);
371
530
  //# sourceMappingURL=order-tracking.service.js.map