@things-factory/sales-base 4.0.21 → 4.0.25

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 (44) hide show
  1. package/dist-server/controllers/ecommerce/ecommerce-controller.js +7 -4
  2. package/dist-server/controllers/ecommerce/ecommerce-controller.js.map +1 -1
  3. package/dist-server/controllers/ecommerce/sellercraft-controller.js +59 -0
  4. package/dist-server/controllers/ecommerce/sellercraft-controller.js.map +1 -1
  5. package/dist-server/controllers/order-controller.js +40 -1
  6. package/dist-server/controllers/order-controller.js.map +1 -1
  7. package/dist-server/service/arrival-notice/arrival-notice-mutation.js +180 -0
  8. package/dist-server/service/arrival-notice/arrival-notice-mutation.js.map +1 -1
  9. package/dist-server/service/arrival-notice/arrival-notice-query.js +193 -1
  10. package/dist-server/service/arrival-notice/arrival-notice-query.js.map +1 -1
  11. package/dist-server/service/arrival-notice/arrival-notice-types.js +160 -2
  12. package/dist-server/service/arrival-notice/arrival-notice-types.js.map +1 -1
  13. package/dist-server/service/delivery-order/delivery-order-types.js +1 -1
  14. package/dist-server/service/delivery-order/delivery-order-types.js.map +1 -1
  15. package/dist-server/service/goods-receival-note/goods-receival-note.js +5 -0
  16. package/dist-server/service/goods-receival-note/goods-receival-note.js.map +1 -1
  17. package/dist-server/service/order-inventory/order-inventory.js +22 -1
  18. package/dist-server/service/order-inventory/order-inventory.js.map +1 -1
  19. package/dist-server/service/release-good/release-good-mutation.js +242 -81
  20. package/dist-server/service/release-good/release-good-mutation.js.map +1 -1
  21. package/dist-server/service/release-good/release-good-types.js +8 -0
  22. package/dist-server/service/release-good/release-good-types.js.map +1 -1
  23. package/dist-server/service/release-good/release-good.js +4 -0
  24. package/dist-server/service/release-good/release-good.js.map +1 -1
  25. package/dist-server/service/return-order/return-order-mutation.js +1 -1
  26. package/dist-server/service/return-order/return-order-mutation.js.map +1 -1
  27. package/dist-server/utils/inventory-util.js +89 -1
  28. package/dist-server/utils/inventory-util.js.map +1 -1
  29. package/package.json +12 -12
  30. package/server/controllers/ecommerce/ecommerce-controller.ts +15 -6
  31. package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
  32. package/server/controllers/order-controller.ts +55 -2
  33. package/server/service/arrival-notice/arrival-notice-mutation.ts +237 -1
  34. package/server/service/arrival-notice/arrival-notice-query.ts +214 -4
  35. package/server/service/arrival-notice/arrival-notice-types.ts +120 -1
  36. package/server/service/delivery-order/delivery-order-types.ts +1 -1
  37. package/server/service/goods-receival-note/goods-receival-note.ts +4 -0
  38. package/server/service/order-inventory/order-inventory.ts +17 -1
  39. package/server/service/release-good/release-good-mutation.ts +310 -119
  40. package/server/service/release-good/release-good-types.ts +6 -0
  41. package/server/service/release-good/release-good.ts +3 -0
  42. package/server/service/return-order/return-order-mutation.ts +1 -1
  43. package/server/utils/index.ts +1 -1
  44. package/server/utils/inventory-util.ts +129 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/sales-base",
3
- "version": "4.0.21",
3
+ "version": "4.0.25",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -24,16 +24,16 @@
24
24
  "migration:create": "node ../../node_modules/typeorm/cli.js migration:create -d ./server/migrations"
25
25
  },
