@things-factory/operato-hub 4.3.184 → 4.3.186

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.
Files changed (24) hide show
  1. package/client/actions/hub.js +0 -5
  2. package/config.development.js +8 -23
  3. package/dist-server/graphql/resolvers/synchronization/get-lmd-awb.js +137 -0
  4. package/dist-server/graphql/resolvers/synchronization/get-lmd-awb.js.map +1 -0
  5. package/dist-server/graphql/resolvers/synchronization/index.js +2 -1
  6. package/dist-server/graphql/resolvers/synchronization/index.js.map +1 -1
  7. package/dist-server/graphql/types/synchronization/index.js +1 -0
  8. package/dist-server/graphql/types/synchronization/index.js.map +1 -1
  9. package/dist-server/routers/api/restful-apis/unstable/add-release-order.js +33 -1
  10. package/dist-server/routers/api/restful-apis/unstable/add-release-order.js.map +1 -1
  11. package/dist-server/routers/api/restful-apis/v1/utils/params.js +3 -1
  12. package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
  13. package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js +33 -1
  14. package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js.map +1 -1
  15. package/dist-server/routers/xilnex-router.js +1 -0
  16. package/dist-server/routers/xilnex-router.js.map +1 -1
  17. package/package.json +18 -18
  18. package/server/graphql/resolvers/synchronization/get-lmd-awb.ts +178 -0
  19. package/server/graphql/resolvers/synchronization/index.ts +3 -1
  20. package/server/graphql/types/synchronization/index.ts +1 -0
  21. package/server/routers/api/restful-apis/unstable/add-release-order.ts +52 -2
  22. package/server/routers/api/restful-apis/v1/utils/params.ts +3 -1
  23. package/server/routers/api/restful-apis/v1/warehouse/add-release-order.ts +42 -2
  24. package/server/routers/xilnex-router.ts +1 -0
