@things-factory/operato-wms 4.3.752 → 4.3.755

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 (78) hide show
  1. package/client/pages/inbound/unload-product.js +30 -33
  2. package/client/pages/outbound/loading-product.js +2 -3
  3. package/client/pages/outbound/loading-v2/loading-execution-base.js +568 -56
  4. package/client/pages/outbound/loading-v2/loading-execution.js +258 -8
  5. package/client/pages/outbound/loading-v2/loading.js +82 -12
  6. package/client/pages/outbound/picking-product.js +11 -6
  7. package/client/pages/outbound/picking-v2/picking-execution-base.js +8 -8
  8. package/client/pages/outbound/picking-v2/picking-execution.js +75 -0
  9. package/client/pages/outbound/route-label-history-dialog.js +253 -0
  10. package/client/pages/outbound/route-label-popup.js +637 -131
  11. package/client/pages/outbound/show-ro-list-popup.js +8 -4
  12. package/client/pages/outbound/single-outbound-worksheet.js +55 -6
  13. package/client/pages/outbound/sorting-product.js +71 -25
  14. package/client/pages/outbound/zone-worksheet.js +62 -3
  15. package/dist-server/entities/index.js +9 -0
  16. package/dist-server/entities/index.js.map +1 -0
  17. package/dist-server/entities/route-label-sequence-counter.js +75 -0
  18. package/dist-server/entities/route-label-sequence-counter.js.map +1 -0
  19. package/dist-server/entities/route-label-sequence-log.js +74 -0
  20. package/dist-server/entities/route-label-sequence-log.js.map +1 -0
  21. package/dist-server/graphql/resolvers/index.js +3 -0
  22. package/dist-server/graphql/resolvers/index.js.map +1 -1
  23. package/dist-server/graphql/resolvers/outbound/complete-load.js +40 -3
  24. package/dist-server/graphql/resolvers/outbound/complete-load.js.map +1 -1
  25. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js +50 -11
  26. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js.map +1 -1
  27. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good.js +90 -48
  28. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good.js.map +1 -1
  29. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js +15 -7
  30. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js.map +1 -1
  31. package/dist-server/graphql/resolvers/outbound/sort-item.js +95 -16
  32. package/dist-server/graphql/resolvers/outbound/sort-item.js.map +1 -1
  33. package/dist-server/graphql/resolvers/route-label/index.js +7 -0
  34. package/dist-server/graphql/resolvers/route-label/index.js.map +1 -0
  35. package/dist-server/graphql/resolvers/route-label/route-label.js +220 -0
  36. package/dist-server/graphql/resolvers/route-label/route-label.js.map +1 -0
  37. package/dist-server/graphql/resolvers/zone-worksheet/index.js.map +1 -1
  38. package/dist-server/graphql/resolvers/zone-worksheet/zone-picking.js +123 -27
  39. package/dist-server/graphql/resolvers/zone-worksheet/zone-picking.js.map +1 -1
  40. package/dist-server/graphql/resolvers/zone-worksheet/zone-worksheet.js.map +1 -1
  41. package/dist-server/graphql/types/index.js +4 -0
  42. package/dist-server/graphql/types/index.js.map +1 -1
  43. package/dist-server/graphql/types/outbound/index.js +7 -1
  44. package/dist-server/graphql/types/outbound/index.js.map +1 -1
  45. package/dist-server/graphql/types/route-label/index.js +14 -0
  46. package/dist-server/graphql/types/route-label/index.js.map +1 -0
  47. package/dist-server/graphql/types/route-label/route-label-sequence.js +53 -0
  48. package/dist-server/graphql/types/route-label/route-label-sequence.js.map +1 -0
  49. package/dist-server/index.js +5 -0
  50. package/dist-server/index.js.map +1 -1
  51. package/dist-server/validators/setting-validators.js +519 -0
  52. package/dist-server/validators/setting-validators.js.map +1 -0
  53. package/helps/operato-wms/outbound/picking-product.en.md +156 -158
  54. package/package.json +50 -50
  55. package/server/entities/index.ts +6 -0
  56. package/server/entities/route-label-sequence-counter.ts +56 -0
  57. package/server/entities/route-label-sequence-log.ts +47 -0
  58. package/server/graphql/resolvers/index.ts +3 -0
  59. package/server/graphql/resolvers/outbound/complete-load.ts +42 -1
  60. package/server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.ts +56 -13
  61. package/server/graphql/resolvers/outbound/find-loadable-release-good.ts +120 -57
  62. package/server/graphql/resolvers/outbound/loading-worksheet-v2.ts +9 -2
  63. package/server/graphql/resolvers/outbound/sort-item.ts +125 -21
  64. package/server/graphql/resolvers/route-label/index.ts +16 -0
  65. package/server/graphql/resolvers/route-label/route-label.ts +264 -0
  66. package/server/graphql/resolvers/zone-worksheet/index.ts +2 -2
  67. package/server/graphql/resolvers/zone-worksheet/zone-picking.ts +156 -39
  68. package/server/graphql/resolvers/zone-worksheet/zone-worksheet.ts +1 -2
  69. package/server/graphql/types/index.ts +4 -0
  70. package/server/graphql/types/outbound/index.ts +7 -1
  71. package/server/graphql/types/route-label/index.ts +18 -0
  72. package/server/graphql/types/route-label/route-label-sequence.ts +50 -0
  73. package/server/index.ts +7 -1
  74. package/server/validators/setting-validators.ts +668 -0
  75. package/translations/en.json +280 -252
  76. package/translations/ko.json +29 -1
  77. package/translations/ms.json +28 -0
  78. package/translations/zh.json +28 -0