26
26
  "dependencies": {
27
- "@things-factory/attachment-base": "^4.0.21",
28
- "@things-factory/auth-base": "^4.0.21",
29
- "@things-factory/biz-base": "^4.0.21",
30
- "@things-factory/code-base": "^4.0.21",
31
- "@things-factory/notification": "^4.0.21",
32
- "@things-factory/product-base": "^4.0.21",
33
- "@things-factory/setting-base": "^4.0.21",
34
- "@things-factory/shell": "^4.0.21",
35
- "@things-factory/transport-base": "^4.0.21",
36
- "@things-factory/warehouse-base": "^4.0.21"
27
+ "@things-factory/attachment-base": "^4.0.25",
28
+ "@things-factory/auth-base": "^4.0.25",
29
+ "@things-factory/biz-base": "^4.0.25",
30
+ "@things-factory/code-base": "^4.0.25",
31
+ "@things-factory/notification": "^4.0.25",
32
+ "@things-factory/product-base": "^4.0.25",
33
+ "@things-factory/setting-base": "^4.0.25",
34
+ "@things-factory/shell": "^4.0.25",
35
+ "@things-factory/transport-base": "^4.0.25",
36
+ "@things-factory/warehouse-base": "^4.0.25"
37
37
  },
38
- "gitHead": "ab5451f749a4e886ae3668bee82777b566538e17"
38
+ "gitHead": "9c44141d651dab7b4a9c0f0b6fecb8d6937d116f"
39
39
  }
@@ -1,8 +1,11 @@
1
- import { Domain } from '@things-factory/shell'
2
- import { Product } from '@things-factory/product-base'
3
- import { Inventory } from '@things-factory/warehouse-base'
4
- import { MarketplaceStore, MarketplaceSetting, StoreAPI } from '@things-factory/integration-marketplace'
1
+ import { Equal, Not } from 'typeorm'
2
+
3
+ import { MarketplaceSetting, MarketplaceStore, StoreAPI } from '@things-factory/integration-marketplace'
5
4
  import { MarketplaceProductVariation } from '@things-factory/marketplace-base'
5
+ import { Product } from '@things-factory/product-base'
6
+ import { Domain } from '@things-factory/shell'
7
+ import { Inventory, INVENTORY_STATUS, LOCATION_TYPE } from '@things-factory/warehouse-base'
8
+
6
9
  import { OrderController } from '../order-controller'
7
10
 
