@things-factory/operato-wms 4.3.760 → 4.3.762

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 (26) hide show
  1. package/client/pages/outbound/loading-v2/delivery-order-info.js +21 -7
  2. package/client/pages/outbound/loading-v2/loading-execution-base.js +82 -18
  3. package/client/pages/outbound/loading-v2/loading-execution.js +3 -1
  4. package/client/pages/outbound/picking-product.js +34 -0
  5. package/client/pages/outbound/sorting-product.js +30 -0
  6. package/client/pages/outbound/tote-popup.js +226 -36
  7. package/dist-server/graphql/resolvers/outbound/index.js +2 -1
  8. package/dist-server/graphql/resolvers/outbound/index.js.map +1 -1
  9. package/dist-server/graphql/resolvers/outbound/undo-load.js +2 -1
  10. package/dist-server/graphql/resolvers/outbound/undo-load.js.map +1 -1
  11. package/dist-server/graphql/resolvers/outbound/unsort-item.js +65 -0
  12. package/dist-server/graphql/resolvers/outbound/unsort-item.js.map +1 -0
  13. package/dist-server/graphql/resolvers/reports/tote-loading-qc-report.js +19 -10
  14. package/dist-server/graphql/resolvers/reports/tote-loading-qc-report.js.map +1 -1
  15. package/dist-server/graphql/types/outbound/index.js +4 -0
  16. package/dist-server/graphql/types/outbound/index.js.map +1 -1
  17. package/package.json +3 -3
  18. package/server/graphql/resolvers/outbound/index.ts +2 -0
  19. package/server/graphql/resolvers/outbound/undo-load.ts +2 -1
  20. package/server/graphql/resolvers/outbound/unsort-item.ts +78 -0
  21. package/server/graphql/resolvers/reports/tote-loading-qc-report.ts +34 -11
  22. package/server/graphql/types/outbound/index.ts +4 -0
  23. package/translations/en.json +7 -0
  24. package/translations/ko.json +6 -0
  25. package/translations/ms.json +6 -0
  26. package/translations/zh.json +6 -0
@@ -33,7 +33,8 @@ export const undoLoad = {
33
33
  targetInventory: { id: In(doInventoryIds) },
34
34
  type: WORKSHEET_TYPE.LOADING,
35
35
  status: WORKSHEET_STATUS.DONE
36
- }
36
+ },
37
+ relations: ['targetInventory']
37
38
  })
38
39
 
39
40
  // Create a map for O(1) lookup of worksheet details by targetInventory.id
