@things-factory/operato-hub 4.3.823 → 4.3.824

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,100 +1,138 @@
1
- import { getConnection, getRepository } from 'typeorm'
1
+ import { getConnection } from 'typeorm'
2
2
 
3
3
  import { ApplicationType } from '@things-factory/auth-base'
4
4
  import { logger } from '@things-factory/env'
5
5
  import { Platform, Powrup, PowrupAPI } from '@things-factory/integration-powrup'
6
6
  import { ORDER_STATUS, OrderPackage } from '@things-factory/sales-base'
7
7
 
8
+ const ADVISORY_LOCK_KEY = 'powrup-initiate-shipment'
9
+
8
10
  export const powrupInitiateShipmentResolver = {
9
11
  async powrupInitiateShipment(_: void, {}, context: any): Promise<void> {
10
12
  const maxRTSRetry: number = 10
13
+
14
+ // Same fix as powrupGetAirwayBill / powrupGetTrackingNo: avoid wrapping the
15
+ // resolver in a long transaction across external PowrupAPI HTTP calls. Use a
16
+ // single QueryRunner with a session-level advisory lock for concurrency safety,
17
+ // and let each UPDATE auto-commit so row locks are held only for the duration
18
+ // of the UPDATE statement, not across the API call.
19
+ const queryRunner = getConnection().createQueryRunner()
20
+ let connected = false
21
+
11
22
  try {
12
- await getConnection().transaction(async tx => {
13
- try {
14
- const powrup: Powrup = await tx
15
- .getRepository(Powrup)
16
- .findOne({ where: { platform: Platform.POWRUP, active: true } })
17
-
18
- let orderPackageQb = tx
19
- .getRepository(OrderPackage)
20
- .createQueryBuilder('opk')
21
- .innerJoinAndSelect('opk.releaseGood', 'rg')
22
- .innerJoinAndSelect('opk.domain', 'domain')
23
- .innerJoinAndSelect('opk.bizplace', 'bizplace')
24
- .leftJoinAndSelect('opk.creator', 'creator')
25
- .leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
26
- .where(`rg.source = :source`, { source: ApplicationType.POWRUP })
27
- .andWhere(`rg.type = :rgType`, { rgType: 'b2c' })
28
- .andWhere(`rg.ref_order_id NOTNULL`)
29
- .andWhere(`rg.status = :status`, {
30
- status: ORDER_STATUS.DONE
31
- })
32
- .andWhere(`opk.market_rts_call_success ISNULL`)
33
- .andWhere(`COALESCE(opk.market_rts_call_retry, 0) <= :maxRTSRetry`, { maxRTSRetry: maxRTSRetry })
34
- .orderBy(`opk.created_at`)
35
-
36
- let orderPackagesWithoutRTS: OrderPackage[] = await orderPackageQb.getMany()
37
-
38
- if (orderPackagesWithoutRTS?.length > 0) {
39
- for (let i = 0; i < orderPackagesWithoutRTS.length; i++) {
40
- const orderPackage: OrderPackage = orderPackagesWithoutRTS[i]
41
- const releaseGood = orderPackage.releaseGood
42
-
43
- if (releaseGood.lmdOption == true && orderPackage?.trackingNo == null) {
44
- continue
45
- }
23
+ await queryRunner.connect()
24
+ connected = true
46
25
 
47
- let orderInformation: any = {
48
- refOrderId: orderPackage.releaseGood.refOrderId,
49
- packageId: orderPackage.packageId,
50
- platform: orderPackage.releaseGood.platformCode
51
- }
52
- if (orderPackage?.releaseGood?.lmdOption) {
53
- orderInformation = {
54
- ...orderInformation,
55
- awb: orderPackage.airwayBill,
56
- trackingNo: orderPackage.trackingNo,
57
- lmdOption: orderPackage?.releaseGood?.lmdOption,
58
- shipperName: orderPackage?.releaseGood?.lastMileDelivery?.platform
59
- }
60
- }
26
+ const lockResult: { acquired: boolean }[] = await queryRunner.query(
27
+ `SELECT pg_try_advisory_lock(hashtextextended($1, 0)) AS acquired`,
28
+ [ADVISORY_LOCK_KEY]
29
+ )
30
+ if (!lockResult?.[0]?.acquired) {
31
+ logger.info(`powrupInitiateShipment: another instance is running, skipping`)
32
+ return
33
+ }
34
+
35
+ try {
36
+ const manager = queryRunner.manager
37
+
38
+ const powrup: Powrup = await manager.findOne(Powrup, {
39
+ where: { platform: Platform.POWRUP, active: true }
40
+ })
41
+
42
+ const orderPackagesWithoutRTS: OrderPackage[] = await manager
43
+ .createQueryBuilder(OrderPackage, 'opk')
44
+ .innerJoinAndSelect('opk.releaseGood', 'rg')
45
+ .innerJoinAndSelect('opk.domain', 'domain')
46
+ .innerJoinAndSelect('opk.bizplace', 'bizplace')
47
+ .leftJoinAndSelect('opk.creator', 'creator')
48
+ .leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
49
+ .where(`rg.source = :source`, { source: ApplicationType.POWRUP })
50
+ .andWhere(`rg.type = :rgType`, { rgType: 'b2c' })
51
+ .andWhere(`rg.ref_order_id NOTNULL`)
52
+ .andWhere(`rg.status = :status`, { status: ORDER_STATUS.DONE })
53
+ .andWhere(`opk.market_rts_call_success ISNULL`)
54
+ .andWhere(`COALESCE(opk.market_rts_call_retry, 0) <= :maxRTSRetry`, { maxRTSRetry })
55
+ .orderBy(`opk.created_at`)
56
+ .getMany()
57
+
58
+ if (orderPackagesWithoutRTS?.length > 0) {
59
+ for (let i = 0; i < orderPackagesWithoutRTS.length; i++) {
60
+ const orderPackage: OrderPackage = orderPackagesWithoutRTS[i]
61
+ const releaseGood = orderPackage.releaseGood
62
+
63
+ if (releaseGood.lmdOption == true && orderPackage?.trackingNo == null) {
64
+ continue
65
+ }
61
66
 
62
- performance.mark(`start:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`)
63
- try {
64
- await PowrupAPI.initiateShipment(powrup, {
65
- ...orderInformation,
66
- context: { state: { domain: null, user: null } }
67
- })
68
- await tx
69
- .getRepository(OrderPackage)
70
- .update({ id: orderPackage.id }, { marketRtsCallSuccess: new Date() })
71
- performance.mark(`end:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`)
72
- } catch (error) {
73
- performance.mark(`end:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`)
74
- logger.error(`powrupInitiateShipment[InitiateShipment][Order Package ID: ${orderPackage.id}]: ${error}`)
75
- await getRepository(OrderPackage)
76
- .createQueryBuilder()
77
- .update(OrderPackage)
78
- .set({ marketRtsCallRetry: () => `coalesce("market_rts_call_retry", 0) + 1` })
79
- .where('id = :id', { id: orderPackage.id })
80
- .execute()
81
- continue
82
- } finally {
83
- let perf = performance.measure(
84
- `powrupInitiateShipment: ${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`,
85
- `start:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`,
86
- `end:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`
87
- )
88
- console.info(perf.toJSON())
67
+ let orderInformation: any = {
68
+ refOrderId: orderPackage.releaseGood.refOrderId,
69
+ packageId: orderPackage.packageId,
70
+ platform: orderPackage.releaseGood.platformCode
71
+ }
72
+ if (orderPackage?.releaseGood?.lmdOption) {
73
+ orderInformation = {
74
+ ...orderInformation,
75
+ awb: orderPackage.airwayBill,
76
+ trackingNo: orderPackage.trackingNo,
77
+ lmdOption: orderPackage?.releaseGood?.lmdOption,
78
+ shipperName: orderPackage?.releaseGood?.lastMileDelivery?.platform
89
79
  }
90
80
  }
81
+
82
+ performance.mark(`start:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`)
83
+ try {
84
+ await PowrupAPI.initiateShipment(powrup, {
85
+ ...orderInformation,
86
+ context: { state: { domain: null, user: null } }
87
+ })
88
+
89
+ // Auto-commit single-statement UPDATE — row lock held only for this UPDATE,
90
+ // not across the API call above.
91
+ await manager.update(OrderPackage, { id: orderPackage.id }, { marketRtsCallSuccess: new Date() })
92
+
93
+ performance.mark(`end:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`)
94
+ } catch (error) {
95
+ performance.mark(`end:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`)
96
+ logger.error(`powrupInitiateShipment[InitiateShipment][Order Package ID: ${orderPackage.id}]: ${error}`)
97
+
98
+ await manager
99
+ .createQueryBuilder()
100
+ .update(OrderPackage)
101
+ .set({ marketRtsCallRetry: () => `coalesce("market_rts_call_retry", 0) + 1` })
102
+ .where('id = :id', { id: orderPackage.id })
103
+ .execute()
104
+
105
+ continue
106
+ } finally {
107
+ let perf = performance.measure(
108
+ `powrupInitiateShipment: ${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`,
109
+ `start:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`,
110
+ `end:${orderPackage.releaseGood.refOrderId} - ${orderPackage.packageId}`
111
+ )
112
+ console.info(perf.toJSON())
113
+ }
91
114
  }
92
- } catch (e) {
93
- logger.error(`powrupInitiateShipment[InitiateShipment]: ${e}`)
94
115
  }
95
- })
116
+ } catch (e) {
117
+ logger.error(`powrupInitiateShipment[InitiateShipment]: ${e}`)
118
+ } finally {
119
+ try {
120
+ await queryRunner.query(`SELECT pg_advisory_unlock(hashtextextended($1, 0))`, [ADVISORY_LOCK_KEY])
121
+ } catch (releaseErr) {
122
+ logger.error(`powrupInitiateShipment[AdvisoryUnlock]: ${releaseErr}`)
123
+ }
124
+ }
96
125
  } catch (e) {
97
126
  logger.error(`powrupInitiateShipment[MainError]: ${e}`)
127
+ } finally {
128
+ if (connected) {
129
+ try {
130
+ await queryRunner.query(`SELECT pg_advisory_unlock_all()`)
131
+ } catch (unlockAllErr) {
132
+ logger.error(`powrupInitiateShipment[AdvisoryUnlockAll]: ${unlockAllErr}`)
133
+ }
134
+ }
135
+ await queryRunner.release()
98
136
  }
99
137
  }
100
138
  }
@@ -1,116 +1,159 @@
1
- import { getConnection, getRepository } from 'typeorm'
1
+ import { getConnection } from 'typeorm'
2
2
 
3
3
  import { ApplicationType } from '@things-factory/auth-base'
4
4
  import { logger } from '@things-factory/env'
5
5
  import { Sellercraft, SellercraftAPI } from '@things-factory/integration-sellercraft'
6
6
  import { ORDER_STATUS, OrderPackage } from '@things-factory/sales-base'
7
7
 
8
+ const ADVISORY_LOCK_KEY = 'sellercraft-initiate-shipment'
9
+
8
10
  export const sellercraftInitiateShipmentResolver = {
9
11
  async sellercraftInitiateShipment(_: void, {}, context: any): Promise<void> {
10
12
  const maxRTSRetry: number = 10
13
+
14
+ // Same fix as powrupGetAirwayBill / powrupGetTrackingNo: do not wrap the
15
+ // resolver in a single long transaction across external SellercraftAPI HTTP
16
+ // calls. The previous version held row locks on every UPDATE'd order_packages
17
+ // row until the loop finished, which was the direct cause of the order_packages
18
+ // lock chains observed in pg_stat_activity (sessions stuck `idle in transaction`
19
+ // for 10+ minutes after `UPDATE order_packages SET market_rts_call_success`).
20
+ //
21
+ // Use a single QueryRunner with a session-level advisory lock for concurrency
22
+ // safety, and let each UPDATE auto-commit on its own (row lock held only for
23
+ // the duration of that single statement).
24
+ const queryRunner = getConnection().createQueryRunner()
25
+ let connected = false
26
+
11
27
  try {
12
- await getConnection().transaction(async tx => {
13
- try {
14
- let orderPackageQb = tx
15
- .getRepository(OrderPackage)
16
- .createQueryBuilder('opk')
17
- .innerJoinAndSelect('opk.releaseGood', 'rg')
18
- .innerJoinAndSelect('opk.domain', 'domain')
19
- .innerJoinAndSelect('opk.bizplace', 'bizplace')
20
- .innerJoinAndSelect('bizplace.domain', 'bdomain')
21
- .leftJoinAndSelect('opk.creator', 'creator')
22
- .leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
23
- .where(`rg.source = :source`, { source: ApplicationType.SELLERCRAFT })
24
- .andWhere(`rg.type = :rgType`, { rgType: 'b2c' })
25
- .andWhere(`rg.status = :status`, {
26
- status: ORDER_STATUS.DONE
28
+ await queryRunner.connect()
29
+ connected = true
30
+
31
+ const lockResult: { acquired: boolean }[] = await queryRunner.query(
32
+ `SELECT pg_try_advisory_lock(hashtextextended($1, 0)) AS acquired`,
33
+ [ADVISORY_LOCK_KEY]
34
+ )
35
+ if (!lockResult?.[0]?.acquired) {
36
+ logger.info(`sellercraftInitiateShipment: another instance is running, skipping`)
37
+ return
38
+ }
39
+
40
+ try {
41
+ const manager = queryRunner.manager
42
+
43
+ const orderPackagesWithoutRTS: OrderPackage[] = await manager
44
+ .createQueryBuilder(OrderPackage, 'opk')
45
+ .innerJoinAndSelect('opk.releaseGood', 'rg')
46
+ .innerJoinAndSelect('opk.domain', 'domain')
47
+ .innerJoinAndSelect('opk.bizplace', 'bizplace')
48
+ .innerJoinAndSelect('bizplace.domain', 'bdomain')
49
+ .leftJoinAndSelect('opk.creator', 'creator')
50
+ .leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
51
+ .where(`rg.source = :source`, { source: ApplicationType.SELLERCRAFT })
52
+ .andWhere(`rg.type = :rgType`, { rgType: 'b2c' })
53
+ .andWhere(`rg.status = :status`, { status: ORDER_STATUS.DONE })
54
+ .andWhere(`opk.market_rts_call_success ISNULL`)
55
+ .andWhere(`COALESCE(opk.market_rts_call_retry, 0) <= :maxRTSRetry`, { maxRTSRetry })
56
+ .orderBy(`opk.created_at`)
57
+ .getMany()
58
+
59
+ if (orderPackagesWithoutRTS?.length > 0) {
60
+ for (let i = 0; i < orderPackagesWithoutRTS.length; i++) {
61
+ const orderPackage: OrderPackage = orderPackagesWithoutRTS[i]
62
+ const releaseGood = orderPackage.releaseGood
63
+
64
+ const sellercraft: Sellercraft = await manager.findOne(Sellercraft, {
65
+ where: { domain: { id: orderPackage.bizplace.domain.id }, status: 'ACTIVE' }
27
66
  })
28
- .andWhere(`opk.market_rts_call_success ISNULL`)
29
- .andWhere(`COALESCE(opk.market_rts_call_retry, 0) <= :maxRTSRetry`, { maxRTSRetry: maxRTSRetry })
30
- .orderBy(`opk.created_at`)
31
67
 
32
- let orderPackagesWithoutRTS: OrderPackage[] = await orderPackageQb.getMany()
68
+ if (!sellercraft) {
69
+ continue
70
+ }
33
71
 
34
- if (orderPackagesWithoutRTS?.length > 0) {
35
- for (let i = 0; i < orderPackagesWithoutRTS.length; i++) {
36
- const orderPackage: OrderPackage = orderPackagesWithoutRTS[i]
37
- const releaseGood = orderPackage.releaseGood
72
+ let sellercraftSetting: any = sellercraft.sellercraftSetting
73
+ let disableInitiateOrderShipment: boolean =
74
+ sellercraftSetting?.disableInitiateOrderShipment &&
75
+ sellercraftSetting?.disableInitiateOrderShipment == 1
76
+ ? true
77
+ : false
38
78
 
39
- const sellercraft: Sellercraft = await tx
40
- .getRepository(Sellercraft)
41
- .findOne({ where: { domain: { id: orderPackage.bizplace.domain.id }, status: 'ACTIVE' } })
79
+ if (
80
+ disableInitiateOrderShipment === true ||
81
+ (releaseGood.lmdOption == true && orderPackage?.trackingNo == null)
82
+ ) {
83
+ continue
84
+ }
42
85
 
43
- if (!sellercraft) {
44
- continue
86
+ let orderInformation: any = {
87
+ accountId: sellercraft.accountId,
88
+ orderId: releaseGood.refNo2,
89
+ packageId: orderPackage.packageId
90
+ }
91
+ if (orderPackage?.releaseGood?.lmdOption) {
92
+ orderInformation = {
93
+ ...orderInformation,
94
+ awb: orderPackage.airwayBill,
95
+ trackingNo: orderPackage.trackingNo,
96
+ lmdOption: orderPackage?.releaseGood?.lmdOption,
97
+ shipperName: orderPackage?.releaseGood?.lastMileDelivery?.platform
45
98
  }
99
+ }
46
100
 
47
- let sellercraftSetting: any = sellercraft.sellercraftSetting
48
- let disableInitiateOrderShipment: boolean =
49
- sellercraftSetting?.disableInitiateOrderShipment &&
50
- sellercraftSetting?.disableInitiateOrderShipment == 1
51
- ? true
52
- : false
53
-
54
- if (
55
- disableInitiateOrderShipment === true ||
56
- (releaseGood.lmdOption == true && orderPackage?.trackingNo == null)
57
- ) {
58
- continue
59
- }
101
+ performance.mark(`start:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`)
102
+ try {
103
+ await SellercraftAPI.initiateOrderShipment(sellercraft, {
104
+ ...orderInformation,
105
+ context: { state: { domain: null, user: null } }
106
+ })
60
107
 
61
- let orderInformation: any = {
62
- accountId: sellercraft.accountId,
63
- orderId: releaseGood.refNo2,
64
- packageId: orderPackage.packageId
65
- }
66
- if (orderPackage?.releaseGood?.lmdOption) {
67
- orderInformation = {
68
- ...orderInformation,
69
- awb: orderPackage.airwayBill,
70
- trackingNo: orderPackage.trackingNo,
71
- lmdOption: orderPackage?.releaseGood?.lmdOption,
72
- shipperName: orderPackage?.releaseGood?.lastMileDelivery?.platform
73
- }
74
- }
108
+ // Auto-commit single-statement UPDATE — row lock held only for this UPDATE,
109
+ // not across the API call above.
110
+ await manager.update(OrderPackage, { id: orderPackage.id }, { marketRtsCallSuccess: new Date() })
75
111
 
76
- performance.mark(`start:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`)
77
- try {
78
- await SellercraftAPI.initiateOrderShipment(sellercraft, {
79
- ...orderInformation,
80
- context: { state: { domain: null, user: null } }
81
- })
82
- await tx
83
- .getRepository(OrderPackage)
84
- .update({ id: orderPackage.id }, { marketRtsCallSuccess: new Date() })
85
- performance.mark(`end:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`)
86
- } catch (error) {
87
- performance.mark(`end:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`)
88
- logger.error(
89
- `sellercraftInitiateShipment[InitiateShipment][Order Package ID: ${orderPackage.id}]: ${error}`
90
- )
91
- await getRepository(OrderPackage)
92
- .createQueryBuilder()
93
- .update(OrderPackage)
94
- .set({ marketRtsCallRetry: () => `coalesce("market_rts_call_retry", 0) + 1` })
95
- .where('id = :id', { id: orderPackage.id })
96
- .execute()
97
- continue
98
- } finally {
99
- let perf = performance.measure(
100
- `sellercraftInitiateShipment: ${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`,
101
- `start:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`,
102
- `end:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`
103
- )
104
- console.info(perf.toJSON())
105
- }
112
+ performance.mark(`end:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`)
113
+ } catch (error) {
114
+ performance.mark(`end:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`)
115
+ logger.error(
116
+ `sellercraftInitiateShipment[InitiateShipment][Order Package ID: ${orderPackage.id}]: ${error}`
117
+ )
118
+
119
+ await manager
120
+ .createQueryBuilder()
121
+ .update(OrderPackage)
122
+ .set({ marketRtsCallRetry: () => `coalesce("market_rts_call_retry", 0) + 1` })
123
+ .where('id = :id', { id: orderPackage.id })
124
+ .execute()
125
+
126
+ continue
127
+ } finally {
128
+ let perf = performance.measure(
129
+ `sellercraftInitiateShipment: ${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`,
130
+ `start:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`,
131
+ `end:${orderPackage.releaseGood.refNo2} - ${orderPackage.packageId}`
132
+ )
133
+ console.info(perf.toJSON())
106
134
  }
107
135
  }
108
- } catch (e) {
109
- logger.error(`sellercraftInitiateShipment[InitiateShipment]: ${e}`)
110
136
  }
111
- })
137
+ } catch (e) {
138
+ logger.error(`sellercraftInitiateShipment[InitiateShipment]: ${e}`)
139
+ } finally {
140
+ try {
141
+ await queryRunner.query(`SELECT pg_advisory_unlock(hashtextextended($1, 0))`, [ADVISORY_LOCK_KEY])
142
+ } catch (releaseErr) {
143
+ logger.error(`sellercraftInitiateShipment[AdvisoryUnlock]: ${releaseErr}`)
144
+ }
145
+ }
112
146
  } catch (e) {
113
147
  logger.error(`sellercraftInitiateShipment[MainError]: ${e}`)
148
+ } finally {
149
+ if (connected) {
150
+ try {
151
+ await queryRunner.query(`SELECT pg_advisory_unlock_all()`)
152
+ } catch (unlockAllErr) {
153
+ logger.error(`sellercraftInitiateShipment[AdvisoryUnlockAll]: ${unlockAllErr}`)
154
+ }
155
+ }
156
+ await queryRunner.release()
114
157
  }
115
158
  }
116
159
  }
@@ -19,14 +19,14 @@ export const Mutation = /* GraphQL */ `
19
19
  syncAllProductsToSellercraft: Boolean @transaction
20
20
  syncAllProductsToPowrup: Boolean @transaction
21
21
  sellercraftPackMarketplaceOrder: Boolean @transaction
22
- sellercraftInitiateShipment: Boolean @transaction
23
22
  powrupPackOrder: Boolean @transaction
24
23
  # Intentionally NOT @transaction — these resolvers manage their own QueryRunner
25
- # to avoid holding row locks on order_packages across external Powrup API calls.
26
- # See powrup-get-airway-bill.ts and powrup-get-tracking-no.ts for the rationale.
24
+ # to avoid holding row locks on order_packages across external API calls.
25
+ # See the resolver files for the rationale.
27
26
  powrupGetTrackingNo: Boolean
28
27
  powrupGetAirwayBill: Boolean
29
- powrupInitiateShipment: Boolean @transaction
28
+ powrupInitiateShipment: Boolean
29
+ sellercraftInitiateShipment: Boolean
30
30
  getLmdAwb: Boolean @transaction
31
31
  createDatShipments: Boolean @transaction
32
32
  autoUpdateAllMarketplaceProductVariationQuantity(companyDomainId: String, warehouseId: String): Boolean @transaction
@@ -929,7 +929,18 @@ async function massageOrderItems(
929
929
  inputOrderProducts = await inputOrderProducts.reduce(async (accPromise, itm, idx) => {
930
930
  const acc = await accPromise
931
931
  let { sku, refCode } = itm.product
932
- let { packingType, packingSize, batchId, releaseQty, sellingPrice, paidAmount, uom, refItemId, warehouseCode } = itm
932
+ let {
933
+ packingType,
934
+ packingSize,
935
+ batchId,
936
+ releaseQty,
937
+ sellingPrice,
938
+ paidAmount,
939
+ uom,
940
+ refItemId,
941
+ warehouseCode,
942
+ remark
943
+ } = itm
933
944
 
934
945
  if (!sku && !refCode) {
935
946
  throw new ApiError('E01', 'sku or refCode not found')
@@ -965,7 +976,8 @@ async function massageOrderItems(
965
976
  uom: uom,
966
977
  batchId: batchId?.trim() || '',
967
978
  refItemId: refItemId ?? '',
968
- warehouseCode: normalizedWarehouseCode
979
+ warehouseCode: normalizedWarehouseCode,
980
+ remark: remark || null
969
981
  })
970
982
  }
971
983