@things-factory/worksheet-base 4.3.190 → 4.3.193

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 (25) hide show
  1. package/dist-server/controllers/outbound/picking-worksheet-controller.js +1 -1
  2. package/dist-server/controllers/outbound/picking-worksheet-controller.js.map +1 -1
  3. package/dist-server/controllers/outbound/sorting-worksheet-controller.js +1 -1
  4. package/dist-server/controllers/outbound/sorting-worksheet-controller.js.map +1 -1
  5. package/dist-server/controllers/worksheet-controller.js +1 -1
  6. package/dist-server/controllers/worksheet-controller.js.map +1 -1
  7. package/dist-server/graphql/resolvers/worksheet/confirm-cancellation-release-order.js +3 -2
  8. package/dist-server/graphql/resolvers/worksheet/confirm-cancellation-release-order.js.map +1 -1
  9. package/dist-server/graphql/resolvers/worksheet/packing-worksheet.js +40 -45
  10. package/dist-server/graphql/resolvers/worksheet/packing-worksheet.js.map +1 -1
  11. package/dist-server/graphql/resolvers/worksheet/picking/complete-batch-picking.js +30 -1
  12. package/dist-server/graphql/resolvers/worksheet/picking/complete-batch-picking.js.map +1 -1
  13. package/dist-server/graphql/resolvers/worksheet/picking/complete-picking.js +25 -0
  14. package/dist-server/graphql/resolvers/worksheet/picking/complete-picking.js.map +1 -1
  15. package/dist-server/utils/lmd-util.js +138 -1
  16. package/dist-server/utils/lmd-util.js.map +1 -1
  17. package/package.json +9 -8
  18. package/server/controllers/outbound/picking-worksheet-controller.ts +34 -9
  19. package/server/controllers/outbound/sorting-worksheet-controller.ts +18 -5
  20. package/server/controllers/worksheet-controller.ts +34 -12
  21. package/server/graphql/resolvers/worksheet/confirm-cancellation-release-order.ts +2 -1
  22. package/server/graphql/resolvers/worksheet/packing-worksheet.ts +38 -45
  23. package/server/graphql/resolvers/worksheet/picking/complete-batch-picking.ts +34 -2
  24. package/server/graphql/resolvers/worksheet/picking/complete-picking.ts +27 -1
  25. package/server/utils/lmd-util.ts +186 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/worksheet-base",
3
- "version": "4.3.190",
3
+ "version": "4.3.193",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -28,17 +28,18 @@
28
28
  "@things-factory/document-template-base": "^4.3.179",
29
29
  "@things-factory/id-rule-base": "^4.3.187",
30
30
  "@things-factory/integration-accounting": "^4.3.190",
31
- "@things-factory/integration-lmd": "^4.3.190",
32
- "@things-factory/integration-marketplace": "^4.3.190",
33
- "@things-factory/integration-sellercraft": "^4.3.190",
31
+ "@things-factory/integration-lmd": "^4.3.193",
32
+ "@things-factory/integration-marketplace": "^4.3.193",
33
+ "@things-factory/integration-sellercraft": "^4.3.193",
34
34
  "@things-factory/integration-sftp": "^4.3.190",
35
- "@things-factory/marketplace-base": "^4.3.190",
35
+ "@things-factory/marketplace-base": "^4.3.193",
36
36
  "@things-factory/notification": "^4.3.186",
37
- "@things-factory/sales-base": "^4.3.190",
37
+ "@things-factory/sales-base": "^4.3.193",
38
38
  "@things-factory/setting-base": "^4.3.186",
39
39
  "@things-factory/shell": "^4.3.179",
40
40
  "@things-factory/transport-base": "^4.3.179",
41
- "@things-factory/warehouse-base": "^4.3.190"
41
+ "@things-factory/warehouse-base": "^4.3.193",
42
+ "uuid": "^9.0.0"
42
43
  },
43
- "gitHead": "9fc85f3d95eeddd2191d156d99fc77bcc81b5071"
44
+ "gitHead": "8cba442aa16d21e21fa20c2bd22ba039e0a9bb5a"
44
45
  }
@@ -1,10 +1,26 @@
1
- import { EntityManager, Equal, getConnection, getManager, In, IsNull, Not } from 'typeorm'
1
+ import {
2
+ EntityManager,
3
+ Equal,
4
+ getConnection,
5
+ getManager,
6
+ In,
7
+ IsNull,
8
+ Not
9
+ } from 'typeorm'
2
10
 