@@ -0,0 +1,78 @@
1
+ import { EntityManager, Not } from 'typeorm'
2
+ import { User } from '@things-factory/auth-base'
3
+ import { Domain } from '@things-factory/shell'
4
+ import { ORDER_INVENTORY_STATUS, OrderInventory, OrderToteItem } from '@things-factory/sales-base'
5
+
6
+ export const unsortItem = {
7
+ async unsortItem(_: any, { orderToteItemId, reason }, context: any) {
8
+ const { domain, user, tx }: { domain: Domain; user: User; tx: EntityManager } = context.state
9
+
10
+ // Find the order tote item with relations
11
+ const orderToteItem = (await tx.getRepository(OrderToteItem).findOne({
12
+ where: { domain, id: orderToteItemId },
13
+ relations: ['orderTote', 'orderInventory']
14
+ })) as OrderToteItem | null
15
+
16
+ if (!orderToteItem) {
17
+ throw new Error('Order tote item not found')
18
+ }
19
+
20
+ // Check if tote is sealed - cannot undo if sealed
21
+ if (orderToteItem.orderTote?.closedDate) {
22
+ throw new Error('Cannot remove item from sealed tote.')
23
+ }
24
+
25
+ const orderInventory: OrderInventory = orderToteItem.orderInventory
26
+ const qtyToRemove = orderToteItem.qty
27
+
28
+ if (!orderInventory) {
29
+ throw new Error('Associated order inventory not found')
30
+ }
31
+
32
+ // Validate that we can reduce the sorted quantity
33
+ if (orderInventory.sortedQty < qtyToRemove) {
34
+ throw new Error(`Cannot remove ${qtyToRemove} items. Current sorted quantity is ${orderInventory.sortedQty}`)
35
+ }
36
+
37
+ // Calculate new sorted quantity
38
+ const newSortedQty = orderInventory.sortedQty - qtyToRemove
39
+
40
+ // Determine new status - revert to PICKED if no more sorted items
41
+ let newStatus = orderInventory.status
42
+ if (newSortedQty === 0 && orderInventory.status === ORDER_INVENTORY_STATUS.LOADING) {
43
+ // Check if there are other order tote items for this order inventory
44
+ const otherToteItems = await tx.getRepository(OrderToteItem).count({
45
+ where: {
46
+ domain,
47
+ orderInventory: { id: orderInventory.id },
48
+ id: Not(orderToteItemId)
49
+ }
50
+ })
51
+
52
+ // Only revert to PICKED if this is the last tote item (no more sorted items)
53
+ if (otherToteItems === 0) {
54
+ newStatus = ORDER_INVENTORY_STATUS.SORTING
55
+ }
56
+ }
57
+
58
+ // Update the order inventory
59
+ await tx.getRepository(OrderInventory).update(orderInventory.id, {
60
+ sortedQty: newSortedQty,
61
+ status: newStatus,
62
+ updatedAt: new Date(),
63
+ updater: user
64
+ })
65
+
66
+ // Delete the order tote item
67
+ await tx.getRepository(OrderToteItem).delete(orderToteItem.id)
68
+
69
+ // Log the undo action if reason provided
70
+ if (reason) {
71
+ console.log(
72
+ `[UNSORT] User ${user.name} removed ${qtyToRemove} items from tote ${orderToteItem.orderTote?.name}. Reason: ${reason}`
73
+ )
74
+ }
75
+
76
+ return true
77
+ }
78
+ }
@@ -62,7 +62,8 @@ export const loadingQcReport = {
62
62
  ELSE NULL
63
63
  END
64
64
  END`,
65
- 'totalCarton')
65
+ 'totalCarton'
66
+ )
66
67
  .addSelect('case when oti.qty is null then oi.release_qty else oti.qty end', 'orderQuantity')
67
68
  .addSelect('ot.name', 'tote')
68
69
  .addSelect('case when ot.tote_id is null then false else true end', 'toteBox')
@@ -74,14 +75,17 @@ export const loadingQcReport = {
74
75
  .leftJoin('order_totes', 'ot', 'ot.id = oti.order_tote_id')
75
76
  .leftJoin('order_tote_seals', 'ots', 'ots.order_tote_id = ot.id')
76
77
  .innerJoin('product_details', 'pd', 'oi.product_detail_id = pd.id')
77
- .where('rg.status = :status', { status: 'LOADING' })
78
- .andWhere('bizplace.id = :bizplaceId', { bizplaceId: bizplaceFilter.value })
78
+ .where('bizplace.id = :bizplaceId', { bizplaceId: bizplaceFilter.value })
79
79
  .andWhere('rg.release_date <= :toDate', { toDate: toDateFilter.value })
80
80
  .andWhere('rg.release_date >= :fromDate', { fromDate: fromDateFilter.value })
81
81
  .andWhere('w.type = :type', { type: 'LOADING' })
82
82
  .andWhere('rg.domain_id = :domainId', { domainId: domain.id })
83
+ .andWhere('rg.name IS NOT NULL')
84
+ .andWhere('rg.status NOT IN (:...excludedStatuses)', {
85
+ excludedStatuses: ['PENDING_CANCEL', 'CANCELLED', 'PENDING_WORKSHEET', 'READY_TO_PICK']
86
+ })
83
87
  .groupBy('bizplace.name')
84
- .addGroupBy('rg.name ')
88
+ .addGroupBy('rg.name')
85
89
  .addGroupBy('rg.route_id')
86
90
  .addGroupBy('rg.stop_id')
87
91
  .addGroupBy('rg.release_date')
@@ -121,21 +125,40 @@ export const loadingQcReport = {
121
125
 
122
126
  items = items.reduce((prev, curr, idx) => {
123
127
  if (curr.tote) {
124
- let reducedItemIndex = prev.findIndex(itm => itm.releaseGoodNo == curr.releaseGoodNo && itm.tote == curr.tote)
128
+ // Find duplicate by releaseGoodNo, tote, and additional fields to ensure proper grouping
129
+ let reducedItemIndex = prev.findIndex(
130
+ itm =>
131
+ itm.releaseGoodNo === curr.releaseGoodNo &&
132
+ itm.tote === curr.tote &&
133
+ itm.bizplaceName === curr.bizplaceName
134
+ )
125
135
  if (reducedItemIndex == -1) {
126
- prev.push(curr)
127
- prev[prev.length - 1].totalSeal = 1
136
+ // Ensure numeric fields are properly typed when adding new item
137
+ prev.push({
138
+ ...curr,
139
+ orderQuantity: Number(curr.orderQuantity) || 0,
140
+ totalCarton: Number(curr.totalCarton) || 0,
141
+ totalSeal: curr.sealId ? 1 : 0
142
+ })
128
143
  } else {
129
- prev[reducedItemIndex].orderQuantity += curr.orderQuantity
130
- prev[reducedItemIndex].totalCarton += curr.totalCarton
131
- if (!prev[reducedItemIndex].sealId.includes(curr.sealId)) {
144
+ // Aggregate quantities with explicit numeric conversion
145
+ prev[reducedItemIndex].orderQuantity += Number(curr.orderQuantity) || 0
146
+ prev[reducedItemIndex].totalCarton += Number(curr.totalCarton) || 0
147
+ // Concatenate seal IDs only if they're unique and not null
148
+ if (curr.sealId && !prev[reducedItemIndex].sealId.includes(curr.sealId)) {
132
149
  prev[reducedItemIndex].sealId = prev[reducedItemIndex].sealId.concat(', ', curr.sealId)
133
150
  prev[reducedItemIndex].totalSeal++
134
151
  }
135
152
  }
136
153
  return prev
137
154
  } else {
138
- prev.push(curr)
155
+ // Ensure numeric fields are properly typed for items without totes
156
+ prev.push({
157
+ ...curr,
158
+ orderQuantity: Number(curr.orderQuantity) || 0,
159
+ totalCarton: Number(curr.totalCarton) || 0,
160
+ totalSeal: 0
161
+ })
139
162
  return prev
140
163
  }
141
164
  }, [])
@@ -19,6 +19,10 @@ export const Mutation = `
19
19
  worksheetDetails: [Object!]
20
20
  toteNo: String
21
21
  ): Boolean @transaction