@@ -0,0 +1,178 @@
1
+ import { getRepository, getConnection, Not, In, IsNull, SelectQueryBuilder, EntityManager } from 'typeorm'
2
+ import { v4 as uuidv4 } from 'uuid'
3
+
4
+ import { ReleaseGood, OrderPackage, ORDER_STATUS } from '@things-factory/sales-base'
5
+ import { LastMileAPI } from '@things-factory/integration-lmd'
6
+ import { Board } from '@things-factory/board-service'
7
+ import { logger } from '@things-factory/env'
8
+ import { headlessModel, pdf } from '@things-factory/board-service'
9
+ import { uploadAwb } from '@things-factory/attachment-base'
10
+ import { Attachment } from '@things-factory/attachment-base'
11
+ import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
12
+ import { ApplicationType } from '@things-factory/auth-base'
13
+ import { SellercraftController } from '@things-factory/worksheet-base'
14
+ import { Setting } from '@things-factory/setting-base'
15
+
16
+ const LMD_BOARDS = {
17
+ dhl: 'operato-lmd-dhl-awb',
18
+ airspeed: 'operato-lmd-airspeed-awb'
19
+ }
20
+
21
+ export const getLmdAwbResolver = {
22
+ async getLmdAwb(_: any, {}, context: any) {
23
+ const qb: SelectQueryBuilder<OrderPackage> = getRepository(OrderPackage)
24
+ .createQueryBuilder('op')
25
+ .innerJoinAndSelect('op.releaseGood', 'rg')
26
+ .innerJoinAndSelect('rg.domain', 'd')
27
+ .innerJoinAndSelect('rg.bizplace', 'b')
28
+ .innerJoinAndSelect('b.domain', 'd2')
29
+ .leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
30
+ .where('rg.lmd_option = true')
31
+ .andWhere('rg.status NOT IN (:...statuses)', {
32
+ statuses: [ORDER_STATUS.CANCELLED, ORDER_STATUS.PENDING_CANCEL, ORDER_STATUS.DONE]
33
+ })
34
+ .andWhere('op.parcel_id IS NOT NULL')
35
+ .andWhere('op.airway_bill IS NULL')
36
+
37
+ const orderPackages: OrderPackage[] = await qb.getMany()
38
+
39
+ let releaseOrders: ReleaseGood = []
40
+
41
+ for (let orderPackage of orderPackages) {
42
+ await getConnection().transaction(async tx => {
43
+ try {
44
+ let awbRes = await LastMileAPI.getAwb(
45
+ { ...orderPackage.releaseGood.lastMileDelivery, platform: 'lmdMiddleware' },
46
+ { parcelId: orderPackage.parcelId }
47
+ )
48
+
49
+ if (awbRes) {
50
+ await constructAndUploadAwb(orderPackage.releaseGood, orderPackage, awbRes)
51
+ releaseOrders.push(orderPackage.releaseGood)
52
+ }
53
+ } catch (e) {
54
+ logger.error(e)
55
+ }
56
+ })
57
+ }
58
+
59
+ // trigger RTS
60
+ try {
61
+ let uniqueReleaseOrders: ReleaseGood = [...new Map(releaseOrders.map(item => [item['id'], item])).values()]
62
+
63
+ for (let releaseOrder of uniqueReleaseOrders) {
64
+ const orderSource: string = releaseOrder.source
65
+
66
+ if (orderSource == ApplicationType.SELLERCRAFT) {
67
+ const sellercraft: Sellercraft = await getRepository(Sellercraft).findOne({
68
+ domain: releaseOrder.bizplace.domain,
69
+ status: SellercraftStatus.ACTIVE
70
+ })
71
+ if (sellercraft) {
72
+ await getConnection().transaction(async (tx: EntityManager) => {
73
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, null, null)
74
+ const rtsTriggerLevel: Setting = await tx.getRepository(Setting).findOne({
75
+ where: { domain: releaseOrder.domain, category: 'id-rule', name: 'rts-trigger-level' }
76
+ })
77
+
78
+ if (rtsTriggerLevel && parseInt(rtsTriggerLevel?.value || 0) == 1) {
79
+ await sellercraftCtrl.initiateOrderShipment(sellercraft, releaseOrder)
80
+ }
81
+ })
82
+ }
83
+ }
84
+ }
85
+ } catch (e) {
86
+ logger.error(`[get-lmd-awb-sof-trigger]: ${e}`)
87
+ }
88
+ //
89
+ }
90
+ }
91
+
92
+ async function constructAndUploadAwb(releaseOrder, orderPackage, awbRes) {
93
+ let {
94
+ shipmentID,
95
+ deliveryConfirmationNo,
96
+ deliveryDepotCode,
97
+ primarySortCode,
98
+ secondarySortCode,
99
+ recipient,
100
+ sender,
101
+ tracking_no,
102
+ courier
103
+ } = awbRes
104
+
105
+ let senderFullAddress = `${sender?.address}${sender?.address2 ? ', ' + sender?.address2 : ''}${
106
+ sender?.district ? ', ' + sender?.district : ''
107
+ }${sender?.city ? ', ' + sender?.city : ''}${sender?.postcode ? ', ' + sender?.postcode : ''}${
108
+ sender?.state ? ', ' + sender?.state : ''
109
+ }`
110
+
111
+ let recipientAddress1 = `${recipient?.address}${recipient?.address2 ? ', ' + recipient?.address2 : ''}${
112
+ recipient?.district ? ', ' + recipient?.district : ''
113
+ }`
114
+
115
+ let recipientAddress2 = `${recipient?.state}${recipient?.city ? ', ' + recipient?.city : ''}${
116
+ recipient?.postcode ? ', ' + recipient?.postcode : ''
117
+ }`
118
+
119
+ let data = {
120
+ senderFullAddress,
121
+ senderName: sender?.name,
122
+ recipientAddress1,
123
+ recipientAddress2,
124
+ recipientPhone: recipient?.phone,
125
+ recipientName: recipient?.name,
126
+ refId: shipmentID,
127
+ deliveryConfirmationNo: deliveryConfirmationNo || tracking_no,
128
+ deliveryDepotCode: deliveryDepotCode,
129
+ primarySortCode: primarySortCode,
130
+ secondarySortCode: secondarySortCode,
131
+ orderNo: releaseOrder?.refNo
132
+ }
133
+
134
+ let lmdBoard: Board = await getRepository(Board).findOne({
135
+ where: { name: LMD_BOARDS[courier] },
136
+ relations: ['domain']
137
+ })
138
+
139
+ if (!lmdBoard) throw new Error(`[get-lmd-awb] board is not found (RO: ${releaseOrder.name})`)
140
+
141
+ const { model } = await headlessModel({ domain: lmdBoard.domain, id: lmdBoard.id })
142
+
143
+ const result = await pdf({
144
+ id: lmdBoard.id,
145
+ model,
146
+ data,
147
+ options: {
148
+ format: 'A4'
149
+ },
150
+ context: { state: { domain: lmdBoard.domain } }
151
+ })
152
+
153
+ let uuid = uuidv4()
154
+ let awb = await uploadAwb({
155
+ content: result,
156
+ title: uuid
157
+ })
158
+
159
+ if (!awb?.url) throw new Error(`[get-lmd-awb] awb was not uploaded for (RO: ${releaseOrder.name})`)
160
+
161
+ await getRepository(OrderPackage).update(
162
+ { id: orderPackage.id },
163
+ { airwayBill: awb.url, trackingNo: deliveryConfirmationNo || tracking_no }
164
+ )
165
+
166
+ // create attachment
167
+
168
+ await getRepository(Attachment).insert({
169
+ name: uuid,
170
+ mimetype: 'application/pdf',
171
+ category: 'LMD_AWB',
172
+ refBy: orderPackage.id,
173
+ path: awb.url,
174
+ domain: releaseOrder.domain,
175
+ size: 0,
176
+ encoding: ''
177
+ })
178
+ }
@@ -8,6 +8,7 @@ import { syncProductsToAccountResolver } from './sync-products-to-account'
8
8
  import { syncProductsToSellercraftResolver } from './sync-products-to-sellercraft'