3
11
  import { ApplicationType } from '@things-factory/auth-base'
4
12
  import { Bizplace } from '@things-factory/biz-base'
13
+ import { logger } from '@things-factory/env'
5
14
  import { generateId } from '@things-factory/id-rule-base'
6
- import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
7
- import { Product, ProductBarcode, ProductDetail } from '@things-factory/product-base'
15
+ import {
16
+ Sellercraft,
17
+ SellercraftStatus
18
+ } from '@things-factory/integration-sellercraft'
19
+ import {
20
+ Product,
21
+ ProductBarcode,
22
+ ProductDetail
23
+ } from '@things-factory/product-base'
8
24
  import {
9
25
  GenerateBatchPickInfo,
10
26
  ORDER_INVENTORY_STATUS,
@@ -17,8 +33,7 @@ import {
17
33
  OrderToteItem,
18
34
  OrderToteSeal,
19
35
  OrderVas,
20
- ReleaseGood,
21
- Replenishment
36
+ ReleaseGood
22
37
  } from '@things-factory/sales-base'
23
38
  import { Setting } from '@things-factory/setting-base'
24
39
  import {
@@ -35,12 +50,22 @@ import {
35
50
  TOTE_STATUS
36
51
  } from '@things-factory/warehouse-base'
37
52
 
38
- import { TASK_NUMBER_RULE_TYPE, TASK_NUMBER_SETTING_KEY, WORKSHEET_STATUS, WORKSHEET_TYPE } from '../../constants'
53
+ import {
54
+ TASK_NUMBER_RULE_TYPE,
55
+ TASK_NUMBER_SETTING_KEY,
56
+ WORKSHEET_STATUS,
57
+ WORKSHEET_TYPE
58
+ } from '../../constants'
39
59
  import { SellercraftController } from '../../controllers'
40
- import { Worksheet, WorksheetDetail } from '../../entities'
41
- import { isInventoryObsolete, WorksheetNoGenerator } from '../../utils'
60
+ import {
61
+ Worksheet,
62
+ WorksheetDetail
63
+ } from '../../entities'
64
+ import {
65
+ isInventoryObsolete,
66
+ WorksheetNoGenerator
67
+ } from '../../utils'
42
68
  import { VasWorksheetController } from '../vas/vas-worksheet-controller'
43
- import { logger } from '@things-factory/env'
44
69
 
45
70
  export class PickingWorksheetController extends VasWorksheetController {
46
71
  async generatePickingWorksheet(releaseGoodNo: string, currentStatus: string = null): Promise<Worksheet> {
@@ -1,6 +1,13 @@
1
- import { In, IsNull } from 'typeorm'
1
+ import {
2
+ In,
3
+ IsNull
4
+ } from 'typeorm'
2
5
 
3
- import { Product, ProductDetail, ProductBarcode } from '@things-factory/product-base'
6
+ import {
7
+ Product,
8
+ ProductBarcode,
9
+ ProductDetail
10
+ } from '@things-factory/product-base'
4
11
  import {
5
12
  ORDER_INVENTORY_STATUS,
6
13
  ORDER_STATUS,
@@ -21,8 +28,14 @@ import {
21
28
  TOTE_STATUS
22
29
  } from '@things-factory/warehouse-base'
23
30
 
24
- import { WORKSHEET_STATUS, WORKSHEET_TYPE } from '../../constants'
25
- import { Worksheet, WorksheetDetail } from '../../entities'
31
+ import {
32
+ WORKSHEET_STATUS,
33
+ WORKSHEET_TYPE
34
+ } from '../../constants'
35
+ import {
36
+ Worksheet,
37
+ WorksheetDetail
38
+ } from '../../entities'
26
39
  import { VasWorksheetController } from '../vas/vas-worksheet-controller'
27
40
 
28
41
  export class SortingWorksheetController extends VasWorksheetController {
@@ -227,7 +240,7 @@ export class SortingWorksheetController extends VasWorksheetController {
227
240
  await this.updateOrderTargets([matchingOI])
228
241
  await this.transactionInventory(inventory, releaseGood, 0, 0, INVENTORY_TRANSACTION_TYPE.SORTING)
229
242
 
230
- let worksheetDetail = worksheetDetails.find(wd => (wd.targetInventory.id = matchingOI.id))
243
+ let worksheetDetail = worksheetDetails.find(wd => (wd.targetInventory.id == matchingOI.id))
231
244
  await this.trxMgr
232
245
  .getRepository(WorksheetDetail)
233
246
  .update({ id: worksheetDetail.id }, { status: WORKSHEET_STATUS.DONE, updater: this.user })
@@ -1,9 +1,25 @@
1
- import { EntityManager, EntitySchema, Equal, FindOneOptions, In, Not, getRepository } from 'typeorm'
1
+ import {
2
+ EntityManager,
3
+ EntitySchema,
4
+ Equal,
5
+ FindOneOptions,
6
+ getRepository,
7
+ In,
8
+ Not
9
+ } from 'typeorm'
2
10
 
3
- import { Role, User } from '@things-factory/auth-base'
4
- import { Bizplace, getDomainUsers } from '@things-factory/biz-base'
5
- import { sendNotification } from '@things-factory/notification'
6
- import { Product, ProductDetail } from '@things-factory/product-base'
11
+ import {
12
+ Role,
13
+ User
14
+ } from '@things-factory/auth-base'
15
+ import {
16
+ Bizplace,
17
+ getDomainUsers
18
+ } from '@things-factory/biz-base'
19
+ import {
20
+ Product,
21
+ ProductDetail
22
+ } from '@things-factory/product-base'
7
23
  import {
8
24
  ArrivalNotice,
9
25
  DeliveryOrder,
@@ -15,21 +31,27 @@ import {
15
31
  OrderProduct,
16
32
  OrderVas,
17
33
  ReleaseGood,
34
+ Replenishment,
18
35
  ReturnOrder,
19
- VasOrder,
20
- Replenishment
36
+ VasOrder
21
37
  } from '@things-factory/sales-base'
22
38
  import { Domain } from '@things-factory/shell'
23
39
  import {
40
+ generateInventoryHistory,
24
41
  Inventory,
25
42
  INVENTORY_STATUS,
26
43
  InventoryItem,
27
- Pallet,
28
- generateInventoryHistory
44
+ Pallet
29
45
  } from '@things-factory/warehouse-base'
30
46
 
31
- import { WORKSHEET_STATUS, WORKSHEET_TYPE } from '../constants'
32
- import { Worksheet, WorksheetDetail } from '../entities'
47
+ import {
48
+ WORKSHEET_STATUS,
49
+ WORKSHEET_TYPE
50
+ } from '../constants'
51
+ import {
52
+ Worksheet,
53
+ WorksheetDetail
54
+ } from '../entities'
33
55
  import { WorksheetNoGenerator } from '../utils'
34
56
 
35
57
  export type ReferenceOrderType =
@@ -1101,7 +1123,7 @@ export class WorksheetController {
1101
1123
  union all
1102
1124
  select pd.product_id as "productId", pd.id, pd.packing_size as "packingSize",
1103
1125
  pd.packing_type as "packingType", pd.uom,
1104
- dt1.qty * pd.packing_size * pd.uom_value as "uomValue", dt1.qty * pd.packing_size as "qty", pb.gtin
1126
+ dt1.qty * pd.uom_value as "uomValue", dt1.qty * pd.packing_size as "qty", pb.gtin
1105
1127
  from product_details pd
1106
1128
  inner join product_barcodes pb on pb.product_detail_id = pd.id
1107
1129
  inner join cte dt1 on dt1.id = pd.child_product_detail_id
@@ -246,7 +246,8 @@ export const confirmCancellationReleaseOrder = {
246
246
  pickupAccountId: releaseGood.lastMileDelivery?.pickupAccountId,
247
247
  token: releaseGood.lastMileDelivery?.accessToken,
248
248
  parcelId: op.parcelId,
249
- warehouse: domain?.name
249
+ warehouse: domain?.name,
250
+ staging: releaseGood.lastMileDelivery?.staging
250
251
  })
251
252
  if (!res?.status) throw new Error('cannot cancel consignment note')
252
253
  }
@@ -27,6 +27,10 @@ import { Worksheet } from '../../../entities'
27
27
 
28
28
  export const packingWorksheetResolver = {
29
29
  async packingWorksheet(_: any, { releaseGoodNo }, context: any) {
30
+ // Performance tracker
31
+ let timeflag = releaseGoodNo
32
+ console.time(`packingWorksheet(${timeflag})`)
33
+
30
34
  try {
31
35
  const { tx, domain, user }: { tx: EntityManager; domain: Domain; user: User } = context.state
32
36
  let releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
@@ -42,19 +46,23 @@ export const packingWorksheetResolver = {
42
46
 
43
47
  // Find Release Order based on Bin
44
48
  if (!releaseGood) {
49
+ console.time(`packingWorksheet-findingBin(${timeflag})`)
45
50
  binLocation = await tx.getRepository(Location).findOne({
46
51
  where: { domain, name: releaseGoodNo }
47
52
  })
48
53
 
49
54
  if (binLocation) {
55
+
56
+ // find unique release goods id based on READY_TO_PACK, PACKING, PACKED order inventories within bins
50
57
  const qb: SelectQueryBuilder<OrderInventory> = tx
51
58
  .getRepository(OrderInventory)
52
59
  .createQueryBuilder('orderInventory')
53
60
 
54
- qb.innerJoinAndSelect('orderInventory.releaseGood', 'releaseGood')
55
- .innerJoinAndSelect('releaseGood.bizplace', 'bizplace')
56
- .innerJoinAndSelect('bizplace.domain', 'domain')
57
- .innerJoinAndSelect(
61
+ qb.select('releaseGood.id', 'id')
62
+ .innerJoin('orderInventory.releaseGood', 'releaseGood')
63
+ .innerJoin('releaseGood.bizplace', 'bizplace')
64
+ .innerJoin('bizplace.domain', 'domain')
65
+ .innerJoin(
58
66
  'worksheets',
59
67
  'ws',
60
68
  `orderInventory.release_good_id = ws.release_good_id AND ws.type = 'PACKING' AND (ws.status = 'EXECUTING' OR ws.status = 'DEACTIVATED')`
@@ -71,54 +79,37 @@ export const packingWorksheetResolver = {
71
79
  .andWhere('releaseGood.status IN (:...status)', {
72
80
  status: [ORDER_STATUS.READY_TO_PACK, ORDER_STATUS.PACKING]
73
81
  })
74
- .orderBy(`case when orderInventory.status = '${ORDER_INVENTORY_STATUS.PACKED}' then 1 end`)
75
- .addOrderBy('ws.created_at', 'ASC')
76
-
77
- const [orderInventoriesByBin, totalInventoriesByBin] = await qb.getManyAndCount()
78
- // @oscarchuaweiwen-fsd @chrislim I suggest using getMany() instead since the total is redundant here
79
-
80
- // remove duplicated count of release good
81
- const filteredRO = [
82
- ...new Set(
83
- orderInventoriesByBin.map(oiBin => {
84
- return oiBin.releaseGood.id
85
- })
86
- )
87
- ]
88
-
89
- if (orderInventoriesByBin.length > 0) {
90
- if (filteredRO?.length > 1) {
91
- await Promise.all(
92
- filteredRO.map(async roId => {
93
- let orderPackages: OrderPackage[] = await tx
94
- .getRepository(OrderPackage)
95
- .createQueryBuilder('opk')
96
- .innerJoinAndSelect('opk.releaseGood', 'rg')
97
- .innerJoinAndSelect('rg.bizplace', 'bizplace')
98
- .innerJoinAndSelect('bizplace.domain', 'domain')
99
- .innerJoinAndSelect('rg.orderInventories', 'oi')
100
- .where('rg.id = :releaseGoodId', { releaseGoodId: roId })
101
- .getMany()
102
-
103
- if (orderPackages?.length) {
104
- releaseGood = orderPackages[0].releaseGood
105
- }
106
- })
107
- )
108
- releaseGoodNo = releaseGood?.name
109
- isBatchPicking = releaseGood?.orderInventories[0]?.refWorksheetId ? true : false
110
- } else {
111
- releaseGoodNo = orderInventoriesByBin[0].releaseGood.name
112
- releaseGood = orderInventoriesByBin[0].releaseGood
113
- isBatchPicking = orderInventoriesByBin[0]?.refWorksheetId ? true : false
82
+ .orderBy('ws.created_at', 'ASC')
83
+ .addOrderBy('releaseGood.name', 'ASC')
84
+ .groupBy('ws.created_at')
85
+ .addGroupBy('releaseGood.name')
86
+ .addGroupBy('releaseGood.id')
87
+
88
+ const orderInventoriesByBin = await qb.getRawMany()
89
+ const totalInventoriesByBin = orderInventoriesByBin.length
90
+
91
+ if (totalInventoriesByBin > 0) {
92
+ //find first release good of order inventory within the bin
93
+ releaseGood = await tx.getRepository(ReleaseGood).findOne({
94
+ where: { id: orderInventoriesByBin[0].id },
95
+ relations: ['bizplace', 'bizplace.domain', 'orderInventories']
96
+ })
97
+
98
+ //query worksheet to find does the order associated with batch picking
99
+ let worksheet: Worksheet = null
100
+ if (releaseGood.orderInventories[0].refWorksheetId) {
101
+ worksheet = await tx.getRepository(Worksheet).findOne({ where: { id: releaseGood.orderInventories[0].refWorksheetId, type: WORKSHEET_TYPE.BATCH_PICKING } })
114
102
  }
115
103
 
116
- releaseGood.totalInventoriesByBin = filteredRO.length
104
+ releaseGoodNo = releaseGood.name
105
+ isBatchPicking = worksheet ? true : false
106
+ releaseGood.totalInventoriesByBin = totalInventoriesByBin
117
107
  releaseGood.isOrderInventoryBin = true
118
108
  } else {
119
109
  throw new Error(`Bin do not have any existing order.`)
120
110
  }
121
111
  }
112
+ console.timeEnd(`packingWorksheet-findingBin(${timeflag})`)
122
113
  }
123
114
 
124
115
  if (!releaseGood) throw new Error(`Release good do not exist.`)
@@ -263,6 +254,7 @@ export const packingWorksheetResolver = {
263
254
  let processingOrderPackage: OrderPackage[] =
264
255
  orderPackages.filter(opk => opk.status == ORDER_STATUS.PROCESSING) || []
265
256
 
257
+ console.timeEnd(`packingWorksheet(${timeflag})`)
266
258
  return {
267
259
  worksheetInfo: {
268
260
  releaseGood,
@@ -321,6 +313,7 @@ export const packingWorksheetResolver = {
321
313
  })
322
314
  }
323
315
  } catch (error) {
316
+ console.timeEnd(`packingWorksheet(${timeflag})`)
324
317
  console.error(error)
325
318
  throw error
326
319
  }
@@ -1,4 +1,4 @@
1
- import { EntityManager, getManager, In } from 'typeorm'
1
+ import { EntityManager, getManager, In, getConnection } from 'typeorm'
2
2
 
3
3
  import { ApplicationType, User } from '@things-factory/auth-base'
4
4
  import { Bizplace, getMyBizplace } from '@things-factory/biz-base'
@@ -16,6 +16,7 @@ import {
16
16
  import { Setting } from '@things-factory/setting-base'
17
17
  import { Domain } from '@things-factory/shell'
18
18
  import { Inventory } from '@things-factory/warehouse-base'
19
+ import { logger } from '@things-factory/env'
19
20
 
20
21
  import {
21
22
  PackingWorksheetController,
@@ -190,8 +191,39 @@ export async function completeBatchPicking(
190
191
  }
191
192
 
192
193
  // trigger LMD API to create parcel
193
- if (foundReleaseGood?.orderPackages?.length && foundReleaseGood.lmdOption && foundReleaseGood.lastMileDelivery && foundReleaseGood?.orderPackages?.some(op => op.parcelId == null)){
194
+ if (
195
+ foundReleaseGood?.orderPackages?.length &&
196
+ foundReleaseGood.lmdOption &&
197
+ foundReleaseGood.lastMileDelivery &&
198
+ foundReleaseGood?.orderPackages?.some(op => op.parcelId == null)
199
+ ) {
194
200
  await createLmdParcel([foundReleaseGood], tx)
201
+ // trigger RTS
202
+ try {
203
+ const orderSource: string = foundReleaseGood.source
204
+
205
+ if (orderSource == ApplicationType.SELLERCRAFT) {
206
+ const sellercraft: Sellercraft = await tx.getRepository(Sellercraft).findOne({
207
+ domain: foundReleaseGood.bizplace.domain,
208
+ status: SellercraftStatus.ACTIVE
209
+ })
210
+ if (sellercraft) {
211
+ await getConnection().transaction(async tx => {
212
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, null, null)
213
+ const rtsTriggerLevel: Setting = await tx.getRepository(Setting).findOne({
214
+ where: { domain: foundReleaseGood.domain, category: 'id-rule', name: 'rts-trigger-level' }
215
+ })
216
+
217
+ if (rtsTriggerLevel && parseInt(rtsTriggerLevel?.value || 0) == 1) {
218
+ await sellercraftCtrl.initiateOrderShipment(sellercraft, foundReleaseGood)
219
+ }
220
+ })
221
+ }
222
+ }
223
+ } catch (e) {
224
+ logger.error(`[get-lmd-awb-sof-trigger]: ${e}`)
225
+ }
226
+ //
195
227
  }
196
228
  //
197
229
  }
@@ -1,4 +1,4 @@
1
- import { EntityManager, getManager, In } from 'typeorm'
1
+ import { EntityManager, getManager, In, getConnection } from 'typeorm'
2
2
 
3
3
  import { ApplicationType, User } from '@things-factory/auth-base'
4
4
  import { Bizplace, ContactPoint, getMyBizplace } from '@things-factory/biz-base'
@@ -460,6 +460,32 @@ export async function completePicking(
460
460
  releaseGood?.orderPackages?.some(op => op.parcelId == null)
461
461
  ) {
462
462
  await createLmdParcel([releaseGood], tx)
463
+ // trigger RTS
464
+ try {
465
+ const orderSource: string = releaseGood.source
466
+
467
+ if (orderSource == ApplicationType.SELLERCRAFT) {
468
+ const sellercraft: Sellercraft = await tx.getRepository(Sellercraft).findOne({
469
+ domain: releaseGood.bizplace.domain,
470
+ status: SellercraftStatus.ACTIVE
471
+ })
472
+ if (sellercraft) {
473
+ await getConnection().transaction(async tx => {
474
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, null, null)
475
+ const rtsTriggerLevel: Setting = await tx.getRepository(Setting).findOne({
476
+ where: { domain: releaseGood.domain, category: 'id-rule', name: 'rts-trigger-level' }
477
+ })
478
+
479
+ if (rtsTriggerLevel && parseInt(rtsTriggerLevel?.value || 0) == 1) {
480
+ await sellercraftCtrl.initiateOrderShipment(sellercraft, releaseGood)
481
+ }
482
+ })
483
+ }
484
+ }
485
+ } catch (e) {
486
+ logger.error(`[get-lmd-awb-sof-trigger]: ${e}`)
487
+ }
488
+ //
463
489
  }
464
490
  //
465
491
  } else {
@@ -1,14 +1,29 @@
1
+ import { getRepository, getConnection, SelectQueryBuilder } from 'typeorm'
2
+ import { v4 as uuidv4 } from 'uuid'
3
+
1
4
  import { LastMileAPI, LastMileDelivery } from '@things-factory/integration-lmd'
2
5
  import { logger } from '@things-factory/env'
3
6
  import { GeoArea } from '@things-factory/geography'
4
7
  import { Bizplace } from '@things-factory/biz-base'
5
8
  import { ReleaseGood, OrderPackage } from '@things-factory/sales-base'
9
+ import { Board } from '@things-factory/board-service'
10
+ import { headlessModel, pdf } from '@things-factory/board-service'
11
+ import { Attachment, uploadAwb } from '@things-factory/attachment-base'
12
+ import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
13
+ import { ApplicationType } from '@things-factory/auth-base'
14
+ import { Setting } from '@things-factory/setting-base'
15
+
16
+ // import { SellercraftController } from '../controllers'
17
+
18
+ const LMD_BOARDS = {
19
+ // dhl: 'operato-lmd-dhl-awb',
20
+ airspeed: 'operato-lmd-airspeed-awb'
21
+ }
6
22
 
7
23
  export async function createLmdParcel(releaseGoods, tx) {
8
24
  try {
9
25
  let parcelsRequest = []
10
26
  for (let releaseGood of releaseGoods) {
11
-
12
27
  const lmd: LastMileDelivery = releaseGood.lastMileDelivery
13
28
 
14
29
  const bizplace: Bizplace = await tx.getRepository(Bizplace).findOne({
@@ -27,6 +42,7 @@ export async function createLmdParcel(releaseGoods, tx) {
27
42
  pickupAccountId: lmd?.pickupAccountId,
28
43
  soldToAccountId: lmd?.soldToAccountId,
29
44
  token: lmd?.accessToken,
45
+ staging: lmd?.staging,
30
46
 
31
47
  recipient: releaseGood.attentionTo,
32
48
  phone: releaseGood.phone1,
@@ -46,6 +62,8 @@ export async function createLmdParcel(releaseGoods, tx) {
46
62
  pDistrict: senderGeoArea.cityName,
47
63
  pState: senderGeoArea.stateName,
48
64
  pCountry: senderGeoArea.countryCode,
65
+
66
+ handoverMethod: 1
49
67
  }
50
68
 
51
69
  let unprocessedOrderPackages: OrderPackage[] = releaseGood.orderPackages.filter(op => !op.parcelId)
@@ -79,8 +97,175 @@ export async function createLmdParcel(releaseGoods, tx) {
79
97
  for (let parcel of res) {
80
98
  await tx.getRepository(OrderPackage).update({ id: parcel.refNo }, { parcelId: parcel.parcelId })
81
99
  }
100
+
101
+ const qb: SelectQueryBuilder<OrderPackage> = tx.getRepository(OrderPackage)
102
+ .createQueryBuilder('op')
103
+ .innerJoinAndSelect('op.releaseGood', 'rg')
104
+ .innerJoinAndSelect('rg.domain', 'd')
105
+ .innerJoinAndSelect('rg.bizplace', 'b')
106
+ .innerJoinAndSelect('b.domain', 'd2')
107
+ .leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
108
+ .where('op.id IN (:...orderPackageIds)', { orderPackageIds: res.map(parcel => { return parcel.refNo }) })
109
+
110
+ const orderPackages: OrderPackage[] = await qb.getMany()
111
+ await processAwbAndTrackingNo(orderPackages, tx)
82
112
  }
83
113
  } catch (e) {
84
114
  logger.error(`[lmd-create-parcel]: ${e}`)
85
115
  }
86
116
  }
117
+
118
+ export async function processAwbAndTrackingNo(orderPackages, tx) {
119
+ let releaseOrders: ReleaseGood = []
120
+
121
+ for (let orderPackage of orderPackages) {
122
+ // await getConnection().transaction(async tx => {
123
+ try {
124
+ let awbRes = await LastMileAPI.getAwb(
125
+ { ...orderPackage.releaseGood.lastMileDelivery, platform: 'lmdMiddleware' },
126
+ { parcelId: orderPackage.parcelId, staging: orderPackage.releaseGood.lastMileDelivery?.staging }
127
+ )
128
+
129
+ if (awbRes) {
130
+ let awbContent: any
131
+ const { deliveryConfirmationNo, tracking_no } = awbRes
132
+ if (awbRes.content) {
133
+ awbContent = Buffer.from(awbRes.content, 'base64')
134
+ } else {
135
+ awbContent = await constructAwb(orderPackage, awbRes)
136
+ }
137
+ const awb = await uploadAwbToS3(awbContent, orderPackage)
138
+ await tx.getRepository(OrderPackage).update(
139
+ { id: orderPackage.id },
140
+ { airwayBill: awb.url, trackingNo: deliveryConfirmationNo || tracking_no }
141
+ )
142
+ releaseOrders.push(orderPackage.releaseGood)
143
+ }
144
+ } catch (e) {
145
+ logger.error(e)
146
+ }
147
+ // })
148
+ }
149
+
150
+ // trigger RTS
151
+ // try {
152
+ // let uniqueReleaseOrders: ReleaseGood = [...new Map(releaseOrders.map(item => [item['id'], item])).values()]
153
+
154
+ // for (let releaseOrder of uniqueReleaseOrders) {
155
+ // const orderSource: string = releaseOrder.source
156
+
157
+ // if (orderSource == ApplicationType.SELLERCRAFT) {
158
+ // const sellercraft: Sellercraft = await getRepository(Sellercraft).findOne({
159
+ // domain: releaseOrder.bizplace.domain,
160
+ // status: SellercraftStatus.ACTIVE
161
+ // })
162
+ // if (sellercraft) {
163
+ // await getConnection().transaction(async tx => {
164
+ // const sellercraftCtrl: SellercraftController = new SellercraftController(tx, null, null)
165
+ // const rtsTriggerLevel: Setting = await tx.getRepository(Setting).findOne({
166
+ // where: { domain: releaseOrder.domain, category: 'id-rule', name: 'rts-trigger-level' }
167
+ // })
168
+
169
+ // if (rtsTriggerLevel && parseInt(rtsTriggerLevel?.value || 0) == 1) {
170
+ // await sellercraftCtrl.initiateOrderShipment(sellercraft, releaseOrder)
171
+ // }
172
+ // })
173
+ // }
174
+ // }
175
+ // }
176
+ // } catch (e) {
177
+ // logger.error(`[get-lmd-awb-sof-trigger]: ${e}`)
178
+ // }
179
+ //
180
+ }
181
+
182
+ async function constructAwb(orderPackage, awbRes) {
183
+ const releaseOrder = orderPackage.releaseOrder
184
+ let {
185
+ shipmentID,
186
+ deliveryConfirmationNo,
187
+ deliveryDepotCode,
188
+ primarySortCode,
189
+ secondarySortCode,
190
+ recipient,
191
+ sender,
192
+ tracking_no,
193
+ courier
194
+ } = awbRes
195
+
196
+ let senderFullAddress = `${sender?.address}${sender?.address2 ? ', ' + sender?.address2 : ''}${
197
+ sender?.district ? ', ' + sender?.district : ''
198
+ }${sender?.city ? ', ' + sender?.city : ''}${sender?.postcode ? ', ' + sender?.postcode : ''}${
199
+ sender?.state ? ', ' + sender?.state : ''
200
+ }`
201
+
202
+ let recipientAddress1 = `${recipient?.address}${recipient?.address2 ? ', ' + recipient?.address2 : ''}${
203
+ recipient?.district ? ', ' + recipient?.district : ''
204
+ }`
205
+
206
+ let recipientAddress2 = `${recipient?.state}${recipient?.city ? ', ' + recipient?.city : ''}${
207
+ recipient?.postcode ? ', ' + recipient?.postcode : ''
208
+ }`
209
+
210
+ let data = {
211
+ senderFullAddress,
212
+ senderName: sender?.name,
213
+ recipientAddress1,
214
+ recipientAddress2,
215
+ recipientPhone: recipient?.phone,
216
+ recipientName: recipient?.name,
217
+ refId: shipmentID,
218
+ deliveryConfirmationNo: deliveryConfirmationNo || tracking_no,
219
+ deliveryDepotCode: deliveryDepotCode,
220
+ primarySortCode: primarySortCode,
221
+ secondarySortCode: secondarySortCode,
222
+ orderNo: releaseOrder?.refNo
223
+ }
224
+
225
+ let lmdBoard: Board = await getRepository(Board).findOne({
226
+ where: { name: LMD_BOARDS[courier] },
227
+ relations: ['domain']
228
+ })
229
+
230
+ if (!lmdBoard) throw new Error(`[get-lmd-awb] board is not found (RO: ${releaseOrder.name})`)
231
+
232
+ const { model } = await headlessModel({ domain: lmdBoard.domain, id: lmdBoard.id })
233
+
234
+ const result = await pdf({
235
+ id: lmdBoard.id,
236
+ model,
237
+ data,
238
+ options: {
239
+ format: 'A4'
240
+ },
241
+ context: { state: { domain: lmdBoard.domain } }
242
+ })
243
+
244
+ return result
245
+ }
246
+
247
+ async function uploadAwbToS3(result, orderPackage) {
248
+ const releaseOrder = orderPackage.releaseGood
249
+ const uuid = uuidv4()
250
+ const awb: any = await uploadAwb({
251
+ content: result,
252
+ title: uuid
253
+ })
254
+
255
+ if (!awb?.url) throw new Error(`[get-lmd-awb] awb was not uploaded for (RO: ${releaseOrder.name})`)
256
+
257
+ // create attachment
258
+
259
+ await getRepository(Attachment).insert({
260
+ name: uuid,
261
+ mimetype: 'application/pdf',
262
+ category: 'LMD_AWB',
263
+ refBy: orderPackage.id,
264
+ path: awb.url,
265
+ domain: releaseOrder.domain,
266
+ size: 0,
267
+ encoding: ''
268
+ })
269
+
270
+ return awb
271
+ }