22
+ unsortItem(
23
+ orderToteItemId: String!
24
+ reason: String
25
+ ): Boolean @transaction
22
26
  loadByQty(worksheetDetailPatch: LoadingWorksheetDetails): Boolean @transaction
23
27
  undoLoad(doId: String!): Boolean @transaction
24
28
  warehouseReturn (releaseGoodNo: String!, worksheetDetails: [Object!]): Worksheet @privilege(category: "worksheet_control", privilege: "mutation") @transaction
@@ -1907,6 +1907,13 @@
1907
1907
  "text.tote_is_not_scanned": "tote is not scanned",
1908
1908
  "text.tote_number": "tote number",
1909
1909
  "text.tote_status_does_not_exist": "tote status does not exist",
1910
+ "text.are_you_sure_to_remove_x_from_tote": "are you sure you want to remove {x} from this tote?",
1911
+ "text.item_removed_from_tote": "item removed from tote",
1912
+ "text.items_removed_from_tote": "{count} items removed from tote",
1913
+ "text.nothing_selected": "nothing selected",
1914
+ "text.please_select_items_to_undo": "please select items to undo",
1915
+ "text.viewing_items_in_tote": "viewing items in tote",
1916
+ "text.list_refreshed": "list refreshed",
1910
1917
  "text.tracking_no_added_successfully": "tracking no added successfully",