8
11
  export class EcommerceController extends OrderController {
@@ -14,10 +17,16 @@ export class EcommerceController extends OrderController {
14
17
  const inventoryProducts: any[] = await Promise.all(
15
18
  productIds.map(async productId => {
16
19
  const product: Product = await this.trxMgr.getRepository(Product).findOne(productId)
17
- const inventories: Inventory[] = await this.trxMgr.getRepository(Inventory).find({
18
- where: { domain: this.domain, product }
20
+ let inventories: Inventory[] = await this.trxMgr.getRepository(Inventory).find({
21
+ where: { domain: this.domain, product, status: Not(Equal(INVENTORY_STATUS.TERMINATED)) },
22
+ relations: ['location']
19
23
  })
20
24
 
25
+ inventories = inventories.filter(
26
+ inventory =>
27
+ inventory?.location.type !== LOCATION_TYPE.QUARANTINE || inventory?.location.type !== LOCATION_TYPE.RESERVE
28
+ )
29
+
21
30
  const inventoryQty: number = inventories.reduce((total, currentValue) => {
22
31
  total += currentValue.qty
23
32
  return total
@@ -1,6 +1,10 @@
1
+ import { Equal, Not } from 'typeorm'
2
+
1
3
  import { Sellercraft, SellercraftAPI } from '@things-factory/integration-sellercraft'
2
- import { Product } from '@things-factory/product-base'
4
+ import { Product, ProductDetail } from '@things-factory/product-base'
5
+ import { Inventory, INVENTORY_STATUS } from '@things-factory/warehouse-base'
3
6
 
7
+ import { ORDER_PRODUCT_STATUS } from '../../constants'
4
8
  import { OrderProduct, ReleaseGood } from '../../service'
5
9
  import { OrderController } from '../order-controller'
6
10
 
@@ -31,6 +35,78 @@ export class SellercraftController extends OrderController {
31
35
  return releaseGood
32
36
  }
33
37
 
38
+ async updateSellercraftStock(sellercraft: Sellercraft, orderProducts: OrderProduct[], type: string): Promise<void> {
39
+ for (var i = 0; i < orderProducts.length; i++) {
40
+ const orderProduct: OrderProduct = orderProducts[i]
41
+
42
+ const product: Product = orderProduct.product
43
+ const pendingAssignOPs: OrderProduct[] = await this.trxMgr
44
+ .getRepository(OrderProduct)
45
+ .find({ where: { domain: this.domain, product, status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN } })
46
+
47
+ const productDetails: ProductDetail[] = product.productDetails
48
+
49
+ let totalUnassignReleaseQty: number = 0
50
+ if (pendingAssignOPs?.length) {
51
+ totalUnassignReleaseQty = pendingAssignOPs.reduce((total, currentValue) => {
52
+ total += currentValue.releaseQty
53
+ return total
54
+ }, 0)
55
+ }
56
+
57
+ const inventories: Inventory[] = await this.trxMgr.getRepository(Inventory).find({
58
+ where: { domain: this.domain, product, status: Not(Equal(INVENTORY_STATUS.TERMINATED)) }
59
+ })
60
+
61
+ let defaultProductDetail: ProductDetail
62
+ const sellercraftInvs: any[] = await Promise.all(
63
+ inventories.map(async (inventory: Inventory) => {
64
+ const inventoryPackingType: string = inventory.packingType
65
+ defaultProductDetail = product.productDetails.find(productDetail => productDetail.isDefault)
66
+
67
+ let packingSize: number = 1
68
+ if (inventoryPackingType !== defaultProductDetail.packingType) {
69
+ const unmatchingProductDetail: ProductDetail = product.productDetails.find(
70
+ productDetail => productDetail.packingType === inventoryPackingType
71
+ )
72
+
73
+ packingSize = await this.getChildPackingSize(productDetails, defaultProductDetail, unmatchingProductDetail)
74
+ if (packingSize == 0) packingSize = 1
75
+ }
76
+
77
+ return { quantity: inventory.qty * packingSize - inventory.lockedQty * packingSize }
78
+ })
79
+ )
80
+
81
+ let inventoryTotalQty: number = sellercraftInvs.reduce((total, currentValue) => {
82
+ total += currentValue.quantity
83
+ return total
84
+ }, 0)
85
+
86
+ switch (type) {
87
+ case 'CONFIRM_ORDER':
88
+ inventoryTotalQty -= totalUnassignReleaseQty
89
+ break
90
+ }
91
+
92
+ const sellercraftInv: any = {
93
+ accountId: sellercraft.accountId,
94
+ sku: product.sku,
95
+ uom: 'EA',
96
+ quantity: inventoryTotalQty,
97
+ gtin: defaultProductDetail.gtin,
98
+ weight: defaultProductDetail.nettWeight < 1 ? 1 : defaultProductDetail.nettWeight,
99
+ packageDimension: {
100
+ length: defaultProductDetail.depth < 1 ? 1 : defaultProductDetail.depth,
101
+ width: defaultProductDetail.width < 1 ? 1 : defaultProductDetail.width,
102
+ height: defaultProductDetail.height < 1 ? 1 : defaultProductDetail.height
103
+ }
104
+ }
105
+
106
+ await SellercraftAPI.updateProduct(sellercraft, { sellercraftInv })
107
+ }
108
+ }
109
+
34
110
  async checkExistingReleaseGood(sellercraft: Sellercraft, releaseGood: ReleaseGood): Promise<void> {
35
111
  const { refNo, refNo2 } = releaseGood
36
112
  const foundReleaseGood: ReleaseGood = await this.trxMgr.getRepository(ReleaseGood).findOne({
@@ -1,9 +1,12 @@
1
+ import { EntityManager, EntitySchema, FindOneOptions } from 'typeorm'
2
+
1
3
  import { Role, User } from '@things-factory/auth-base'
2
4
  import { Bizplace, getDomainUsers } from '@things-factory/biz-base'
3
5
  import { sendNotification } from '@things-factory/notification'
6
+ import { ProductDetail } from '@things-factory/product-base'
4
7
  import { Domain } from '@things-factory/shell'
5
8
  import { Inventory } from '@things-factory/warehouse-base'
6
- import { EntityManager, EntitySchema, FindOneOptions } from 'typeorm'
9
+
7
10
  import {
8
11
  ArrivalNotice,
9
12
  DeliveryOrder,
@@ -48,7 +51,9 @@ export type NotificationMsgInterface = {
48
51
  export class OrderController {
49
52
  public readonly ERROR_MSG = {
50
53
  FIND: {
51
- NO_RESULT: (condition: any) => `There's no results matched with condition ${condition}`
54
+ NO_RESULT: (condition: any) => `There's no results matched with condition ${condition}`,
55
+ NO_CHILD_RESULT: (condition: any) => `There's no child result matched with condition ${condition}`,
56
+ NOT_MATCH: (source: any, target: any) => `Unable to find matching ${target} using ${source}`
52
57
  },
53
58
  CREATE: {
54
59
  ID_EXISTS: 'Target has ID already',
@@ -217,6 +222,54 @@ export class OrderController {
217
222
  return await this.trxMgr.getRepository(Inventory).save(inventory)
218
223
  }
219
224
 
225
+ async getChildPackingSize(
226
+ productDetails: ProductDetail[],
227
+ defaultProductDetail: ProductDetail,
228
+ unmatchingProductDetail: ProductDetail
229
+ ): Promise<number> {
230
+ let hasChildRelation: boolean = Boolean(unmatchingProductDetail?.childProductDetail)
231
+ let hasMatchingChild: boolean
232
+ let packingSize: number = 1
233
+ let currentChildProductDetail: ProductDetail
234
+
235
+ if (hasChildRelation) {
236
+ currentChildProductDetail = productDetails.find(
237
+ (productDetail: ProductDetail) => productDetail.id === unmatchingProductDetail.childProductDetail.id
238
+ )
239
+
240
+ if (!currentChildProductDetail) {
241
+ throw new Error(this.ERROR_MSG.FIND.NOT_MATCH('packing type', `GTIN (${unmatchingProductDetail.gtin})`))
242
+ }
243
+
244
+ hasMatchingChild = Boolean(defaultProductDetail.id === currentChildProductDetail.id)
245
+ if (hasMatchingChild) {
246
+ packingSize = unmatchingProductDetail.packingSize
247
+ } else {
248
+ packingSize = unmatchingProductDetail.packingSize * packingSize
249
+ }
250
+ } else {
251
+ throw new Error(this.ERROR_MSG.FIND.NO_CHILD_RESULT(`${unmatchingProductDetail.gtin}`))
252
+ }
253
+
254
+ while (hasChildRelation && !hasMatchingChild) {
255
+ if (hasMatchingChild) {
256
+ packingSize = currentChildProductDetail.packingSize * packingSize // 12 x 10
257
+ } else if (!hasMatchingChild && hasChildRelation) {
258
+ packingSize = currentChildProductDetail.packingSize * packingSize // 12 x 10
259
+ currentChildProductDetail = productDetails.find(
260
+ (productDetail: ProductDetail) => productDetail.id === currentChildProductDetail.childProductDetail.id
261
+ )
262
+
263
+ hasChildRelation = Boolean(currentChildProductDetail?.childProductDetail)
264
+ hasMatchingChild = Boolean(defaultProductDetail.id === currentChildProductDetail.id)
265
+ } else if (!hasChildRelation && !hasMatchingChild) {
266
+ throw new Error(this.ERROR_MSG.FIND.NO_RESULT(unmatchingProductDetail.gtin))
267
+ }
268
+ }
269
+
270
+ return packingSize
271
+ }
272
+
220
273
  /**
221
274
  * @summary set common stamp like domain, creator, updater
222
275
  * @description Set common stamp to passed record
@@ -1,6 +1,6 @@
1
1
  import { FileUpload, GraphQLUpload } from 'graphql-upload'
2
2
  import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
3
- import { EntityManager, getRepository, In, Repository } from 'typeorm'
3
+ import { EntityManager, getConnection, getRepository, In, Repository } from 'typeorm'
4
4
 
5
5
  import { Attachment, createAttachments } from '@things-factory/attachment-base'
6
6
  import { Role, User } from '@things-factory/auth-base'
@@ -38,10 +38,13 @@ import {
38
38
  ORDER_VAS_STATUS
39
39
  } from '../../constants'
40
40
  import { ATTACHMENT_TYPE } from '../../constants/attachment-type'
41
+ import { ValidationError } from '../../errors'
41
42
  import { OrderNoGenerator } from '../../utils/order-no-generator'
42
43
  import { generateJobSheet } from '../job-sheet/job-sheet-mutation'
43
44
  import { confirmReleaseGood, deleteReleaseGood, rejectReleaseGood } from '../release-good/release-good-mutation'
45
+ import { validateBulkArrivalNoticesFunction } from './'
44
46
  import { ArrivalNotice } from './arrival-notice'
47
+ import { ArrivalNoticeList, RawArrivalNotice } from './arrival-notice-types'
45
48
 
46
49
  const debug = require('debug')('things-factory:operato-wms:addArrivalNotice')
47
50
 
@@ -230,6 +233,139 @@ export class ArrivalNoticeMutation {
230
233
  await tx.getRepository(ArrivalNotice).update(existingArrivalNotice.id, arrivalNotice)
231
234
  return await tx.getRepository(ArrivalNotice).findOne(existingArrivalNotice.id)
232
235
  }
236
+
237
+ @Directive('@privilege(category: "order_customer", privilege: "mutation")')
238
+ @Directive('@transaction')
239
+ @Mutation(returns => ArrivalNoticeList)
240
+ async bulkGenerateArrivalNotices(
241
+ @Ctx() context: any,
242
+ @Arg('rawArrivalNotices', type => [NewArrivalNotice], { nullable: true }) rawArrivalNotices: NewArrivalNotice[],
243
+ @Arg('bizplaceId', type => String) bizplaceId: string
244
+ ): Promise<ArrivalNoticeList> {
245
+ const { domain, user, tx }: { domain: Domain; user: User; tx: EntityManager } = context.state
246
+ let createdArrivalNoticeIds: string[] = [],
247
+ createdOrderProductIds: string[] = [],
248
+ errorsCaught: any[] = [],
249
+ finalArrivalNoticeList: ArrivalNotice[] = []
250
+
251
+ const foundBizplace: Bizplace = await tx.getRepository(Bizplace).findOne(bizplaceId)
252
+
253
+ if (!foundBizplace) throw new Error('company not found')
254
+
255
+ // to check whether the raw data are valid
256
+ const validatedArrivalNotices: RawArrivalNotice[] = await validateBulkArrivalNoticesFunction(
257
+ rawArrivalNotices,
258
+ bizplaceId,
259
+ tx
260
+ )
261
+
262
+ // check for custom GAN order no setting
263
+ const ganNoSetting: Setting = await tx.getRepository(Setting).findOne({
264
+ where: {
265
+ domain,
266
+ name: ORDER_NUMBER_SETTING_KEY.GAN_NUMBER_RULE
267
+ }
268
+ })
269
+
270
+ // extract rawArrivalNotices data into normal ArrivalNotice format
271
+ let arrivalNotices: Partial<ArrivalNotice[]> = extractArrivalNotices(validatedArrivalNotices)
272
+
273
+ for (let i = 0, l = arrivalNotices.length; i < l; i++) {
274
+ try {
275
+ await getConnection().transaction(async childTx => {
276
+ // check for duplication
277
+ const duplicatedArrivalNotice: ArrivalNotice = await childTx.getRepository(ArrivalNotice).findOne({
278
+ where: {
279
+ refNo: arrivalNotices[i].refNo,
280
+ refNo2: arrivalNotices[i].refNo2,
281
+ refNo3: arrivalNotices[i].refNo3,
282
+ etaDate: arrivalNotices[i].etaDate,
283
+ truckNo: arrivalNotices[i].truckNo,
284
+ containerNo: arrivalNotices[i].containerNo,
285
+ containerSize: arrivalNotices[i].containerSize,
286
+ looseItem: arrivalNotices[i].looseItem,
287
+ importCargo: arrivalNotices[i].importCargo
288
+ }
289
+ })
290
+
291
+ if (duplicatedArrivalNotice) {
292
+ throw new Error(`duplicated with ${duplicatedArrivalNotice.name}`)
293
+ }
294
+
295
+ // save new arrival notice
296
+ const createdArrivalNotice: ArrivalNotice = await childTx.getRepository(ArrivalNotice).save({
297
+ ...arrivalNotices[i],
298
+ name: ganNoSetting
299
+ ? await generateId({ domain, type: ORDER_NUMBER_RULE_TYPE.GAN_NUMBER, seed: {} })
300
+ : OrderNoGenerator.arrivalNotice(),
301
+ domain,
302
+ bizplace: foundBizplace,
303
+ status: ORDER_STATUS.PENDING,
304
+ creator: user,
305
+ updater: user
306
+ })
307
+
308
+ // save new order products
309
+ let createdOrderProducts: OrderProduct[] = await Promise.all(
310
+ arrivalNotices[i].orderProducts.map(async (op: OrderProduct) => {
311
+ return {
312
+ ...op,
313
+ domain,
314
+ bizplace: foundBizplace,
315
+ name: OrderNoGenerator.orderProduct(),
316
+ // product: await childTx.getRepository(Product).findOne(op.product.id),
317
+ status: ORDER_PRODUCT_STATUS.PENDING,
318
+ arrivalNotice: createdArrivalNotice,
319
+ creator: user
320
+ }
321
+ })
322
+ )
323
+ createdOrderProducts = await childTx.getRepository(OrderProduct).save(createdOrderProducts)
324
+
325
+ // generate new jobsheet
326
+ await generateJobSheet(domain, user, createdArrivalNotice, childTx)
327
+
328
+ // push arrivalNotice and orderProducts IDs if successfully saved
329
+ createdArrivalNoticeIds.push(createdArrivalNotice.id)
330
+ createdOrderProductIds.push(...createdOrderProducts.map(op => op.id))
331
+ })
332
+ } catch (error) {
333
+ // collect the arrivalNotice details and its error
334
+ let rawArrivalNotices: RawArrivalNotice[] = formRawArrivalNotices(arrivalNotices[i], error.message)
335
+ errorsCaught.push(...rawArrivalNotices)
336
+ }
337
+ }
338
+
339
+ if (createdArrivalNoticeIds.length && createdOrderProductIds.length) {
340
+ await getConnection().transaction(async childTx => {
341
+ // update arrivalNotices status to PENDING_RECEIVE
342
+ await childTx
343
+ .getRepository(ArrivalNotice)
344
+ .update({ id: In(createdArrivalNoticeIds) }, { status: ORDER_STATUS.PENDING_RECEIVE, updater: user })
345
+
346
+ // update orderProducts status to PENDING_RECEIVE
347
+ await childTx
348
+ .getRepository(OrderProduct)
349
+ .update({ id: In(createdOrderProductIds) }, { status: ORDER_PRODUCT_STATUS.PENDING_RECEIVE, updater: user })
350
+
351
+ finalArrivalNoticeList = await childTx.getRepository(ArrivalNotice).find({
352
+ where: { id: In(createdArrivalNoticeIds) },
353
+ relations: ['orderProducts', 'orderProducts.product', 'bizplace']
354
+ })
355
+ })
356
+ }
357
+
358
+ if (errorsCaught.length) {
359
+ // should return this error list in the grist so that user know which gan need to amend
360
+ throw new ValidationError({
361
+ ...ValidationError.ERROR_CODES.INVALID_DATA_FOUND,
362
+ detail: { data: JSON.stringify(errorsCaught) }
363
+ })
364
+ }
365
+
366
+ // return arrivalNotices with its orderProducts list
367
+ return { items: finalArrivalNoticeList, total: 0 }
368
+ }
233
369
  }
234
370
 
235
371
  export async function deleteArrivalNotice(
@@ -780,3 +916,103 @@ export async function editArrivalNoticeProducts(
780
916
  debug('gan-updated-order-products', updatedOrderProducts)
781
917
  await orderProductRepo.save(updatedOrderProducts)
782
918
  }
919
+
920
+ function extractArrivalNotices(rawArrivalNotices): Partial<ArrivalNotice[]> {
921
+ return rawArrivalNotices.reduce((arrivalNotices, raw) => {
922
+ // discard the item that has no productId or productDetailId
923
+ if (!raw.productId || !raw.productDetailId) return
924
+
925
+ const idx: number = arrivalNotices.findIndex(an => {
926
+ // consider these attributes if they are exist in "item"
927
+ const comparison = [
928
+ 'refNo2',
929
+ 'refNo3',
930
+ 'truckNo',
931
+ 'ownTransport',
932
+ 'container',
933
+ 'containerNo',
934
+ 'containerSize',
935
+ 'looseItem',
936
+ 'importCargo'
937
+ ]
938
+
939
+ let a: any = {},
940
+ b: any = {}
941
+
942
+ comparison.forEach(cc => {
943
+ if (raw[cc] !== null) {
944
+ a[cc] = raw[cc]
945
+ b[cc] = an[cc]
946
+ }
947
+ })
948
+
949
+ a = JSON.stringify(Object.fromEntries(Object.entries(a).sort()))
950
+ b = JSON.stringify(Object.fromEntries(Object.entries(b).sort()))
951
+
952
+ return an.refNo == raw.refNo && an.etaDate == raw.etaDate && a === b
953
+ })
954
+
955
+ const product: Product = Object.assign(new Product(), { id: raw.productId, sku: raw.sku })
956
+ const orderProduct: Partial<OrderProduct> = {
957
+ product,
958
+ batchId: raw.batchId,
959
+ packingType: raw.packingType,
960
+ packingSize: raw.packingSize,
961
+ packQty: raw.packQty,
962
+ uomValue: raw.uomValue,
963
+ uom: raw.uom,
964
+ totalUomValue: raw.totalUomValue,
965
+ palletQty: raw.palletQty,
966
+ unitPrice: raw.unitPrice,
967
+ manufactureYear: raw.manufactureYear
968
+ }
969
+
970
+ if (idx >= 0) {
971
+ const duplicateSkuIdx: number = arrivalNotices[idx].orderProducts.findIndex(
972
+ op =>
973
+ op.sku === raw.sku &&
974
+ op.packingType === raw.packingType &&
975
+ op.packingSize === raw.packingSize &&
976
+ op.batchId === raw.batchId
977
+ )
978
+
979
+ if (duplicateSkuIdx >= 0) arrivalNotices[idx].orderProducts[duplicateSkuIdx].packQty += raw.packQty
980
+ else arrivalNotices[idx].orderProducts.push(orderProduct)
981
+ } else {
982
+ arrivalNotices.push({
983
+ refNo: raw.refNo,
984
+ refNo2: raw.refNo2,
985
+ refNo3: raw.refNo3,
986
+ etaDate: raw.etaDate,
987
+ truckNo: raw.truckNo,
988
+ ownTransport: raw.ownTransport,
989
+ container: raw.container,
990
+ containerNo: raw.containerNo,
991
+ containerSize: raw.containerSize,
992
+ importCargo: raw.importCargo,
993
+ looseItem: raw.looseItem,
994
+ orderProducts: [orderProduct]
995
+ })
996
+ }
997
+
998
+ return arrivalNotices
999
+ }, [])
1000
+ }
1001
+
1002
+ function formRawArrivalNotices(arrivalNotice, errorMessage) {
1003
+ let rawArrivalNotices = []
1004
+ for (let i = 0, l = arrivalNotice.orderProducts.length; i < l; i++) {
1005
+ let rawArrivalNotice = {
1006
+ ...arrivalNotice,
1007
+ ...arrivalNotice.orderProducts[i],
1008
+ sku: arrivalNotice.orderProducts[i].product.sku,
1009
+ remark: errorMessage
1010
+ }
1011
+
1012
+ delete rawArrivalNotice.orderProducts
1013
+ delete rawArrivalNotice.product
1014
+
1015
+ rawArrivalNotices.push(rawArrivalNotice)
1016
+ }
1017
+ return rawArrivalNotices
1018
+ }