@things-factory/worksheet-base 4.3.753 → 4.3.756

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 (31) hide show
  1. package/dist-server/controllers/outbound/picking-worksheet-controller.js +97 -27
  2. package/dist-server/controllers/outbound/picking-worksheet-controller.js.map +1 -1
  3. package/dist-server/controllers/outbound/sorting-worksheet-controller.js +117 -24
  4. package/dist-server/controllers/outbound/sorting-worksheet-controller.js.map +1 -1
  5. package/dist-server/controllers/render-ro-do.js +324 -96
  6. package/dist-server/controllers/render-ro-do.js.map +1 -1
  7. package/dist-server/graphql/resolvers/worksheet/find-sorting-release-orders-by-task-no.js +244 -123
  8. package/dist-server/graphql/resolvers/worksheet/find-sorting-release-orders-by-task-no.js.map +1 -1
  9. package/dist-server/graphql/resolvers/worksheet/index.js +1 -1
  10. package/dist-server/graphql/resolvers/worksheet/index.js.map +1 -1
  11. package/dist-server/graphql/resolvers/worksheet/loading/index.js +3 -1
  12. package/dist-server/graphql/resolvers/worksheet/loading/index.js.map +1 -1
  13. package/dist-server/graphql/resolvers/worksheet/loading/validate-qc-seals.js +79 -0
  14. package/dist-server/graphql/resolvers/worksheet/loading/validate-qc-seals.js.map +1 -0
  15. package/dist-server/graphql/types/worksheet/index.js +5 -1
  16. package/dist-server/graphql/types/worksheet/index.js.map +1 -1
  17. package/dist-server/graphql/types/worksheet/validate-qc-seals-result.js +12 -0
  18. package/dist-server/graphql/types/worksheet/validate-qc-seals-result.js.map +1 -0
  19. package/dist-server/graphql/types/worksheet/worksheet-info.js +1 -0
  20. package/dist-server/graphql/types/worksheet/worksheet-info.js.map +1 -1
  21. package/package.json +21 -21
  22. package/server/controllers/outbound/picking-worksheet-controller.ts +105 -31
  23. package/server/controllers/outbound/sorting-worksheet-controller.ts +137 -25
  24. package/server/controllers/render-ro-do.ts +378 -136
  25. package/server/graphql/resolvers/worksheet/find-sorting-release-orders-by-task-no.ts +305 -128
  26. package/server/graphql/resolvers/worksheet/index.ts +3 -2
  27. package/server/graphql/resolvers/worksheet/loading/index.ts +5 -0
  28. package/server/graphql/resolvers/worksheet/loading/validate-qc-seals.ts +91 -0
  29. package/server/graphql/types/worksheet/index.ts +5 -1
  30. package/server/graphql/types/worksheet/validate-qc-seals-result.ts +9 -0
  31. package/server/graphql/types/worksheet/worksheet-info.ts +1 -0
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidateQcSealsResult = void 0;
4
+ const apollo_server_koa_1 = require("apollo-server-koa");
5
+ exports.ValidateQcSealsResult = (0, apollo_server_koa_1.gql) `
6
+ type ValidateQcSealsResult {
7
+ valid: Boolean!
8
+ error: String
9
+ minimumSealNumber: Int
10
+ }
11
+ `;
12
+ //# sourceMappingURL=validate-qc-seals-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-qc-seals-result.js","sourceRoot":"","sources":["../../../../server/graphql/types/worksheet/validate-qc-seals-result.ts"],"names":[],"mappings":";;;AAAA,yDAAuC;AAE1B,QAAA,qBAAqB,GAAG,IAAA,uBAAG,EAAA;;;;;;CAMvC,CAAA"}
@@ -16,6 +16,7 @@ exports.WorksheetInfo = (0, apollo_server_koa_1.gql) `
16
16
  marketplaceStatus: String
17
17
  customerCompanyDomainId: String
18
18
  binLocationName: String
19
+ isReleaseGoodScan: Boolean
19
20
  bizplace: Bizplace
20
21
  containerNo: String
21
22
  airwayBill: String
@@ -1 +1 @@
1
- {"version":3,"file":"worksheet-info.js","sourceRoot":"","sources":["../../../../server/graphql/types/worksheet/worksheet-info.ts"],"names":[],"mappings":";;;AAAA,yDAAuC;AAE1B,QAAA,aAAa,GAAG,IAAA,uBAAG,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyC/B,CAAA"}
1
+ {"version":3,"file":"worksheet-info.js","sourceRoot":"","sources":["../../../../server/graphql/types/worksheet/worksheet-info.ts"],"names":[],"mappings":";;;AAAA,yDAAuC;AAE1B,QAAA,aAAa,GAAG,IAAA,uBAAG,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C/B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/worksheet-base",
3
- "version": "4.3.753",
3
+ "version": "4.3.756",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -23,28 +23,28 @@
23
23
  "migration:create": "node ../../node_modules/typeorm/cli.js migration:create -d ./server/migrations"
24
24
  },
25
25
  "dependencies": {
26
- "@things-factory/auth-base": "^4.3.752",
27
- "@things-factory/biz-base": "^4.3.752",
28
- "@things-factory/document-template-base": "^4.3.752",
29
- "@things-factory/id-rule-base": "^4.3.752",
30
- "@things-factory/integration-accounting": "^4.3.752",
31
- "@things-factory/integration-base": "^4.3.752",
32
- "@things-factory/integration-lmd": "^4.3.752",
33
- "@things-factory/integration-marketplace": "^4.3.752",
34
- "@things-factory/integration-powrup": "^4.3.752",
35
- "@things-factory/integration-sellercraft": "^4.3.752",
36
- "@things-factory/integration-sftp": "^4.3.752",
37
- "@things-factory/marketplace-base": "^4.3.752",
38
- "@things-factory/notification": "^4.3.752",
39
- "@things-factory/sales-base": "^4.3.752",
40
- "@things-factory/setting-base": "^4.3.752",
41
- "@things-factory/shell": "^4.3.752",
42
- "@things-factory/transport-base": "^4.3.752",
43
- "@things-factory/warehouse-base": "^4.3.752",
44
- "@things-factory/worksheet-ui": "^4.3.752",
26
+ "@things-factory/auth-base": "^4.3.755",
27
+ "@things-factory/biz-base": "^4.3.755",
28
+ "@things-factory/document-template-base": "^4.3.755",
29
+ "@things-factory/id-rule-base": "^4.3.755",
30
+ "@things-factory/integration-accounting": "^4.3.755",
31
+ "@things-factory/integration-base": "^4.3.755",
32
+ "@things-factory/integration-lmd": "^4.3.755",
33
+ "@things-factory/integration-marketplace": "^4.3.755",
34
+ "@things-factory/integration-powrup": "^4.3.755",
35
+ "@things-factory/integration-sellercraft": "^4.3.755",
36
+ "@things-factory/integration-sftp": "^4.3.755",
37
+ "@things-factory/marketplace-base": "^4.3.755",
38
+ "@things-factory/notification": "^4.3.755",
39
+ "@things-factory/sales-base": "^4.3.755",
40
+ "@things-factory/setting-base": "^4.3.755",
41
+ "@things-factory/shell": "^4.3.755",
42
+ "@things-factory/transport-base": "^4.3.755",
43
+ "@things-factory/warehouse-base": "^4.3.755",
44
+ "@things-factory/worksheet-ui": "^4.3.755",
45
45
  "jspdf": "2.5.1",
46
46
  "puppeteer": "21.0.3",
47
47
  "uuid": "^9.0.0"
48
48
  },
49
- "gitHead": "b8d997d26b2b149d46ee6ba33c0db6f57fa13fa7"
49
+ "gitHead": "b16ce3a1cde77139646a0812c3b198a854e48a19"
50
50
  }
@@ -1730,12 +1730,59 @@ export class PickingWorksheetController extends VasWorksheetController {
1730
1730
  }
1731
1731
 
1732
1732
  async completePicking(releaseGood: ReleaseGood, worksheet: Worksheet, inventories: Inventory[]): Promise<any> {
1733
- const foundNotSealedOrderTote = await this.trxMgr
1734
- .getRepository(OrderTote)
1735
- .findOne({ where: { releaseGood, closedDate: IsNull() } })
1733
+ // Check enable-tote-scanning setting
1734
+ let enableToteScanningSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
1735
+ where: {
1736
+ domain: this.domain,
1737
+ category: 'id-rule',
1738
+ name: 'enable-tote-scanning'
1739
+ }
1740
+ })
1741
+
1742
+ if (enableToteScanningSetting) {
1743
+ // enable-tote-scanning is numeric: 0 = disabled, >= 1 = enabled
1744
+ const enableToteScanningValue = parseInt(enableToteScanningSetting.value || '0', 10)
1745
+
1746
+ if (enableToteScanningValue >= 1) {
1747
+ // Check minimum-seal-number setting
1748
+ let minimumSealNumberSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
1749
+ where: {
1750
+ domain: this.domain,
1751
+ category: 'id-rule',
1752
+ name: 'minimum-seal-number'
1753
+ }
1754
+ })
1736
1755
 
1737
- if (foundNotSealedOrderTote) {
1738
- throw new Error('Please seal the tote(s) before completing')
1756
+ const minimumSealNumber = parseInt(minimumSealNumberSetting?.value || '0', 10)
1757
+
1758
+ // Check if there are unsealed totes (closedDate is null)
1759
+ const foundNotSealedOrderTote = await this.trxMgr
1760
+ .getRepository(OrderTote)
1761
+ .findOne({ where: { releaseGood, closedDate: IsNull() } })
1762
+
1763
+ if (foundNotSealedOrderTote) {
1764
+ throw new Error('Please seal the tote(s) before completing')
1765
+ }
1766
+
1767
+ // If minimum-seal-number > 0, validate seal counts per tote
1768
+ if (minimumSealNumber > 0) {
1769
+ // Get all order totes for this release good
1770
+ const orderTotes: OrderTote[] = await this.trxMgr.getRepository(OrderTote).find({
1771
+ where: { releaseGood },
1772
+ relations: ['orderToteSeals']
1773
+ })
1774
+
1775
+ // Validate each tote has at least minimum-seal-number seals
1776
+ for (const orderTote of orderTotes) {
1777
+ const sealCount = orderTote.orderToteSeals?.length || 0
1778
+ if (sealCount < minimumSealNumber) {
1779
+ throw new Error(
1780
+ `Tote ${orderTote.name} has ${sealCount} seal(s), but minimum ${minimumSealNumber} seal(s) required`
1781
+ )
1782
+ }
1783
+ }
1784
+ }
1785
+ }
1739
1786
  }
1740
1787
 
1741
1788
  //check if any inventories is obsolete
@@ -2198,31 +2245,30 @@ export class PickingWorksheetController extends VasWorksheetController {
2198
2245
 
2199
2246
  private async toteScanning(toteNo, targetProduct, targetInventory, pickedQty, releaseGood, bizplace) {
2200
2247
  //1. find tote
2201
- let foundTote: Tote = await this.trxMgr
2248
+ const foundTote: Tote = await this.trxMgr
2202
2249
  .getRepository(Tote)
2203
2250
  .findOne({ where: { bizplace, name: toteNo, deletedAt: IsNull() } })
2204
2251
 
2205
- if (foundTote?.status == TOTE_STATUS.DAMAGED || foundTote?.status == TOTE_STATUS.DISPATCHED) {
2206
- foundTote = null
2207
- }
2252
+ const validTote = foundTote?.status !== TOTE_STATUS.DAMAGED && foundTote?.status !== TOTE_STATUS.DISPATCHED
2253
+ ? foundTote
2254
+ : null
2208
2255
 
2209
2256
  //2. find order tote
2210
- let foundOrderTote: OrderTote = await this.trxMgr
2257
+ let orderTote: OrderTote = await this.trxMgr
2211
2258
  .getRepository(OrderTote)
2212
2259
  .findOne({ where: { domain: this.domain, name: toteNo, releaseGood } })
2213
2260
 
2214
- //if order tote not found the create one, if tote not found means it's tote box
2215
- //create order tote item
2216
- if (!foundOrderTote) {
2217
- const orderTote = await this.trxMgr.getRepository(OrderTote).save({
2261
+ //if order tote not found then create one, if tote not found means it's tote box
2262
+ if (!orderTote) {
2263
+ orderTote = await this.trxMgr.getRepository(OrderTote).save({
2218
2264
  name: toteNo,
2219
2265
  domain: this.domain,
2220
2266
  releaseGood,
2221
- tote: foundTote ? foundTote : null,
2267
+ tote: validTote ?? null,
2222
2268
  updater: this.user
2223
2269
  })
2224
2270
 
2225
- const orderToteItem = await this.trxMgr.getRepository(OrderToteItem).save({
2271
+ await this.trxMgr.getRepository(OrderToteItem).save({
2226
2272
  domain: this.domain,
2227
2273
  name: OrderNoGenerator.orderToteItem(),
2228
2274
  orderProduct: targetProduct,
@@ -2232,7 +2278,7 @@ export class PickingWorksheetController extends VasWorksheetController {
2232
2278
  updater: this.user
2233
2279
  })
2234
2280
  } else {
2235
- if (foundOrderTote.closedDate) {
2281
+ if (orderTote.closedDate) {
2236
2282
  throw new Error('Tote has been sealed, please try another tote!')
2237
2283
  }
2238
2284
 
@@ -2241,25 +2287,45 @@ export class PickingWorksheetController extends VasWorksheetController {
2241
2287
  domain: this.domain,
2242
2288
  orderProduct: targetProduct,
2243
2289
  orderInventory: targetInventory,
2244
- orderTote: foundOrderTote
2290
+ orderTote
2245
2291
  })
2246
2292
 
2247
- //if not order tote item doesnt exist then create one
2248
2293
  if (!foundOrderToteItem) {
2249
- const orderToteItem = await this.trxMgr.getRepository(OrderToteItem).save({
2294
+ await this.trxMgr.getRepository(OrderToteItem).save({
2250
2295
  domain: this.domain,
2251
2296
  name: OrderNoGenerator.orderToteItem(),
2252
2297
  orderProduct: targetProduct,
2253
2298
  orderInventory: targetInventory,
2254
- orderTote: foundOrderTote,
2299
+ orderTote,
2255
2300
  qty: pickedQty,
2256
2301
  updater: this.user
2257
2302
  })
2258
2303
  } else {
2259
- //if found order tote item found then add the quantity
2260
- const orderToteItem = await this.trxMgr.getRepository(OrderToteItem).save({
2261
- ...foundOrderToteItem,
2262
- qty: foundOrderToteItem.qty + pickedQty
2304
+ await this.trxMgr.getRepository(OrderToteItem).update(foundOrderToteItem.id, {
2305
+ qty: foundOrderToteItem.qty + pickedQty,
2306
+ updater: this.user,
2307
+ updatedAt: new Date()
2308
+ })
2309
+ }
2310
+ }
2311
+
2312
+ // Check minimum-seal-number setting - if 0, automatically close the tote
2313
+ if (!orderTote.closedDate) {
2314
+ const sealNoSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
2315
+ where: {
2316
+ domain: this.domain,
2317
+ name: 'minimum-seal-number'
2318
+ }
2319
+ })
2320
+
2321
+ const minimumSealRequiredRaw = parseInt(sealNoSetting?.value ?? '0', 10)
2322
+ const minimumSealRequired = isNaN(minimumSealRequiredRaw) ? 0 : Math.max(0, minimumSealRequiredRaw)
2323
+
2324
+ if (minimumSealRequired === 0) {
2325
+ await this.trxMgr.getRepository(OrderTote).update(orderTote.id, {
2326
+ closedDate: new Date(),
2327
+ updater: this.user,
2328
+ updatedAt: new Date()
2263
2329
  })
2264
2330
  }
2265
2331
  }
@@ -2376,7 +2442,7 @@ export class PickingWorksheetController extends VasWorksheetController {
2376
2442
  throw new Error('Tote not scanned under this order')
2377
2443
  }
2378
2444
 
2379
- let totalOrderToteItems = await this.trxMgr.getRepository(OrderToteItem).count({
2445
+ const totalOrderToteItems = await this.trxMgr.getRepository(OrderToteItem).count({
2380
2446
  where: {
2381
2447
  orderTote: foundOrderTote
2382
2448
  }
@@ -2386,7 +2452,7 @@ export class PickingWorksheetController extends VasWorksheetController {
2386
2452
  throw new Error('Tote carton is empty')
2387
2453
  }
2388
2454
 
2389
- const newToteOrderSeal: OrderToteSeal = await this.trxMgr.getRepository(OrderToteSeal).save({
2455
+ await this.trxMgr.getRepository(OrderToteSeal).save({
2390
2456
  domain: this.domain,
2391
2457
  name: sealNo,
2392
2458
  orderTote: foundOrderTote,
@@ -2397,10 +2463,18 @@ export class PickingWorksheetController extends VasWorksheetController {
2397
2463
  orderTote: foundOrderTote
2398
2464
  })
2399
2465
 
2400
- if (totalSeal >= parseInt(sealNoSetting?.value || 0)) {
2401
- await this.trxMgr.getRepository(OrderTote).save({
2402
- ...foundOrderTote,
2403
- closedDate: new Date()
2466
+ // Determine minimum seals required; clamp negative values to 0
2467
+ const minimumSealRequiredRaw = parseInt(sealNoSetting?.value ?? '0', 10)
2468
+ const minimumSealRequired = isNaN(minimumSealRequiredRaw) ? 0 : Math.max(0, minimumSealRequiredRaw)
2469
+
2470
+ // Close tote if: seals are optional (0) OR minimum seal count reached
2471
+ const shouldCloseTote = minimumSealRequired === 0 || totalSeal >= minimumSealRequired
2472
+
2473
+ if (shouldCloseTote) {
2474
+ await this.trxMgr.getRepository(OrderTote).update(foundOrderTote.id, {
2475
+ closedDate: new Date(),
2476
+ updater: this.user,
2477
+ updatedAt: new Date()
2404
2478
  })
2405
2479
  }
2406
2480
  }
@@ -9,6 +9,7 @@ import {
9
9
  OrderNoGenerator,
10
10
  OrderTote,
11
11
  OrderToteItem,
12
+ OrderToteSeal,
12
13
  ReleaseGood
13
14
  } from '@things-factory/sales-base'
14
15
  import { Setting } from '@things-factory/setting-base'
@@ -250,7 +251,7 @@ export class SortingWorksheetController extends VasWorksheetController {
250
251
  } else {
251
252
  sortedQty = matchingProduct.qty
252
253
  }
253
-
254
+
254
255
  const releaseGood: ReleaseGood =
255
256
  orderInventories.length > 1
256
257
  ? orderInventories.find(item => item.productId === scanProductBarcode.productId)?.releaseGood
@@ -535,12 +536,59 @@ export class SortingWorksheetController extends VasWorksheetController {
535
536
  throw new Error('Order is already sorted')
536
537
  }
537
538
 
538
- const foundNotSealedOrderTote = await this.trxMgr
539
- .getRepository(OrderTote)
540
- .findOne({ where: { releaseGood, closedDate: IsNull() } })
539
+ // Check enable-tote-scanning setting
540
+ let enableToteScanningSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
541
+ where: {
542
+ domain: this.domain,
543
+ category: 'id-rule',
544
+ name: 'enable-tote-scanning'
545
+ }
546
+ })
547
+
548
+ if (enableToteScanningSetting) {
549
+ // enable-tote-scanning is numeric: 0 = disabled, >= 1 = enabled
550
+ const enableToteScanningValue = parseInt(enableToteScanningSetting.value || '0', 10)
551
+
552
+ if (enableToteScanningValue >= 1) {
553
+ // Check minimum-seal-number setting
554
+ let minimumSealNumberSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
555
+ where: {
556
+ domain: this.domain,
557
+ category: 'id-rule',
558
+ name: 'minimum-seal-number'
559
+ }
560
+ })
541
561
 
542
- if (foundNotSealedOrderTote) {
543
- throw new Error('Please seal the tote(s) before proceeding')
562
+ const minimumSealNumber = parseInt(minimumSealNumberSetting?.value || '0', 10)
563
+
564
+ // Check if there are unsealed totes (closedDate is null)
565
+ const foundNotSealedOrderTote = await this.trxMgr
566
+ .getRepository(OrderTote)
567
+ .findOne({ where: { releaseGood, closedDate: IsNull() } })
568
+
569
+ if (foundNotSealedOrderTote) {
570
+ throw new Error('Please seal the tote(s) before proceeding')
571
+ }
572
+
573
+ // If minimum-seal-number > 0, validate seal counts per tote
574
+ if (minimumSealNumber > 0) {
575
+ // Get all order totes for this release good
576
+ const orderTotes: OrderTote[] = await this.trxMgr.getRepository(OrderTote).find({
577
+ where: { releaseGood },
578
+ relations: ['orderToteSeals']
579
+ })
580
+
581
+ // Validate each tote has at least minimum-seal-number seals
582
+ for (const orderTote of orderTotes) {
583
+ const sealCount = orderTote.orderToteSeals?.length || 0
584
+ if (sealCount < minimumSealNumber) {
585
+ throw new Error(
586
+ `Tote ${orderTote.name} has ${sealCount} seal(s), but minimum ${minimumSealNumber} seal(s) required`
587
+ )
588
+ }
589
+ }
590
+ }
591
+ }
544
592
  }
545
593
 
546
594
  releaseGood.status = ORDER_STATUS.READY_TO_LOAD
@@ -583,12 +631,59 @@ export class SortingWorksheetController extends VasWorksheetController {
583
631
 
584
632
  let releaseGoodIds = worksheet.worksheetDetails.map(itm => itm.targetInventory.releaseGood.id)
585
633
 
586
- const foundNotSealedOrderTote = await this.trxMgr
587
- .getRepository(OrderTote)
588
- .findOne({ where: { releaseGood: In(releaseGoodIds), closedDate: IsNull() } })
634
+ // Check enable-tote-scanning setting
635
+ let enableToteScanningSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
636
+ where: {
637
+ domain: this.domain,
638
+ category: 'id-rule',
639
+ name: 'enable-tote-scanning'
640
+ }
641
+ })
589
642
 
590
- if (foundNotSealedOrderTote) {
591
- throw new Error('Please seal the tote(s) before proceeding')
643
+ if (enableToteScanningSetting) {
644
+ // enable-tote-scanning is numeric: 0 = disabled, >= 1 = enabled
645
+ const enableToteScanningValue = parseInt(enableToteScanningSetting.value || '0', 10)
646
+
647
+ if (enableToteScanningValue >= 1) {
648
+ // Check minimum-seal-number setting
649
+ let minimumSealNumberSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
650
+ where: {
651
+ domain: this.domain,
652
+ category: 'id-rule',
653
+ name: 'minimum-seal-number'
654
+ }
655
+ })
656
+
657
+ const minimumSealNumber = parseInt(minimumSealNumberSetting?.value || '0', 10)
658
+
659
+ // Check if there are unsealed totes (closedDate is null) for any of the release goods
660
+ const foundNotSealedOrderTote = await this.trxMgr
661
+ .getRepository(OrderTote)
662
+ .findOne({ where: { releaseGood: In(releaseGoodIds), closedDate: IsNull() } })
663
+
664
+ if (foundNotSealedOrderTote) {
665
+ throw new Error('Please seal the tote(s) before proceeding')
666
+ }
667
+
668
+ // If minimum-seal-number > 0, validate seal counts per tote for all release goods
669
+ if (minimumSealNumber > 0) {
670
+ // Get all order totes for all release goods in this worksheet
671
+ const orderTotes: OrderTote[] = await this.trxMgr.getRepository(OrderTote).find({
672
+ where: { releaseGood: In(releaseGoodIds) },
673
+ relations: ['orderToteSeals']
674
+ })
675
+
676
+ // Validate each tote has at least minimum-seal-number seals
677
+ for (const orderTote of orderTotes) {
678
+ const sealCount = orderTote.orderToteSeals?.length || 0
679
+ if (sealCount < minimumSealNumber) {
680
+ throw new Error(
681
+ `Tote ${orderTote.name} has ${sealCount} seal(s), but minimum ${minimumSealNumber} seal(s) required`
682
+ )
683
+ }
684
+ }
685
+ }
686
+ }
592
687
  }
593
688
 
594
689
  this.checkRecordValidity(worksheet, { status: WORKSHEET_STATUS.EXECUTING })
@@ -617,7 +712,7 @@ export class SortingWorksheetController extends VasWorksheetController {
617
712
 
618
713
  private async toteScanning(toteNo, targetProduct, targetInventory, pickedQty, releaseGood, bizplace) {
619
714
  //1. find tote
620
- let foundTote: Tote = await this.trxMgr
715
+ const foundTote: Tote = await this.trxMgr
621
716
  .getRepository(Tote)
622
717
  .findOne({ where: { bizplace, name: toteNo, deletedAt: IsNull() } })
623
718
 
@@ -630,22 +725,21 @@ export class SortingWorksheetController extends VasWorksheetController {
630
725
  }
631
726
 
632
727
  //2. find order tote
633
- let foundOrderTote: OrderTote = await this.trxMgr
728
+ let orderTote: OrderTote = await this.trxMgr
634
729
  .getRepository(OrderTote)
635
730
  .findOne({ where: { domain: this.domain, name: toteNo, releaseGood } })
636
731
 
637
732
  //if order tote not found then create one, if tote not found means it's tote box
638
- //create order tote item
639
- if (!foundOrderTote) {
640
- const orderTote = await this.trxMgr.getRepository(OrderTote).save({
733
+ if (!orderTote) {
734
+ orderTote = await this.trxMgr.getRepository(OrderTote).save({
641
735
  name: toteNo,
642
736
  domain: this.domain,
643
737
  releaseGood,
644
- tote: foundTote ? foundTote : null,
738
+ tote: foundTote ?? null,
645
739
  updater: this.user
646
740
  })
647
741
 
648
- const orderToteItem = await this.trxMgr.getRepository(OrderToteItem).save({
742
+ await this.trxMgr.getRepository(OrderToteItem).save({
649
743
  domain: this.domain,
650
744
  name: OrderNoGenerator.orderToteItem(),
651
745
  orderProduct: targetProduct,
@@ -655,7 +749,7 @@ export class SortingWorksheetController extends VasWorksheetController {
655
749
  updater: this.user
656
750
  })
657
751
  } else {
658
- if (foundOrderTote.closedDate) {
752
+ if (orderTote.closedDate) {
659
753
  throw new Error('Tote has been sealed, please try another tote!')
660
754
  }
661
755
 
@@ -664,27 +758,45 @@ export class SortingWorksheetController extends VasWorksheetController {
664
758
  domain: this.domain,
665
759
  orderProduct: targetProduct,
666
760
  orderInventory: targetInventory,
667
- orderTote: foundOrderTote
761
+ orderTote
668
762
  })
669
763
 
670
- //if not order tote item doesnt exist then create one
671
764
  if (!foundOrderToteItem) {
672
- const orderToteItem = await this.trxMgr.getRepository(OrderToteItem).save({
765
+ await this.trxMgr.getRepository(OrderToteItem).save({
673
766
  domain: this.domain,
674
767
  name: OrderNoGenerator.orderToteItem(),
675
768
  orderProduct: targetProduct,
676
769
  orderInventory: targetInventory,
677
- orderTote: foundOrderTote,
770
+ orderTote,
678
771
  qty: pickedQty,
679
772
  updater: this.user
680
773
  })
681
774
  } else {
682
- //if found order tote item found then add the quantity
683
- const orderToteItem = await this.trxMgr.getRepository(OrderToteItem).save({
775
+ await this.trxMgr.getRepository(OrderToteItem).save({
684
776
  ...foundOrderToteItem,
685
777
  qty: foundOrderToteItem.qty + pickedQty
686
778
  })
687
779
  }
688
780
  }
781
+
782
+ // Check minimum-seal-number setting - if 0, automatically close the tote
783
+ if (!orderTote.closedDate) {
784
+ const sealNoSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
785
+ where: {
786
+ domain: this.domain,
787
+ name: 'minimum-seal-number'
788
+ }
789
+ })
790
+
791
+ const minimumSealRequiredRaw = parseInt(sealNoSetting?.value ?? '0', 10)
792
+ const minimumSealRequired = isNaN(minimumSealRequiredRaw) ? 0 : Math.max(0, minimumSealRequiredRaw)
793
+
794
+ if (minimumSealRequired === 0) {
795
+ await this.trxMgr.getRepository(OrderTote).save({
796
+ ...orderTote,
797
+ closedDate: new Date()
798
+ })
799
+ }
800
+ }
689
801
  }
690
802
  }