9
9
  import { createDatShipments } from './create-dat-shipments'
10
10
  import { autoUpdateAllMarketplaceProductVariationQuantityResolver } from './auto-update-all-marketplace-product-variation-quantity'
11
+ import { getLmdAwbResolver } from './get-lmd-awb'
11
12
 
12
13
  export const Query = {
13
14
  ...fetchSyncProductsResolver,
@@ -22,5 +23,6 @@ export const Mutation = {
22
23
  ...syncAllProductsToSellercraftResolver,
23
24
  ...sellercraftPackMarketplaceOrderResolver,
24
25
  ...createDatShipments,
25
- ...autoUpdateAllMarketplaceProductVariationQuantityResolver
26
+ ...autoUpdateAllMarketplaceProductVariationQuantityResolver,
27
+ ...getLmdAwbResolver
26
28
  }
@@ -17,6 +17,7 @@ export const Mutation = /* GraphQL */ `
17
17
  syncProductsToAccount(accountId: String!, bizplaceId: String!): Boolean @transaction
18
18
  syncAllProductsToSellercraft: Boolean @transaction
19
19
  sellercraftPackMarketplaceOrder: Boolean @transaction
20
+ getLmdAwb: Boolean @transaction
20
21
  createDatShipments: Boolean @transaction
21
22
  autoUpdateAllMarketplaceProductVariationQuantity(companyDomainId: String, warehouseId: String): Boolean @transaction
22
23
  sendEmails: Boolean @transaction
@@ -1,4 +1,14 @@
1
- import { EntityManager, getConnection, getRepository, ILike, In, Not, Repository, SelectQueryBuilder } from 'typeorm'
1
+ import {
2
+ EntityManager,
3
+ getConnection,
4
+ getRepository,
5
+ In,
6
+ Not,
7
+ Repository,
8
+ SelectQueryBuilder,
9
+ ILike,
10
+ Brackets
11
+ } from 'typeorm'
2
12
  import { v4 as uuidv4 } from 'uuid'
3
13
 
4
14
  import { restfulApiRouter as router } from '@things-factory/api'
@@ -30,6 +40,7 @@ import {
30
40
  import { Setting } from '@things-factory/setting-base'
31
41
  import { Domain } from '@things-factory/shell'
32
42
  import { Inventory, INVENTORY_STATUS, LOCATION_TYPE } from '@things-factory/warehouse-base'
43
+ import { LastMileDelivery } from '@things-factory/integration-lmd'
33
44
 
34
45
  import { ValidationError } from '../../../../errors/index'
35
46
  import { businessMiddleware } from '../../../../middlewares/business-middleware'
@@ -116,7 +127,8 @@ router.post(
116
127
  paidAmount: bodyReq?.paidAmount,
117
128
  discountAmount: bodyReq?.discountAmount,
118
129
  taxAmount: bodyReq?.taxAmount,
119
- shippingFee: bodyReq?.shippingFee
130
+ shippingFee: bodyReq?.shippingFee,
131
+ lmdOption: bodyReq?.lmdOption
120
132
  }
121
133
 
122
134
  // check existing order
@@ -492,6 +504,12 @@ async function createReleaseGood(
492
504
  })
493
505
  : null
494
506
 
507
+ // assign lmd
508
+ let lmd: any = null
509
+ if (releaseGood?.lmdOption) {
510
+ lmd = await autoAssignLmd(domain, deliveryCountry?.id, releaseGood?.postalCode)
511
+ }
512
+
495
513
  newReleaseGood = {
496
514
  name: roNoSetting
497
515
  ? await generateId({ domain, type: ORDER_NUMBER_RULE_TYPE.RO_NUMBER, seed: {} })
@@ -545,6 +563,8 @@ async function createReleaseGood(
545
563
  discountAmount: releaseGood?.discountAmount,
546
564
  taxAmount: releaseGood?.taxAmount,
547
565
  shippingFee: releaseGood?.shippingFee,
566
+ lmdOption: releaseGood?.lmdOption,
567
+ lastMileDelivery: lmd,
548
568
  creator: user,
549
569
  updater: user,
550
570
  acceptedBy: user,
@@ -1123,3 +1143,33 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
1123
1143
 
1124
1144
  return orderProducts
1125
1145
  }
1146
+
1147
+ async function autoAssignLmd(domain, countryId, postalCode) {
1148
+ const qb: SelectQueryBuilder<LastMileDelivery> = getRepository(LastMileDelivery)
1149
+ .createQueryBuilder('lmd')
1150
+ .leftJoinAndSelect('lmd.lastMileDeliverySettings', 'lmds')
1151
+ .innerJoinAndSelect('lmds.geoCountry', 'gc')
1152
+ .where('lmd.domain_id = :domainId', { domainId: domain.id })
1153
+ .andWhere('lower(lmd.status) = :status', { status: 'active' })
1154
+ .andWhere('gc.id = :gcId', { gcId: countryId })
1155
+ .andWhere(
1156
+ new Brackets(qb => {
1157
+ qb.where(`lmds.include_postal_code = ''`)
1158
+ .orWhere('lmds.include_postal_code is null')
1159
+ .orWhere('lmds.include_postal_code like :includePostCode', { includePostCode: `%${postalCode}%` })
1160
+ })
1161
+ )
1162
+ .andWhere(
1163
+ new Brackets(qb => {
1164
+ qb.where(`lmds.exclude_postal_code = ''`)
1165
+ .orWhere('lmds.exclude_postal_code is null')
1166
+ .orWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
1167
+ })
1168
+ )
1169
+ // .andWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
1170
+ .orderBy('lmd.rank')
1171
+
1172
+ const lmdResult: LastMileDelivery = await qb.getOne()
1173
+
1174
+ return lmdResult
1175
+ }
@@ -188,6 +188,7 @@ export const params = {
188
188
  'deliverTo.state',
189
189
  'deliverTo.phone1',
190
190
  'deliverTo.phone2',
191
+ 'deliverTo.email',
191
192
  'marketplaceOrderStatus',
192
193
  'airwayBill',
193
194
  'invoice',
@@ -217,7 +218,8 @@ export const params = {
217
218
  'platformName',
218
219
  'discountAmount',
219
220
  'taxAmount',
220
- 'shippingFee'
221
+ 'shippingFee',
222
+ 'lmdOption'
221
223
  ],
222
224
  'cancel-release-order': ['bizplaceId', 'roId', 'orderNo', 'orderNo2', 'orderNo3', 'remark'],
223
225
  'update-release-order-details': [
@@ -1,4 +1,4 @@
1
- import { EntityManager, getConnection, getRepository, In, Not, Repository, SelectQueryBuilder, ILike } from 'typeorm'
1
+ import { EntityManager, getConnection, getRepository, In, Not, Repository, SelectQueryBuilder, ILike, Brackets } from 'typeorm'
2
2
  import { v4 as uuidv4 } from 'uuid'
3
3
 
4
4
  import { restfulApiRouter as router } from '@things-factory/api'
@@ -29,6 +29,7 @@ import { Setting } from '@things-factory/setting-base'
29
29
  import { Domain } from '@things-factory/shell'
30
30
  import { Inventory, INVENTORY_STATUS, LOCATION_TYPE } from '@things-factory/warehouse-base'
31
31
  import { GeoCountry } from '@things-factory/geography'
32
+ import { LastMileDelivery } from '@things-factory/integration-lmd'
32
33
 
33
34
  import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
34
35
  import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
@@ -120,6 +121,7 @@ router.post(
120
121
  discountAmount: bodyReq?.discountAmount,
121
122
  taxAmount: bodyReq?.taxAmount,
122
123
  shippingFee: bodyReq?.shippingFee,
124
+ lmdOption: bodyReq?.lmdOption,
123
125
  shippingOrder: {
124
126
  shipName: bodyReq?.shippingOrder.shipName,
125
127
  containerNo: bodyReq?.shippingOrder.containerNo,
@@ -414,6 +416,12 @@ async function createReleaseGood(
414
416
  })
415
417
  : null
416
418
 
419
+ // assign lmd
420
+ let lmd: any = null
421
+ if (releaseGood?.lmdOption) {
422
+ lmd = await autoAssignLmd(domain, deliveryCountry?.id, releaseGood?.postalCode)
423
+ }
424
+
417
425
  newReleaseGood = {
418
426
  name: roNoSetting
419
427
  ? await generateId({ domain, type: ORDER_NUMBER_RULE_TYPE.RO_NUMBER, seed: {} })
@@ -470,7 +478,9 @@ async function createReleaseGood(
470
478
  updater: user,
471
479
  acceptedBy: user,
472
480
  acceptedAt: new Date(),
473
- shippingOrder: shippingOrder ? shippingOrder : null
481
+ shippingOrder: shippingOrder ? shippingOrder : null,
482
+ lastMileDelivery: lmd,
483
+ lmdOption: releaseGood.lmdOption
474
484
  }
475
485
 
476
486
  let resultReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
@@ -981,3 +991,33 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
981
991
 
982
992
  return orderProducts
983
993
  }
994
+
995
+ async function autoAssignLmd(domain, countryId, postalCode) {
996
+ const qb: SelectQueryBuilder<LastMileDelivery> = getRepository(LastMileDelivery)
997
+ .createQueryBuilder('lmd')
998
+ .leftJoinAndSelect('lmd.lastMileDeliverySettings', 'lmds')
999
+ .innerJoinAndSelect('lmds.geoCountry', 'gc')
1000
+ .where('lmd.domain_id = :domainId', { domainId: domain.id })
1001
+ .andWhere('lower(lmd.status) = :status', { status: 'active' })
1002
+ .andWhere('gc.id = :gcId', { gcId: countryId })
1003
+ .andWhere(
1004
+ new Brackets(qb => {
1005
+ qb.where(`lmds.include_postal_code = ''`)
1006
+ .orWhere('lmds.include_postal_code is null')
1007
+ .orWhere('lmds.include_postal_code like :includePostCode', { includePostCode: `%${postalCode}%` })
1008
+ })
1009
+ )
1010
+ .andWhere(
1011
+ new Brackets(qb => {
1012
+ qb.where(`lmds.exclude_postal_code = ''`)
1013
+ .orWhere('lmds.exclude_postal_code is null')
1014
+ .orWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
1015
+ })
1016
+ )
1017
+ // .andWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
1018
+ .orderBy('lmd.rank')
1019
+
1020
+ const lmdResult: LastMileDelivery = await qb.getOne()
1021
+
1022
+ return lmdResult
1023
+ }
@@ -447,6 +447,7 @@ xilnexRouter.post('/webhook-xilnex-transfer-note', async (context, next) => {
447
447
  palletQty: 1,
448
448
  totalUomValue: `${(productDetail.uomValue * item.confirmedQty).toFixed(2)} ${productDetail.uom}`,
449
449
  status: 'PENDING',
450
+ refItemId: item.orderItemId,
450
451
  arrivalNotice,
451
452
  domain: warehouseDomain,
452
453
  bizplace: customerBizplace