@@ -27,6 +27,7 @@ import {
27
27
  } from '@things-factory/warehouse-base'
28
28
  import { Product, ProductBarcode, ProductDetail } from '@things-factory/product-base'
29
29
  import { User } from '@things-factory/auth-base'
30
+ import { Setting } from '@things-factory/setting-base'
30
31
  import { v4 as uuidv4 } from 'uuid'
31
32
 
32
33
  export const zonePicking = {
@@ -345,14 +346,62 @@ async function completeZonePicking(worksheetId: string, context: any, isPartial:
345
346
  }
346
347
  )
347
348
  } else if (!isPartial) {
348
- await tx.getRepository(ReleaseGood).update(
349
- { id: In(roIds), status: ORDER_STATUS.PARTIAL_PROCESSING },
350
- {
351
- status: ORDER_STATUS.PROCESSING,
352
- updater: user,
353
- updatedAt: new Date()
349
+ // When completing fully, check if all OrderInventories for each ReleaseGood are actually completed
350
+ // Only update from PARTIAL_PROCESSING to PROCESSING if all OrderInventories are completed
351
+ const releaseGoods: ReleaseGood[] = await tx.getRepository(ReleaseGood).find({
352
+ where: { id: In(roIds) },
353
+ relations: ['orderInventories']
354
+ })
355
+
356
+ const completedRoIds: string[] = []
357
+ const incompleteRoIds: string[] = []
358
+
359
+ for (const rg of releaseGoods) {
360
+ // Check if all OrderInventories are in a completed state (not PROCESSING)
361
+ const completedStatuses = [
362
+ ORDER_INVENTORY_STATUS.PICKED,
363
+ ORDER_INVENTORY_STATUS.SORTING,
364
+ ORDER_INVENTORY_STATUS.LOADING,
365
+ ORDER_INVENTORY_STATUS.LOADED,
366
+ ORDER_INVENTORY_STATUS.TERMINATED,
367
+ ORDER_INVENTORY_STATUS.MISSING,
368
+ ORDER_INVENTORY_STATUS.CANCELLED
369
+ ]
370
+
371
+ const allCompleted = rg.orderInventories.every(oi => completedStatuses.includes(oi.status))
372
+
373
+ if (allCompleted) {
374
+ completedRoIds.push(rg.id)
375
+ } else {
376
+ incompleteRoIds.push(rg.id)
354
377
  }
355
- )
378
+ }
379
+
380
+ // Update ReleaseGoods that are fully completed from PARTIAL_PROCESSING to PROCESSING
381
+ if (completedRoIds.length > 0) {
382
+ await tx.getRepository(ReleaseGood).update(
383
+ { id: In(completedRoIds), status: ORDER_STATUS.PARTIAL_PROCESSING },
384
+ {
385
+ status: ORDER_STATUS.PROCESSING,
386
+ updater: user,
387
+ updatedAt: new Date()
388
+ }
389
+ )
390
+ }
391
+
392
+ // For ReleaseGoods that still have incomplete OrderInventories:
393
+ // - If they're in PROCESSING, update to PARTIAL_PROCESSING
394
+ // - If they're already in PARTIAL_PROCESSING, leave them as is
395
+ if (incompleteRoIds.length > 0) {
396
+ await tx.getRepository(ReleaseGood).update(
397
+ { id: In(incompleteRoIds), status: ORDER_STATUS.PROCESSING },
398
+ {
399
+ status: ORDER_STATUS.PARTIAL_PROCESSING,
400
+ updater: user,
401
+ updatedAt: new Date()
402
+ }
403
+ )
404
+ }
356
405
  }
357
406
 
358
407
  //find exist loading worksheet
@@ -368,31 +417,75 @@ async function completeZonePicking(worksheetId: string, context: any, isPartial:
368
417
  let roWithoutLoadingWorksheet = roIds.filter(roId => !existLoadingWorksheet.some(ws => ws.releaseGood.id === roId))
369
418
  let roWithLoadingWorksheet = roIds.filter(roId => existLoadingWorksheet.some(ws => ws.releaseGood.id === roId))
370
419
 
420
+ // Get disable-QC-in-loading setting
421
+ const disableQcSetting: Setting = await tx.getRepository(Setting).findOne({
422
+ where: {
423
+ domain,
424
+ name: 'disable-QC-in-loading'
425
+ }
426
+ })
427
+
428
+ const disableQc = disableQcSetting?.value === 'true'
429
+
371
430
  if (roWithoutLoadingWorksheet.length > 0) {
372
- await generateLoadingWorksheets(tx, domain, user, roWithoutLoadingWorksheet)
431
+ await generateLoadingWorksheets(tx, domain, user, roWithoutLoadingWorksheet, disableQc)
373
432
  }
374
433
  if (roWithLoadingWorksheet.length > 0) {
375
- //update picked oi to loading
376
- await tx.getRepository(OrderInventory).update(
377
- { releaseGood: In(roWithLoadingWorksheet), status: ORDER_INVENTORY_STATUS.PICKED },
378
- {
379
- status: ORDER_INVENTORY_STATUS.SORTING,
380
- updater: user,
381
- updatedAt: new Date()
382
- }
383
- )
384
- const loadingOrderInventories: OrderInventory[] = await tx.getRepository(OrderInventory).find({
385
- where: { releaseGood: In(roWithLoadingWorksheet), status: ORDER_INVENTORY_STATUS.SORTING }
386
- })
387
- //update loading wsd deactivated to executing
388
- await tx.getRepository(WorksheetDetail).update(
389
- {
390
- targetInventory: In(loadingOrderInventories.map(oi => oi.id)),
391
- type: WORKSHEET_TYPE.LOADING,
392
- status: WORKSHEET_STATUS.DEACTIVATED
393
- },
394
- { status: WORKSHEET_STATUS.EXECUTING, updatedAt: new Date(), updater: user }
395
- )
434
+ if (disableQc) {
435
+ // When QC is disabled: update PICKED to LOADING status and set sortedQty = pickedQty automatically
436
+ // Use query builder with raw SQL to set sortedQty = pickedQty in a single update
437
+ await tx
438
+ .getRepository(OrderInventory)
439
+ .createQueryBuilder()
440
+ .update(OrderInventory)
441
+ .set({
442
+ status: ORDER_INVENTORY_STATUS.LOADING,
443
+ sortedQty: () => '"picked_qty"', // Set sortedQty equal to pickedQty using raw SQL expression
444
+ updater: user,
445
+ updatedAt: new Date()
446
+ })
447
+ .where('release_good_id IN (:...roIds)', { roIds: roWithLoadingWorksheet })
448
+ .andWhere('status = :status', { status: ORDER_INVENTORY_STATUS.PICKED })
449
+ .execute()
450
+
451
+ const loadingOrderInventories: OrderInventory[] = await tx.getRepository(OrderInventory).find({
452
+ where: { releaseGood: In(roWithLoadingWorksheet), status: ORDER_INVENTORY_STATUS.LOADING }
453
+ })
454
+
455
+ //update loading wsd deactivated to executing
456
+ await tx.getRepository(WorksheetDetail).update(
457
+ {
458
+ targetInventory: In(loadingOrderInventories.map(oi => oi.id)),
459
+ type: WORKSHEET_TYPE.LOADING,
460
+ status: WORKSHEET_STATUS.DEACTIVATED
461
+ },
462
+ { status: WORKSHEET_STATUS.EXECUTING, updatedAt: new Date(), updater: user }
463
+ )
464
+ } else {
465
+ // When QC is enabled: update PICKED to SORTING status (existing behavior)
466
+ await tx.getRepository(OrderInventory).update(
467
+ { releaseGood: In(roWithLoadingWorksheet), status: ORDER_INVENTORY_STATUS.PICKED },
468
+ {
469
+ status: ORDER_INVENTORY_STATUS.SORTING,
470
+ updater: user,
471
+ updatedAt: new Date()
472
+ }
473
+ )
474
+
475
+ const loadingOrderInventories: OrderInventory[] = await tx.getRepository(OrderInventory).find({
476
+ where: { releaseGood: In(roWithLoadingWorksheet), status: ORDER_INVENTORY_STATUS.SORTING }
477
+ })
478
+
479
+ //update loading wsd deactivated to executing
480
+ await tx.getRepository(WorksheetDetail).update(
481
+ {
482
+ targetInventory: In(loadingOrderInventories.map(oi => oi.id)),
483
+ type: WORKSHEET_TYPE.LOADING,
484
+ status: WORKSHEET_STATUS.DEACTIVATED
485
+ },
486
+ { status: WORKSHEET_STATUS.EXECUTING, updatedAt: new Date(), updater: user }
487
+ )
488
+ }
396
489
  }
397
490
 
398
491
  const wsStatus = isPartial ? WORKSHEET_STATUS.PARTIAL_EXECUTING : WORKSHEET_STATUS.DONE
@@ -401,7 +494,13 @@ async function completeZonePicking(worksheetId: string, context: any, isPartial:
401
494
  return true
402
495
  }
403
496
 
404
- async function generateLoadingWorksheets(tx: EntityManager, domain: Domain, user: User, roWithoutLoadingWorksheet) {
497
+ async function generateLoadingWorksheets(
498
+ tx: EntityManager,
499
+ domain: Domain,
500
+ user: User,
501
+ roWithoutLoadingWorksheet,
502
+ disableQc: boolean
503
+ ) {
405
504
  const releaseGoods: ReleaseGood[] = await tx.getRepository(ReleaseGood).find({
406
505
  where: { id: In(roWithoutLoadingWorksheet), status: Not(In(ORDER_STATUS.OBSOLETE)) },
407
506
  relations: ['orderInventories', 'sortedBy']
@@ -442,15 +541,33 @@ async function generateLoadingWorksheets(tx: EntityManager, domain: Domain, user
442
541
 
443
542
  await tx.getRepository(WorksheetDetail).insert(loadingWorksheetDetails)
444
543
 
445
- //update picked oi to loading
446
- await tx.getRepository(OrderInventory).update(
447
- { id: In(orderInventories.map(oi => oi.id)), status: ORDER_INVENTORY_STATUS.PICKED },
448
- {
449
- status: ORDER_INVENTORY_STATUS.SORTING,
450
- updater: user,
451
- updatedAt: new Date()
452
- }
453
- )
544
+ // Update picked OrderInventories based on disableQc setting
545
+ if (disableQc) {
546
+ // When QC is disabled: update PICKED to LOADING status and set sortedQty = pickedQty automatically
547
+ await tx
548
+ .getRepository(OrderInventory)
549
+ .createQueryBuilder()
550
+ .update(OrderInventory)
551
+ .set({
552
+ status: ORDER_INVENTORY_STATUS.LOADING,
553
+ sortedQty: () => '"picked_qty"', // Set sortedQty equal to pickedQty using raw SQL expression
554
+ updater: user,
555
+ updatedAt: new Date()
556
+ })
557
+ .where('id IN (:...oiIds)', { oiIds: orderInventories.map(oi => oi.id) })
558
+ .andWhere('status = :status', { status: ORDER_INVENTORY_STATUS.PICKED })
559
+ .execute()
560
+ } else {
561
+ // When QC is enabled: update PICKED to SORTING status (existing behavior)
562
+ await tx.getRepository(OrderInventory).update(
563
+ { id: In(orderInventories.map(oi => oi.id)), status: ORDER_INVENTORY_STATUS.PICKED },
564
+ {
565
+ status: ORDER_INVENTORY_STATUS.SORTING,
566
+ updater: user,
567
+ updatedAt: new Date()
568
+ }
569
+ )
570
+ }
454
571
  }
455
572
  }
456
573
 
@@ -1,9 +1,8 @@
1
1
  import { EntityManager, getRepository, In, Not } from 'typeorm'
2
2
  import { Domain } from '@things-factory/biz-base'
3
- import { Worksheet, WorksheetDetail, WORKSHEET_STATUS } from '@things-factory/worksheet-base'
3
+ import { Worksheet, WorksheetDetail, WORKSHEET_TYPE } from '@things-factory/worksheet-base'
4
4
  import { Inventories, InventoryChange } from '@things-factory/warehouse-base'
5
5
  import { OrderInventory, ReleaseGood, ORDER_STATUS, ORDER_INVENTORY_STATUS } from '@things-factory/sales-base'
6
-
7
6
  export const zoneWorksheetOverview = {
8
7
  async zoneWorksheetOverview(_: any, { releaseGoods }, context: any) {
9
8
  const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
@@ -8,6 +8,7 @@ import * as Reports from './reports'
8
8
  import * as InventoryComparison from './inventory-comparison'
9
9
  import * as ShippingProvider from './shipping-provider'
10
10
  import * as WarehouseInventory from './warehouse-inventory-adjustment'
11
+ import * as RouteLabel from './route-label'
11
12
  import * as ZoneWorksheet from './zone-worksheet'
12
13
  import * as Outbound from './outbound'
13
14
 
@@ -21,6 +22,7 @@ export const queries = [
21
22
  InventoryComparison.Query,
22
23
  ShippingProvider.Query,
23
24
  Other.Query,
25
+ RouteLabel.Query,
24
26
  ZoneWorksheet.Query,
25
27
  Outbound.Query
26
28
  ]
@@ -28,6 +30,7 @@ export const queries = [
28
30
  export const mutations = [
29
31
  Other.Mutation,
30
32
  WarehouseInventory.Mutation,
33
+ RouteLabel.Mutation,
31
34
  ZoneWorksheet.Mutation,
32
35
  Outbound.Mutation
33
36
  ]
@@ -41,6 +44,7 @@ export const types = [
41
44
  ...InventoryComparison.Types,
42
45
  ...ShippingProvider.Types,
43
46
  ...Other.Types,
47
+ ...RouteLabel.Types,
44
48
  ...ZoneWorksheet.Types,
45
49
  ...Outbound.Types
46
50
  ]
@@ -12,7 +12,13 @@ export const Query = `
12
12
 
13
13
  export const Mutation = `
14
14
  getLoadingTask: LoadingWorksheet @privilege(category: "worksheet_control", privilege: "mutation") @transaction
15
- sortItem(releaseGoodNo: String!, productBarcode: String!, qty: Float!, worksheetDetails: [Object!]): Boolean @transaction
15
+ sortItem(
16
+ releaseGoodNo: String!
17
+ productBarcode: String!
18
+ qty: Float!
19
+ worksheetDetails: [Object!]
20
+ toteNo: String
21
+ ): Boolean @transaction
16
22
  loadByQty(worksheetDetailPatch: LoadingWorksheetDetails): Boolean @transaction
17
23
  undoLoad(doId: String!): Boolean @transaction
18
24
  warehouseReturn (releaseGoodNo: String!, worksheetDetails: [Object!]): Worksheet @privilege(category: "worksheet_control", privilege: "mutation") @transaction
@@ -0,0 +1,18 @@
1
+ import {
2
+ RouteLabelReleaseGood,
3
+ RouteLabelSequenceLogEntry,
4
+ RouteLabelSequenceReservation,
5
+ RouteLabelSequenceStatus
6
+ } from './route-label-sequence'
7
+
8
+ export const Query = `
9
+ routeLabelReleaseGoodsInExecutingWorksheet(partnerId: String!, refNo: String, worksheetType: String!, seqField: String): [RouteLabelReleaseGood] @transaction
10
+ routeLabelSequenceStatuses(releaseGoodIds: [String!]!, field: String!): [RouteLabelSequenceStatus] @transaction
11
+ routeLabelSequenceLogs(releaseGoodId: String!, field: String!): [RouteLabelSequenceLogEntry] @transaction
12
+ `
13
+
14
+ export const Mutation = `
15
+ reserveRouteLabelSequence(releaseGoodId: String!, field: String!, qty: Int!): RouteLabelSequenceReservation @transaction
16
+ `
17
+
18
+ export const Types = [RouteLabelReleaseGood, RouteLabelSequenceStatus, RouteLabelSequenceReservation, RouteLabelSequenceLogEntry]
@@ -0,0 +1,50 @@
1
+ import gql from 'graphql-tag'
2
+
3
+ export const RouteLabelReleaseGood = gql`
4
+ type RouteLabelReleaseGood {
5
+ id: String!
6
+ name: String!
7
+ refNo: String
8
+ refNo2: String
9
+ refNo3: String
10
+ routeId: String
11
+ storeId: String
12
+ storeName: String
13
+ stopId: String
14
+ createdAt: String
15
+ lastSeq: Int
16
+ lastPrintedBy: String
17
+ }
18
+ `
19
+
20
+ export const RouteLabelSequenceStatus = gql`
21
+ type RouteLabelSequenceStatus {
22
+ releaseGoodId: String!
23
+ field: String!
24
+ lastSeq: Int!
25
+ history: [String!]
26
+ lastPrintedBy: String
27
+ }
28
+ `
29
+
30
+ export const RouteLabelSequenceReservation = gql`
31
+ type RouteLabelSequenceReservation {
32
+ releaseGoodId: String!
33
+ field: String!
34
+ from: Int!
35
+ to: Int!
36
+ }
37
+ `
38
+
39
+ export const RouteLabelSequenceLogEntry = gql`
40
+ type RouteLabelSequenceLogEntry {
41
+ id: String!
42
+ releaseGoodId: String!
43
+ field: String!
44
+ from: Int!
45
+ to: Int!
46
+ qty: Int!
47
+ createdAt: String!
48
+ creatorName: String
49
+ }
50
+ `
package/server/index.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  export * from './graphql'
2
2
  export * from './migrations'
3
+ export * from './entities'
3
4
 
4
- import './routes'
5
+ import './routes'
6
+
7
+ // Register setting validators at module load time (before schema is built)
8
+ // This ensures validators are available when mutations are called
9
+ import { registerOperatoWmsSettingValidators } from './validators/setting-validators'
10
+ registerOperatoWmsSettingValidators()