@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.
- package/client/pages/outbound/loading-v2/delivery-order-info.js +21 -7
- package/client/pages/outbound/loading-v2/loading-execution-base.js +82 -18
- package/client/pages/outbound/loading-v2/loading-execution.js +3 -1
- package/client/pages/outbound/picking-product.js +34 -0
- package/client/pages/outbound/sorting-product.js +30 -0
- package/client/pages/outbound/tote-popup.js +226 -36
- package/dist-server/graphql/resolvers/outbound/index.js +2 -1
- package/dist-server/graphql/resolvers/outbound/index.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/undo-load.js +2 -1
- package/dist-server/graphql/resolvers/outbound/undo-load.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/unsort-item.js +65 -0
- package/dist-server/graphql/resolvers/outbound/unsort-item.js.map +1 -0
- package/dist-server/graphql/resolvers/reports/tote-loading-qc-report.js +19 -10
- package/dist-server/graphql/resolvers/reports/tote-loading-qc-report.js.map +1 -1
- package/dist-server/graphql/types/outbound/index.js +4 -0
- package/dist-server/graphql/types/outbound/index.js.map +1 -1
- package/package.json +3 -3
- package/server/graphql/resolvers/outbound/index.ts +2 -0
- package/server/graphql/resolvers/outbound/undo-load.ts +2 -1
- package/server/graphql/resolvers/outbound/unsort-item.ts +78 -0
- package/server/graphql/resolvers/reports/tote-loading-qc-report.ts +34 -11
- package/server/graphql/types/outbound/index.ts +4 -0
- package/translations/en.json +7 -0
- package/translations/ko.json +6 -0
- package/translations/ms.json +6 -0
- 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('
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
prev
|
|
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
|
-
|
|
130
|
-
prev[reducedItemIndex].
|
|
131
|
-
|
|
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
|
-
|
|
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
|
package/translations/en.json
CHANGED
|
@@ -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",
|
package/translations/ko.json
CHANGED
|
@@ -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",
|
package/translations/ms.json
CHANGED
|
@@ -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",
|
package/translations/zh.json
CHANGED
|
@@ -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": "跟踪号已存在于其他清单列表中",
|