1911
1918
  "text.tracking_no_already_exist_in_manifest_list": "tracking no already exist in manifest list",
1912
1919
  "text.tracking_no_already_exist_in_other_manifest_list": "tracking no already exist in other manifest list",
@@ -1892,6 +1892,12 @@
1892
1892
  "text.tote_is_not_scanned": "[ko] tote is not scanned",
1893
1893
  "text.tote_number": "[ko] tote number",
1894
1894
  "text.tote_status_does_not_exist": "[ko] tote status does not exist",
1895
+ "text.are_you_sure_to_remove_x_from_tote": "[ko] are you sure you want to remove {x} from this tote?",
1896
+ "text.item_removed_from_tote": "[ko] item removed from tote",
1897
+ "text.items_removed_from_tote": "[ko] {count} items removed from tote",
1898
+ "text.please_select_items_to_undo": "[ko] please select items to undo",
1899
+ "text.viewing_items_in_tote": "[ko] viewing items in tote",
1900
+ "text.list_refreshed": "[ko] list refreshed",
1895
1901
  "text.tracking_no_added_successfully": "[ko] tracking no added successfully",
1896
1902
  "text.tracking_no_already_exist_in_manifest_list": "[ko] tracking no already exist in manifest list",
1897
1903
  "text.tracking_no_already_exist_in_other_manifest_list": "[ko]tracking no already exist in other manifest list",
@@ -1944,6 +1944,12 @@
1944
1944
  "text.tote_is_not_scanned": "kotak tote tidak dipindai",
1945
1945
  "text.tote_number": "nombor tote",
1946
1946
  "text.tote_status_does_not_exist": "status tote tidak wujud",
1947
+ "text.are_you_sure_to_remove_x_from_tote": "[ms] are you sure you want to remove {x} from this tote?",
1948
+ "text.item_removed_from_tote": "[ms] item removed from tote",
1949
+ "text.items_removed_from_tote": "[ms] {count} items removed from tote",
1950
+ "text.please_select_items_to_undo": "[ms] please select items to undo",
1951
+ "text.viewing_items_in_tote": "[ms] viewing items in tote",
1952
+ "text.list_refreshed": "[ms] list refreshed",
1947
1953
  "text.tracking_no_added_successfully": "no. penjejak telah ditambah",
1948
1954
  "text.tracking_no_already_exist_in_manifest_list": "no. penjejak sudah wujud dalam senarai manifest",
1949
1955
  "text.tracking_no_already_exist_in_other_manifest_list": "no. penjejak sudah wujud dalam senarai manifest lain",
@@ -1977,6 +1977,12 @@
1977
1977
  "text.tote_is_not_scanned": "箱子未扫描",
1978
1978
  "text.tote_number": "箱子编号",
1979
1979
  "text.tote_status_does_not_exist": "箱子状态不存在",
1980
+ "text.are_you_sure_to_remove_x_from_tote": "[zh] are you sure you want to remove {x} from this tote?",
1981
+ "text.item_removed_from_tote": "[zh] item removed from tote",
1982
+ "text.items_removed_from_tote": "[zh] {count} items removed from tote",
1983
+ "text.please_select_items_to_undo": "[zh] please select items to undo",
1984
+ "text.viewing_items_in_tote": "[zh] viewing items in tote",
1985
+ "text.list_refreshed": "[zh] list refreshed",
1980
1986
  "text.tracking_no_added_successfully": "跟踪号已添加成功",
1981
1987
  "text.tracking_no_already_exist_in_manifest_list": "跟踪号已存在于清单列表中",
1982
1988
  "text.tracking_no_already_exist_in_other_manifest_list": "跟踪号已存在于其他清单列表中",