@things-factory/sales-base 4.0.23 → 4.0.27

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 (50) 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/job-sheet/job-sheet-query.js +2 -0
  18. package/dist-server/service/job-sheet/job-sheet-query.js.map +1 -1
  19. package/dist-server/service/order-inventory/order-inventory.js +22 -1
  20. package/dist-server/service/order-inventory/order-inventory.js.map +1 -1
  21. package/dist-server/service/release-good/release-good-mutation.js +219 -212
  22. package/dist-server/service/release-good/release-good-mutation.js.map +1 -1
  23. package/dist-server/service/release-good/release-good-query.js +135 -99
  24. package/dist-server/service/release-good/release-good-query.js.map +1 -1
  25. package/dist-server/service/release-good/release-good-types.js +38 -2
  26. package/dist-server/service/release-good/release-good-types.js.map +1 -1
  27. package/dist-server/service/release-good/release-good.js +63 -1
  28. package/dist-server/service/release-good/release-good.js.map +1 -1
  29. package/dist-server/service/return-order/return-order-mutation.js +1 -1
  30. package/dist-server/service/return-order/return-order-mutation.js.map +1 -1
  31. package/dist-server/utils/inventory-util.js +89 -1
  32. package/dist-server/utils/inventory-util.js.map +1 -1
  33. package/package.json +12 -12
  34. package/server/controllers/ecommerce/ecommerce-controller.ts +15 -6
  35. package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
  36. package/server/controllers/order-controller.ts +55 -2
  37. package/server/service/arrival-notice/arrival-notice-mutation.ts +237 -1
  38. package/server/service/arrival-notice/arrival-notice-query.ts +214 -4
  39. package/server/service/arrival-notice/arrival-notice-types.ts +120 -1
  40. package/server/service/delivery-order/delivery-order-types.ts +1 -1
  41. package/server/service/goods-receival-note/goods-receival-note.ts +4 -0
  42. package/server/service/job-sheet/job-sheet-query.ts +3 -1
  43. package/server/service/order-inventory/order-inventory.ts +17 -1
  44. package/server/service/release-good/release-good-mutation.ts +280 -283
  45. package/server/service/release-good/release-good-query.ts +158 -115
  46. package/server/service/release-good/release-good-types.ts +30 -2
  47. package/server/service/release-good/release-good.ts +46 -0
  48. package/server/service/return-order/return-order-mutation.ts +1 -1
  49. package/server/utils/index.ts +1 -1
  50. package/server/utils/inventory-util.ts +129 -1
@@ -7,7 +7,7 @@ import { User } from '@things-factory/auth-base'
7
7
  import { Bizplace, getCompanyBizplace, getMyBizplace, getPermittedBizplaceIds } from '@things-factory/biz-base'
8
8
  import { Product } from '@things-factory/product-base'
9
9
  import { buildQuery, Domain, Filter, ListParam, Pagination, Sorting } from '@things-factory/shell'
10
- import { Inventory } from '@things-factory/warehouse-base'
10
+ import { Inventory, LOCATION_TYPE } from '@things-factory/warehouse-base'
11
11
 
12
12
  import {
13
13
  InventoryInfos,
@@ -477,120 +477,10 @@ export class ReleaseGoodQuery {
477
477
  @Arg('rawReleaseGoods', type => [NewReleaseGood], { nullable: true }) rawReleaseGoods: NewReleaseGood[],
478
478
  @Arg('bizplaceId', type => String) bizplaceId: string
479
479
  ): Promise<ReleaseGood[]> {
480
- const { domain, tx } = context.state
481
- const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
482
-
483
- if (!rawReleaseGoods) return
484
-
485
- const json_oi = JSON.stringify(
486
- rawReleaseGoods.map(raw => {
487
- return {
488
- sku: raw.sku,
489
- packing_type: raw.packingType,
490
- packing_size: raw.packingSize,
491
- uom: raw.uom,
492
- release_qty: raw.releaseQty
493
- }
494
- })
495
- )
480
+ const { tx }: { tx: EntityManager } = context.state
481
+ const availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(rawReleaseGoods, bizplaceId, context, tx)
496
482
 
497
- await tx.query(
498
- `
499
- CREATE TEMP TABLE temp_order_products(
500
- product_id VARCHAR(50),
501
- sku VARCHAR(150),
502
- packing_type VARCHAR(50),
503
- packing_size INT,
504
- uom VARCHAR(10),
505
- release_qty INT
506
- );
507
- `
508
- )
509
-
510
- await tx.query(
511
- `
512
- INSERT INTO temp_order_products
513
- SELECT p.id AS product_id, js.sku,
514
- CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
515
- CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
516
- CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
517
- js.release_qty
518
- FROM JSON_POPULATE_RECORDSET(NULL::temp_order_products,'${json_oi}') js
519
- LEFT JOIN products p ON js.sku = p.sku AND p.bizplace_id = $1
520
- LEFT JOIN product_details pd ON p.id = pd.product_id AND pd.is_default = TRUE;
521
- `,
522
- [companyBizplaceId.id]
523
- )
524
-
525
- let availableItems = await tx.query(
526
- `
527
- SELECT i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom,
528
- SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
529
- (
530
- SELECT SUM(oi.release_qty) FROM order_inventories oi
531
- WHERE (
532
- oi.status = 'PENDING'
533
- OR oi.status = 'PENDING_RECEIVE'
534
- OR oi.status = 'PENDING_WORKSHEET'
535
- OR oi.status = 'PENDING_SPLIT'
536
- )
537
- AND oi.inventory_id IS null
538
- AND oi.product_id = i.product_id::uuid
539
- AND oi.packing_type = i.packing_type
540
- AND oi.uom = i.uom
541
- AND oi.domain_id = $1
542
- AND oi.bizplace_id = $2
543
- ), 0) as "remain_qty",
544
- SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
545
- (
546
- SELECT SUM(oi.release_uom_value) FROM order_inventories oi
547
- WHERE (
548
- oi.status = 'PENDING'
549
- OR oi.status = 'PENDING_RECEIVE'
550
- OR oi.status = 'PENDING_WORKSHEET'
551
- OR oi.status = 'PENDING_SPLIT'
552
- )
553
- AND oi.inventory_id IS null
554
- AND oi.product_id = i.product_id::uuid
555
- AND oi.packing_type = i.packing_type
556
- AND oi.uom = i.uom
557
- AND oi.domain_id = $1
558
- AND oi.bizplace_id = $2
559
- ), 0) as "remain_uom_value"
560
- FROM inventories i
561
- INNER JOIN (
562
- SELECT top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
563
- FROM temp_order_products top
564
- GROUP BY top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
565
- ) AS foo
566
- ON i.product_id = foo.product_id::uuid
567
- AND i.packing_type = foo.packing_type
568
- AND i.packing_size = foo.packing_size
569
- AND i.uom = foo.uom
570
- AND i.domain_id = $1
571
- AND i.bizplace_id = $2
572
- GROUP BY i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom
573
- ORDER BY foo.sku, remain_qty
574
- `,
575
- [domain.id, bizplaceId]
576
- )
577
-
578
- await tx.query('DROP TABLE temp_order_products')
579
-
580
- availableItems = availableItems.map(item => {
581
- return {
582
- productId: item.product_id,
583
- sku: item.sku,
584
- batchId: item.batch_id,
585
- packingType: item.packing_type,
586
- packingSize: item.packing_size,
587
- uom: item.uom,
588
- remainQty: item.remain_qty,
589
- remainUomValue: item.remain_uom_value
590
- }
591
- })
592
-
593
- return _extractData(rawReleaseGoods, availableItems)
483
+ return availableItems
594
484
  }
595
485
 
596
486
  @FieldResolver(type => Domain)
@@ -689,6 +579,134 @@ async function dropOITempTable(domain: Domain, tx: EntityManager) {
689
579
  await tx.query(`DROP TABLE oi`)
690
580
  }
691
581
 
582
+ export async function bulkReleaseGoodsAvailableItemsFunction(
583
+ rawReleaseGoods: any[],
584
+ bizplaceId: string,
585
+ context: any,
586
+ tx?: EntityManager
587
+ ): Promise<any[]> {
588
+ const { domain }: { domain: Domain } = context.state
589
+ const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
590
+
591
+ if (!rawReleaseGoods) return
592
+ const json_oi = JSON.stringify(
593
+ rawReleaseGoods.map(raw => {
594
+ return {
595
+ sku: raw.sku,
596
+ packing_type: raw.packingType,
597
+ packing_size: raw.packingSize,
598
+ uom: raw.uom,
599
+ release_qty: raw.releaseQty
600
+ }
601
+ })
602
+ )
603
+
604
+ await tx.query(
605
+ `
606
+ CREATE TEMP TABLE raw_release_goods(
607
+ product_id VARCHAR(50),
608
+ product_detail_id VARCHAR(50),
609
+ sku VARCHAR(150),
610
+ product_info VARCHAR(250),
611
+ packing_type VARCHAR(50),
612
+ packing_size INT,
613
+ uom VARCHAR(10),
614
+ release_qty INT
615
+ );
616
+ `
617
+ )
618
+
619
+ await tx.query(
620
+ `
621
+ INSERT INTO raw_release_goods
622
+ SELECT p.id AS product_id,
623
+ pd.id AS product_detail_id,
624
+ js.sku,
625
+ CASE WHEN p.description NOT IN (NULL, '', '-') THEN CONCAT(p.name, '(', p.description, ')')
626
+ ELSE p.name END AS product_info,
627
+ CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
628
+ CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
629
+ CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END
630
+ FROM JSON_POPULATE_RECORDSET(NULL::raw_release_goods, $1) js
631
+ LEFT JOIN products p ON js.sku = p.sku AND p.bizplace_id = $2
632
+ LEFT JOIN product_details pd ON p.id = pd.product_id
633
+ AND CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL AND js.uom NOTNULL
634
+ THEN pd.packing_type = js.packing_type AND pd.packing_size = js.packing_size AND pd.uom = js.uom
635
+ WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL AND js.uom ISNULL
636
+ THEN pd.packing_type = js.packing_type AND pd.packing_size = js.packing_size AND pd.is_default IS TRUE
637
+ WHEN js.packing_type ISNULL AND js.packing_size ISNULL AND js.uom NOTNULL
638
+ THEN pd.uom = js.uom AND pd.is_default IS TRUE ELSE pd.is_default IS TRUE
639
+ END;
640
+ `,
641
+ [json_oi, companyBizplaceId.id]
642
+ )
643
+
644
+ let availableItems = await tx.query(
645
+ `
646
+ WITH inv AS (
647
+ SELECT i.product_id, foo.product_detail_id, foo.sku, foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom,
648
+ SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
649
+ (
650
+ SELECT SUM(oi.release_qty) FROM order_inventories oi
651
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
652
+ AND oi.inventory_id IS null
653
+ AND oi.product_id = i.product_id::uuid
654
+ AND oi.packing_type = i.packing_type
655
+ AND oi.uom = i.uom
656
+ AND oi.domain_id = $1
657
+ AND oi.bizplace_id = $2
658
+ ), 0) as "remain_qty",
659
+ SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
660
+ (
661
+ SELECT SUM(oi.release_uom_value) FROM order_inventories oi
662
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
663
+ AND oi.inventory_id IS null
664
+ AND oi.product_id = i.product_id::uuid
665
+ AND oi.packing_type = i.packing_type
666
+ AND oi.uom = i.uom
667
+ AND oi.domain_id = $1
668
+ AND oi.bizplace_id = $2
669
+ ), 0) as "remain_uom_value"
670
+ FROM inventories i
671
+ LEFT JOIN locations l
672
+ ON i.location_id = l.id
673
+ INNER JOIN (
674
+ SELECT rrg.product_id, rrg.product_detail_id, rrg.sku, rrg.product_info, rrg.packing_type, rrg.packing_size, rrg.uom
675
+ FROM raw_release_goods rrg
676
+ GROUP BY rrg.product_id, rrg.product_detail_id, rrg.sku, rrg.product_info, rrg.packing_type, rrg.packing_size, rrg.uom
677
+ ) AS foo
678
+ ON i.product_id = foo.product_id::uuid
679
+ AND i.packing_type = foo.packing_type
680
+ AND i.packing_size = foo.packing_size
681
+ AND i.uom = foo.uom
682
+ AND i.domain_id = $1
683
+ AND i.bizplace_id = $2
684
+ WHERE l.type NOT IN ($3, $4)
685
+ GROUP BY i.product_id, foo.product_detail_id, foo.sku,foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom
686
+ ORDER BY foo.sku, remain_qty
687
+ ) SELECT * FROM inv WHERE remain_qty > 0
688
+ `,
689
+ [domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
690
+ )
691
+
692
+ availableItems = availableItems.map(item => {
693
+ return {
694
+ productId: item.product_id,
695
+ productDetailId: item.product_detail_id,
696
+ productInfo: item.product_info,
697
+ sku: item.sku,
698
+ batchId: item.batch_id,
699
+ packingType: item.packing_type,
700
+ packingSize: item.packing_size,
701
+ uom: item.uom,
702
+ remainQty: item.remain_qty,
703
+ remainUomValue: item.remain_uom_value
704
+ }
705
+ })
706
+
707
+ return _extractData(rawReleaseGoods, availableItems)
708
+ }
709
+
692
710
  function _extractData(rawData, validatedData) {
693
711
  return rawData.map(raw => {
694
712
  const idx = validatedData.findIndex(val => {
@@ -737,6 +755,7 @@ function _extractData(rawData, validatedData) {
737
755
  validatedData[idx].remainUomValue -= raw.assignedUomValue
738
756
 
739
757
  raw.productId = validatedData[idx].productId
758
+ raw.productDetailId = validatedData[idx].productDetailId
740
759
  raw.packingType = validatedData[idx].packingType
741
760
  raw.packingSize = validatedData[idx].packingSize
742
761
  raw.uom = validatedData[idx].uom
@@ -747,9 +766,25 @@ function _extractData(rawData, validatedData) {
747
766
  raw.productId = null
748
767
  }
749
768
 
769
+ let releaseDate = _getStdDateStr(new Date(raw.releaseDate || ''))
770
+
750
771
  return {
751
772
  ...raw,
752
- releaseUomValue
773
+ releaseUomValue,
774
+ errorMsg:
775
+ !raw.productId || !raw.productDetailId
776
+ ? 'product not found'
777
+ : raw.releaseQty <= 0
778
+ ? 'invalid release qty'
779
+ : raw.assignedQty < raw.releaseQty
780
+ ? 'insufficient stock'
781
+ : raw.releaseDate == ''
782
+ ? 'release date is empty'
783
+ : releaseDate < _getStdDateStr(new Date())
784
+ ? 'backdate is not allowed'
785
+ : !raw.refNo
786
+ ? 'ref no is empty'
787
+ : ''
753
788
  }
754
789
  })
755
790
  }
@@ -772,3 +807,11 @@ function getConditionValues(
772
807
  return condition
773
808
  }, {})
774
809
  }
810
+
811
+ function _getStdDateStr(date) {
812
+ if (isNaN(date.getFullYear())) return ''
813
+ else {
814
+ date.setHours(date.getHours() + 8)
815
+ return date.toISOString().split('T')[0]
816
+ }
817
+ }
@@ -158,6 +158,9 @@ export class NewReleaseGood {
158
158
  @Field({ nullable: true })
159
159
  name: string
160
160
 
161
+ @Field({ nullable: true })
162
+ bundleInfo: string
163
+
161
164
  @Field({ nullable: true })
162
165
  releaseDate: string
163
166
 
@@ -305,6 +308,13 @@ export class NewReleaseGood {
305
308
  @Field({ nullable: true })
306
309
  partnerBizplaceId: string
307
310
 
311
+ /* Mainly used for rawReleaseGood*/
312
+ @Field({ nullable: true })
313
+ productId: string
314
+
315
+ @Field({ nullable: true })
316
+ productDetailId: string
317
+
308
318
  @Field({ nullable: true })
309
319
  sku: string
310
320
 
@@ -312,17 +322,35 @@ export class NewReleaseGood {
312
322
  packingType: string
313
323
 
314
324
  @Field({ nullable: true })
315
- packingSize: string
325
+ packingSize: number
316
326
 
317
327
  @Field({ nullable: true })
318
328
  uom: string
319
329
 
320
330
  @Field({ nullable: true })
321
- releaseQty: string
331
+ releaseQty: number
332
+
333
+ @Field({ nullable: true })
334
+ assignedQty: number
335
+
336
+ @Field({ nullable: true })
337
+ assignedUomValue: number
338
+
339
+ @Field({ nullable: true })
340
+ remainQty: number
341
+
342
+ @Field({ nullable: true })
343
+ remainUomValue: number
344
+
345
+ @Field({ nullable: true })
346
+ errorMsg: string
322
347
  }
323
348
 
324
349
  @InputType()
325
350
  export class ReleaseGoodPatch {
351
+ @Field({ nullable: true })
352
+ id: string
353
+
326
354
  @Field({ nullable: true })
327
355
  name: string
328
356
 
@@ -289,6 +289,9 @@ export class ReleaseGood {
289
289
  @Field({ nullable: true })
290
290
  invoice: string
291
291
 
292
+ @Column({ nullable: true })
293
+ bundleInfo: string
294
+
292
295
  @Column({ type: 'int', nullable: true })
293
296
  @Field({ nullable: true })
294
297
  noOfItems: number
@@ -341,4 +344,47 @@ export class ReleaseGood {
341
344
 
342
345
  @Field(type => ShippingOrderInfo, { nullable: true })
343
346
  shippingOrderInfo?: ShippingOrderInfo
347
+
348
+ /* Mainly used for rawReleaseGood*/
349
+ @Field({ nullable: true })
350
+ productId: string
351
+
352
+ @Field({ nullable: true })
353
+ productDetailId: string
354
+
355
+ @Field({ nullable: true })
356
+ productInfo: string
357
+
358
+ @Field({ nullable: true })
359
+ sku: string
360
+
361
+ @Field({ nullable: true })
362
+ batchId: string
363
+
364
+ @Field({ nullable: true })
365
+ packingType: string
366
+
367
+ @Field({ nullable: true })
368
+ packingSize: number
369
+
370
+ @Field({ nullable: true })
371
+ uom: string
372
+
373
+ @Field({ nullable: true })
374
+ releaseQty: number
375
+
376
+ @Field({ nullable: true })
377
+ assignedQty: number
378
+
379
+ @Field({ nullable: true })
380
+ assignedUomValue: number
381
+
382
+ @Field({ nullable: true })
383
+ remainQty: number
384
+
385
+ @Field({ nullable: true })
386
+ remainUomValue: number
387
+
388
+ @Field({ nullable: true })
389
+ errorMsg: string
344
390
  }
@@ -42,7 +42,7 @@ export class ReturnOrderMutation {
42
42
  @Mutation(returns => ReturnOrder)
43
43
  async generateReturnOrder(
44
44
  @Ctx() context: any,
45
- @Arg('file', type => [GraphQLUpload]) file: FileUpload[],
45
+ @Arg('file', type => [GraphQLUpload], { nullable: true }) file: FileUpload[],
46
46
  @Arg('returnOrder', type => NewReturnOrder, { nullable: true }) returnOrder: NewReturnOrder
47
47
  ): Promise<ReturnOrder> {
48
48
  const { tx, domain, user }: { tx: EntityManager; domain: Domain; user: User } = context.state
@@ -1,2 +1,2 @@
1
1
  export * from './order-no-generator'
2
- export * from './inventory-util'
2
+ export * from './inventory-util'
@@ -1,4 +1,4 @@
1
- import { EntityManager, Equal, getRepository, In, Not, Raw, Repository } from 'typeorm'
1
+ import { EntityManager, Equal, getRepository, In, Not, Raw, Repository, SelectQueryBuilder } from 'typeorm'
2
2
 
3
3
  import { User } from '@things-factory/auth-base'
4
4
  import { Bizplace } from '@things-factory/biz-base'
@@ -16,6 +16,7 @@ import {
16
16
  } from '@things-factory/warehouse-base'
17
17
 
18
18
  import { ValidationError } from '../errors'
19
+ import { ORDER_TYPES } from '../constants'
19
20
  import {
20
21
  ArrivalNotice,
21
22
  DeliveryOrder,
@@ -270,6 +271,68 @@ export const InventoryUtil = {
270
271
  if (duplicatedReusablePalletCnt) throw new Error(palletId + ` exists`)
271
272
  },
272
273
 
274
+ /**
275
+ * To pre-assign inventories automatically for orderInventories in Release Goods
276
+ * @param product
277
+ * @param orderInventory
278
+ * @param packingType
279
+ * @param locationSortingRules
280
+ * @param customerBizplace
281
+ * @param domain
282
+ * @param trxMgr
283
+ * @returns orderInventories
284
+ */
285
+ async autoAssignInventoryForRelease(
286
+ product: Product,
287
+ orderInventory: OrderInventory,
288
+ packingType: string,
289
+ locationSortingRules: any = [],
290
+ customerBizplace: Bizplace,
291
+ domain: Domain,
292
+ trxMgr: EntityManager
293
+ ): Promise<OrderInventory[]> {
294
+ let qb: SelectQueryBuilder<Inventory> = trxMgr.getRepository(Inventory).createQueryBuilder('iv')
295
+ qb.leftJoinAndSelect('iv.location', 'loc')
296
+ .andWhere('"iv"."domain_id" = :domainId')
297
+ .andWhere('"iv"."bizplace_id" = :bizplaceId')
298
+ .andWhere('"iv"."packing_type" = :packingType')
299
+ .andWhere('"iv"."product_id" = :productId')
300
+ .andWhere('"iv"."status" = :status')
301
+ .andWhere('"iv"."qty" - COALESCE("iv"."locked_qty", 0) > 0')
302
+ .andWhere('"loc"."type" NOT IN (:...locationTypes)')
303
+ .setParameters({
304
+ domainId: domain.id,
305
+ bizplaceId: customerBizplace.id,
306
+ packingType: packingType,
307
+ productId: product.id,
308
+ status: INVENTORY_STATUS.STORED,
309
+ locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
310
+ })
311
+
312
+ if (locationSortingRules?.length) {
313
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
314
+ idx === 0
315
+ ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
316
+ : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
317
+ })
318
+ } else {
319
+ qb.addOrderBy('"iv"."created_at"', 'ASC')
320
+ }
321
+
322
+ let inventories: Inventory[] = await qb.getMany()
323
+ if (!inventories?.length) throw new Error(`no inventories found for ${product.sku}`)
324
+
325
+ inventories = inventories.map(inventory => {
326
+ return {
327
+ ...inventory,
328
+ remainQty: inventory.qty - (inventory?.lockedQty || 0),
329
+ remainUomValue: inventory.uomValue - (inventory?.lockedUomValue || 0)
330
+ }
331
+ })
332
+
333
+ return _composeTargetInventories(product, orderInventory, inventories)
334
+ },
335
+
273
336
  /**
274
337
  * @summary Do transaction on inventory record
275
338
  * @description It will update inventory after set a temp (domain, updater)
@@ -617,3 +680,68 @@ export async function switchLocationStatus(
617
680
 
618
681
  return location
619
682
  }
683
+
684
+ export function _composeTargetInventories(product: Product, record: any, inventories: Inventory[]): OrderInventory[] {
685
+ let leftReleaseQty: number = record.releaseQty
686
+ let leftReleaseUomValue: number = record.releaseUomValue
687
+ let compReleaseQty: number = 0
688
+ let compReleaseUomValue: number = 0
689
+ let totalInventoryQty: number = inventories.reduce((total, inventory) => total + inventory.remainQty, 0)
690
+
691
+ if (totalInventoryQty < record.releaseQty) {
692
+ throw new Error(`invalid release qty for ${product?.sku}`)
693
+ }
694
+
695
+ let orderInventories: Partial<OrderInventory[]> = []
696
+ let idx = 0
697
+ while (compReleaseQty < record.releaseQty) {
698
+ const inventory = inventories[idx]
699
+ const {
700
+ packingType,
701
+ packingSize,
702
+ batchId,
703
+ uom
704
+ }: { packingType: string; packingSize: number; batchId: string; uom: string } = inventory
705
+
706
+ let orderInventory: OrderInventory = new OrderInventory()
707
+
708
+ if (inventory.remainQty > leftReleaseQty) {
709
+ const uomValuePerQty: number = Math.round((inventory.remainUomValue / inventory.remainQty) * 100) / 100
710
+
711
+ compReleaseQty += leftReleaseQty
712
+ compReleaseUomValue += leftReleaseUomValue
713
+
714
+ orderInventory = {
715
+ ...orderInventory,
716
+ releaseQty: leftReleaseQty,
717
+ releaseUomValue: Math.round(leftReleaseQty * uomValuePerQty * 100) / 100
718
+ }
719
+ } else {
720
+ compReleaseQty += inventory.remainQty
721
+ compReleaseUomValue += inventory.remainUomValue
722
+ leftReleaseQty -= inventory.remainQty
723
+ leftReleaseUomValue -= inventory.remainUomValue
724
+
725
+ orderInventory = {
726
+ ...orderInventory,
727
+ releaseQty: inventory.remainQty,
728
+ releaseUomValue: inventory.remainUomValue
729
+ }
730
+ }
731
+
732
+ orderInventories.push({
733
+ ...orderInventory,
734
+ inventory,
735
+ packingType,
736
+ packingSize,
737
+ batchId,
738
+ uom,
739
+ product,
740
+ type: ORDER_TYPES.RELEASE_OF_GOODS
741
+ })
742
+
743
+ idx++
744
+ }
745
+
746
+ return orderInventories
747
+ }