@things-factory/operato-wms 4.3.761 → 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/loading-execution-base.js +40 -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/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 +2 -2
- package/server/graphql/resolvers/outbound/index.ts +2 -0
- 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
|
@@ -448,19 +448,58 @@ export class LoadingExecutionBase extends LitElement {
|
|
|
448
448
|
throw new Error(i18next.t('text.no_release_order_selected'))
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
+
// Query for minimum seal number setting
|
|
452
|
+
let minimumSealNumber = 0
|
|
453
|
+
try {
|
|
454
|
+
const validationResponse = await client.query({
|
|
455
|
+
query: VALIDATE_QC_SEALS,
|
|
456
|
+
variables: { releaseGoodNo: orderNo },
|
|
457
|
+
context: gqlContext()
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
|
|
461
|
+
minimumSealNumber = validationResponse.data.validateQcSeals.minimumSealNumber || 0
|
|
462
|
+
}
|
|
463
|
+
} catch (err) {
|
|
464
|
+
// If query fails, default to 0 (don't block tote scanning)
|
|
465
|
+
console.warn('Failed to fetch minimum seal number:', err)
|
|
466
|
+
}
|
|
467
|
+
|
|
451
468
|
openPopup(
|
|
452
469
|
html`
|
|
453
470
|
<tote-popup
|
|
454
471
|
.orderNo="${orderNo}"
|
|
455
472
|
.toteNo="${this.toteNo}"
|
|
456
473
|
.bizplace="${bizplace}"
|
|
474
|
+
.minimumSealNumber="${minimumSealNumber}"
|
|
457
475
|
@completed="${async e => {
|
|
458
476
|
this.toteNo = e.detail
|
|
459
477
|
// Check seal status after tote is sealed (may have changed)
|
|
460
478
|
await this._checkAndUpdateSealStatus()
|
|
479
|
+
|
|
480
|
+
// Refresh data for both QC and LOADING tabs to reflect any undone items
|
|
481
|
+
// Use the same logic as validateSorting to determine which order/bin to refresh
|
|
482
|
+
const orderNoForRefresh = this.worksheet?.isReleaseGoodScan
|
|
483
|
+
? this.worksheet?.releaseGood?.name
|
|
484
|
+
: this._useQcSku
|
|
485
|
+
? this.worksheet?.binLocationName
|
|
486
|
+
: this.worksheet?.releaseGood?.name
|
|
487
|
+
const useQcSkuForRefresh = this._useQcSku && !this.worksheet?.isReleaseGoodScan
|
|
488
|
+
|
|
489
|
+
const [qcData, loadingData] = await Promise.all([
|
|
490
|
+
this.fetchData(orderNoForRefresh, 'qc', useQcSkuForRefresh),
|
|
491
|
+
this.fetchData(orderNoForRefresh, 'loading', useQcSkuForRefresh)
|
|
492
|
+
])
|
|
493
|
+
|
|
494
|
+
// QC tab: items that still need QC (sortedQty < pickedQty)
|
|
495
|
+
this.qcData = qcData.filter(data => data?.sortedQty < data?.pickedQty) || []
|
|
496
|
+
// Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
|
|
497
|
+
this.loadingData =
|
|
498
|
+
loadingData.filter(data => data?.sortedQty >= data?.pickedQty && data.status != WORKSHEET_STATUS.DONE.value) || []
|
|
499
|
+
|
|
461
500
|
// Update button visibility after sealing (Tote button may need to be hidden if sealing is complete)
|
|
462
501
|
this.getButton()
|
|
463
|
-
// Update QC banner state after
|
|
502
|
+
// Update QC banner state after data is refreshed
|
|
464
503
|
if (this._updateQcBannerState) {
|
|
465
504
|
await this._updateQcBannerState()
|
|
466
505
|
}
|
|
@@ -33,6 +33,17 @@ import {
|
|
|
33
33
|
} from '../constants'
|
|
34
34
|
|
|
35
35
|
const PICKING_TYPES = { PICKING, BATCH_PICKING }
|
|
36
|
+
|
|
37
|
+
const VALIDATE_QC_SEALS = gql`
|
|
38
|
+
query validateQcSeals($releaseGoodNo: String!) {
|
|
39
|
+
validateQcSeals(releaseGoodNo: $releaseGoodNo) {
|
|
40
|
+
valid
|
|
41
|
+
error
|
|
42
|
+
minimumSealNumber
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
|
|
36
47
|
class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
37
48
|
static get properties() {
|
|
38
49
|
return {
|
|
@@ -976,14 +987,37 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
976
987
|
|
|
977
988
|
async openTotePopup() {
|
|
978
989
|
await sleep(1010)
|
|
990
|
+
|
|
991
|
+
// Query for minimum seal number setting
|
|
992
|
+
let minimumSealNumber = 0
|
|
993
|
+
try {
|
|
994
|
+
const validationResponse = await client.query({
|
|
995
|
+
query: VALIDATE_QC_SEALS,
|
|
996
|
+
variables: { releaseGoodNo: this.releaseGoodNo },
|
|
997
|
+
context: gqlContext()
|
|
998
|
+
})
|
|
999
|
+
|
|
1000
|
+
if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
|
|
1001
|
+
minimumSealNumber = validationResponse.data.validateQcSeals.minimumSealNumber || 0
|
|
1002
|
+
}
|
|
1003
|
+
} catch (err) {
|
|
1004
|
+
// If query fails, default to 0 (don't block tote scanning)
|
|
1005
|
+
console.warn('Failed to fetch minimum seal number:', err)
|
|
1006
|
+
}
|
|
1007
|
+
|
|
979
1008
|
openPopup(
|
|
980
1009
|
html`
|
|
981
1010
|
<tote-popup
|
|
982
1011
|
.orderNo="${this.releaseGoodNo}"
|
|
983
1012
|
.toteNo="${this.toteNo}"
|
|
984
1013
|
.bizplace="${this._bizplaceName}"
|
|
1014
|
+
.minimumSealNumber="${minimumSealNumber}"
|
|
985
1015
|
@completed="${async e => {
|
|
986
1016
|
this.toteNo = e.detail
|
|
1017
|
+
// Refresh the item list to reflect any undone items
|
|
1018
|
+
await this.fetchInventories(
|
|
1019
|
+
this.refPickingType === PICKING_TYPES.PICKING.value ? this.releaseGoodNo : this.taskNo
|
|
1020
|
+
)
|
|
987
1021
|
}}"
|
|
988
1022
|
></tote-popup>
|
|
989
1023
|
`,
|
|
@@ -21,6 +21,16 @@ import { fetchSettingRule } from '../../fetch-setting-value'
|
|
|
21
21
|
import { WORKSHEET_STATUS } from '../constants'
|
|
22
22
|
import { decodeQR, fetchPageSettings, isValidHttpUrl } from '../../util'
|
|
23
23
|
|
|
24
|
+
const VALIDATE_QC_SEALS = gql`
|
|
25
|
+
query validateQcSeals($releaseGoodNo: String!) {
|
|
26
|
+
validateQcSeals(releaseGoodNo: $releaseGoodNo) {
|
|
27
|
+
valid
|
|
28
|
+
error
|
|
29
|
+
minimumSealNumber
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`
|
|
33
|
+
|
|
24
34
|
class SortingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
25
35
|
static get properties() {
|
|
26
36
|
return {
|
|
@@ -515,14 +525,34 @@ class SortingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
515
525
|
|
|
516
526
|
async _openTotePopup() {
|
|
517
527
|
await sleep(1010)
|
|
528
|
+
|
|
529
|
+
// Query for minimum seal number setting
|
|
530
|
+
let minimumSealNumber = 0
|
|
531
|
+
try {
|
|
532
|
+
const validationResponse = await client.query({
|
|
533
|
+
query: VALIDATE_QC_SEALS,
|
|
534
|
+
variables: { releaseGoodNo: this._selectedReleaseGood }
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
|
|
538
|
+
minimumSealNumber = validationResponse.data.validateQcSeals.minimumSealNumber || 0
|
|
539
|
+
}
|
|
540
|
+
} catch (err) {
|
|
541
|
+
// If query fails, default to 0 (don't block tote scanning)
|
|
542
|
+
console.warn('Failed to fetch minimum seal number:', err)
|
|
543
|
+
}
|
|
544
|
+
|
|
518
545
|
openPopup(
|
|
519
546
|
html`
|
|
520
547
|
<tote-popup
|
|
521
548
|
.orderNo="${this._selectedReleaseGood}"
|
|
522
549
|
.toteNo="${this.toteNo}"
|
|
523
550
|
.bizplace="${this.bizplaceName}"
|
|
551
|
+
.minimumSealNumber="${minimumSealNumber}"
|
|
524
552
|
@completed="${async e => {
|
|
525
553
|
this.toteNo = e.detail
|
|
554
|
+
// Refresh the item list to reflect any undone items
|
|
555
|
+
await this._fetchItemList(this._selectedReleaseGood)
|
|
526
556
|
}}"
|
|
527
557
|
></tote-popup>
|
|
528
558
|
`,
|
|
@@ -6,7 +6,7 @@ import { css, html, LitElement } from 'lit-element'
|
|
|
6
6
|
|
|
7
7
|
import { MultiColumnFormStyles, SingleColumnFormStyles } from '@things-factory/form-ui'
|
|
8
8
|
import { i18next, localize } from '@things-factory/i18n-base'
|
|
9
|
-
import { client } from '@things-factory/shell'
|
|
9
|
+
import { client, CustomAlert } from '@things-factory/shell'
|
|
10
10
|
import { ScrollbarStyles } from '@things-factory/styles'
|
|
11
11
|
import { isMobileDevice } from '@things-factory/utils'
|
|
12
12
|
import { TOTE_STATUS } from '../constants'
|
|
@@ -20,6 +20,7 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
20
20
|
data: Object,
|
|
21
21
|
orderNo: String,
|
|
22
22
|
toteNo: String,
|
|
23
|
+
minimumSealNumber: Number,
|
|
23
24
|
_totalSeal: Number,
|
|
24
25
|
_selectedToteNo: String,
|
|
25
26
|
_toteNoList: Object,
|
|
@@ -76,11 +77,26 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
76
77
|
.break {
|
|
77
78
|
word-wrap: break-word;
|
|
78
79
|
}
|
|
80
|
+
.mobile-hint {
|
|
81
|
+
padding: 10px;
|
|
82
|
+
background-color: var(--primary-light-color, #f0f0f0);
|
|
83
|
+
border-left: 4px solid var(--primary-color, #2196f3);
|
|
84
|
+
margin: 10px;
|
|
85
|
+
font-size: 0.9em;
|
|
86
|
+
}
|
|
87
|
+
.refresh-button {
|
|
88
|
+
margin: 10px;
|
|
89
|
+
display: flex;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
}
|
|
79
92
|
`
|
|
80
93
|
]
|
|
81
94
|
}
|
|
82
95
|
|
|
83
96
|
render() {
|
|
97
|
+
const shouldShowSealSection = this._shouldShowSealSection()
|
|
98
|
+
const sealInputDisabled = !this.toteNo || this.toteNo === ''
|
|
99
|
+
|
|
84
100
|
return html`
|
|
85
101
|
<form class="multi-column-form">
|
|
86
102
|
<fieldset>
|
|
@@ -121,34 +137,46 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
121
137
|
</fieldset>
|
|
122
138
|
</form>
|
|
123
139
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
140
|
+
${shouldShowSealSection
|
|
141
|
+
? html`
|
|
142
|
+
<form class="multi-column-form">
|
|
143
|
+
<fieldset>
|
|
144
|
+
<label>${i18next.t('field.seal_no')}</label>
|
|
145
|
+
<barcode-scanable-input
|
|
146
|
+
name="sealNo"
|
|
147
|
+
custom-input
|
|
148
|
+
?disabled="${sealInputDisabled}"
|
|
149
|
+
@keypress="${e => {
|
|
150
|
+
if (e.keyCode === 13) {
|
|
151
|
+
e.preventDefault()
|
|
152
|
+
this.sealNo = this.sealNoInput.value
|
|
153
|
+
if (this.toteNo) {
|
|
154
|
+
this._sealTote()
|
|
155
|
+
} else {
|
|
156
|
+
this._showToast({ message: i18next.t('text.tote_is_not_scanned') })
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}}"
|
|
160
|
+
></barcode-scanable-input>
|
|
161
|
+
</fieldset>
|
|
162
|
+
|
|
163
|
+
<label>${i18next.t('field.scanned')} (${this._totalSeal || 0})</label>
|
|
164
|
+
<select name="sealNoList">
|
|
165
|
+
<option value="">-- ${i18next.t('text.seal_number')} --</option>
|
|
166
|
+
${(this._sealNoList?.orderToteSeals || []).map(
|
|
167
|
+
list => html` <option value="${list.name}">${list.name}</option> `
|
|
168
|
+
)}
|
|
169
|
+
</select>
|
|
170
|
+
</form>
|
|
171
|
+
`
|
|
172
|
+
: ''}
|
|
173
|
+
${isMobileDevice() && this._selectedToteNo?.length > 0
|
|
174
|
+
? html`
|
|
175
|
+
<div class="mobile-hint">
|
|
176
|
+
${i18next.t('text.viewing_items_in_tote')}: <strong>${this._selectedToteNo[0].name}</strong>
|
|
177
|
+
</div>
|
|
178
|
+
`
|
|
179
|
+
: ''}
|
|
152
180
|
|
|
153
181
|
<div class="grist-container">
|
|
154
182
|
<div class="grist">
|
|
@@ -161,9 +189,35 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
161
189
|
</div>
|
|
162
190
|
</div>
|
|
163
191
|
|
|
192
|
+
${isMobileDevice()
|
|
193
|
+
? html`
|
|
194
|
+
<div class="refresh-button">
|
|
195
|
+
<mwc-button
|
|
196
|
+
icon="refresh"
|
|
197
|
+
@click=${async () => {
|
|
198
|
+
if (this.grist) {
|
|
199
|
+
await this.grist.fetch()
|
|
200
|
+
this._showToast({ type: 'info', message: i18next.t('text.list_refreshed') })
|
|
201
|
+
}
|
|
202
|
+
}}
|
|
203
|
+
label="${i18next.t('button.refresh')}"
|
|
204
|
+
></mwc-button>
|
|
205
|
+
</div>
|
|
206
|
+
`
|
|
207
|
+
: ''}
|
|
208
|
+
|
|
164
209
|
<div class="button-container">
|
|
210
|
+
${this._shouldShowUndoButton()
|
|
211
|
+
? html`
|
|
212
|
+
<mwc-button
|
|
213
|
+
@click=${this._undoSelectedItems.bind(this)}
|
|
214
|
+
raised
|
|
215
|
+
style="--mdc-theme-primary: var(--status-warning-color, #ff9800); margin-right: 10px;"
|
|
216
|
+
label="${i18next.t('button.undo')}"
|
|
217
|
+
></mwc-button>
|
|
218
|
+
`
|
|
219
|
+
: ''}
|
|
165
220
|
<mwc-button
|
|
166
|
-
danger
|
|
167
221
|
@click=${() => {
|
|
168
222
|
this.dispatchEvent(
|
|
169
223
|
new CustomEvent('completed', {
|
|
@@ -188,11 +242,25 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
188
242
|
}
|
|
189
243
|
|
|
190
244
|
get sealNoInput() {
|
|
191
|
-
return this.shadowRoot.querySelector('barcode-scanable-input[name=sealNo]')
|
|
245
|
+
return this.shadowRoot.querySelector('barcode-scanable-input[name=sealNo]')?.shadowRoot?.querySelector('input')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
_shouldShowSealSection() {
|
|
249
|
+
// Only show seal section if minimumSealNumber is greater than 0
|
|
250
|
+
return (this.minimumSealNumber || 0) > 0
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_shouldShowUndoButton() {
|
|
254
|
+
// Show undo button only if selected tote is not sealed
|
|
255
|
+
const selectedTote = this._selectedToteNo?.[0]
|
|
256
|
+
return selectedTote && !selectedTote?.closedDate
|
|
192
257
|
}
|
|
193
258
|
|
|
194
259
|
_focusOnSealNoInput() {
|
|
195
|
-
|
|
260
|
+
const sealInput = this.sealNoInput
|
|
261
|
+
if (sealInput) {
|
|
262
|
+
setTimeout(() => sealInput.focus(), 50)
|
|
263
|
+
}
|
|
196
264
|
}
|
|
197
265
|
|
|
198
266
|
_focusOnToteNoInput() {
|
|
@@ -201,28 +269,36 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
201
269
|
|
|
202
270
|
async firstUpdated() {
|
|
203
271
|
this.config = {
|
|
272
|
+
list: { fields: ['sku', 'packingType', 'pickedQty'] },
|
|
204
273
|
pagination: { pages: [10, 20, 50, 100] },
|
|
205
274
|
rows: {
|
|
206
|
-
appendable: false
|
|
275
|
+
appendable: false,
|
|
276
|
+
selectable: {
|
|
277
|
+
multiple: true
|
|
278
|
+
}
|
|
207
279
|
},
|
|
208
280
|
columns: [
|
|
209
281
|
{ type: 'gutter', gutterName: 'sequence' },
|
|
282
|
+
{ type: 'gutter', gutterName: 'row-selector', multiple: true },
|
|
210
283
|
{
|
|
211
284
|
type: 'string',
|
|
212
285
|
name: 'sku',
|
|
213
286
|
header: i18next.t('field.sku'),
|
|
287
|
+
label: true,
|
|
214
288
|
width: 180
|
|
215
289
|
},
|
|
216
290
|
{
|
|
217
291
|
type: 'string',
|
|
218
292
|
name: 'packingType',
|
|
219
293
|
header: i18next.t('field.packing_type'),
|
|
294
|
+
label: true,
|
|
220
295
|
width: 180
|
|
221
296
|
},
|
|
222
297
|
{
|
|
223
298
|
type: 'string',
|
|
224
299
|
name: 'pickedQty',
|
|
225
300
|
header: i18next.t('field.picked_qty'),
|
|
301
|
+
label: true,
|
|
226
302
|
width: 180
|
|
227
303
|
}
|
|
228
304
|
]
|
|
@@ -269,7 +345,8 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
269
345
|
toteNo: this._selectedToteNo[0].name,
|
|
270
346
|
sortings,
|
|
271
347
|
orderNo: this.orderNo
|
|
272
|
-
}
|
|
348
|
+
},
|
|
349
|
+
fetchPolicy: 'no-cache'
|
|
273
350
|
})
|
|
274
351
|
|
|
275
352
|
if (!response.errors) {
|
|
@@ -279,7 +356,10 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
279
356
|
}
|
|
280
357
|
|
|
281
358
|
if (this.toteNo && this.toteNo !== '') {
|
|
282
|
-
|
|
359
|
+
// Only focus on seal input if seal section is visible
|
|
360
|
+
if (this._shouldShowSealSection()) {
|
|
361
|
+
this._focusOnSealNoInput()
|
|
362
|
+
}
|
|
283
363
|
} else {
|
|
284
364
|
this._focusOnToteNoInput()
|
|
285
365
|
}
|
|
@@ -306,6 +386,21 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
306
386
|
|
|
307
387
|
if (result) {
|
|
308
388
|
await this._checkToteAvailability()
|
|
389
|
+
|
|
390
|
+
// After successful tote scan, refresh the tote list and auto-select the scanned tote
|
|
391
|
+
if (this.toteNo && this.toteNo !== '') {
|
|
392
|
+
await this._fetchToteList()
|
|
393
|
+
|
|
394
|
+
// Auto-select the scanned tote for PDA/mobile users
|
|
395
|
+
const scannedTote = this._toteNoList?.find(tote => tote.name === this.toteNo)
|
|
396
|
+
if (scannedTote) {
|
|
397
|
+
this._selectedToteNo = [scannedTote]
|
|
398
|
+
// Trigger grist to fetch items for the selected tote
|
|
399
|
+
if (this.grist) {
|
|
400
|
+
await this.grist.fetch()
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
309
404
|
} else {
|
|
310
405
|
this.toteNoInput.value = ''
|
|
311
406
|
this.toteNo = ''
|
|
@@ -315,7 +410,10 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
315
410
|
}
|
|
316
411
|
|
|
317
412
|
if (this.toteNo && this.toteNo !== '') {
|
|
318
|
-
|
|
413
|
+
// Only focus on seal input if seal section is visible
|
|
414
|
+
if (this._shouldShowSealSection()) {
|
|
415
|
+
this._focusOnSealNoInput()
|
|
416
|
+
}
|
|
319
417
|
} else {
|
|
320
418
|
this._focusOnToteNoInput()
|
|
321
419
|
}
|
|
@@ -420,6 +518,7 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
420
518
|
items {
|
|
421
519
|
id
|
|
422
520
|
name
|
|
521
|
+
closedDate
|
|
423
522
|
orderToteSeals {
|
|
424
523
|
id
|
|
425
524
|
name
|
|
@@ -446,6 +545,97 @@ class TotePopup extends localize(i18next)(LitElement) {
|
|
|
446
545
|
}
|
|
447
546
|
}
|
|
448
547
|
|
|
548
|
+
async _undoSelectedItems() {
|
|
549
|
+
// Get selected rows from grist
|
|
550
|
+
const selectedRecords = this.grist.selected
|
|
551
|
+
|
|
552
|
+
if (!selectedRecords || selectedRecords.length === 0) {
|
|
553
|
+
await CustomAlert({
|
|
554
|
+
type: 'info',
|
|
555
|
+
title: i18next.t('text.nothing_selected'),
|
|
556
|
+
text: i18next.t('text.please_select_items_to_undo')
|
|
557
|
+
})
|
|
558
|
+
return
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Confirm before deleting
|
|
562
|
+
const itemCount = selectedRecords.length
|
|
563
|
+
const itemText =
|
|
564
|
+
itemCount > 1
|
|
565
|
+
? `${itemCount} items`
|
|
566
|
+
: `${selectedRecords[0].sku} (${selectedRecords[0].pickedQty} ${selectedRecords[0].packingType})`
|
|
567
|
+
|
|
568
|
+
const answer = await CustomAlert({
|
|
569
|
+
type: 'warning',
|
|
570
|
+
title: i18next.t('button.undo'),
|
|
571
|
+
text: i18next.t('text.are_you_sure_to_remove_x_from_tote', { x: itemText }),
|
|
572
|
+
confirmButton: {
|
|
573
|
+
text: i18next.t('button.confirm'),
|
|
574
|
+
color: '#ff9800'
|
|
575
|
+
},
|
|
576
|
+
cancelButton: {
|
|
577
|
+
text: i18next.t('button.cancel'),
|
|
578
|
+
color: '#cfcfcf'
|
|
579
|
+
}
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
if (!answer.value) return
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
// Undo all selected items
|
|
586
|
+
for (const record of selectedRecords) {
|
|
587
|
+
await this._unsortItem(record.id, false) // Don't show individual toasts
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Show single success message for all items
|
|
591
|
+
this._showToast({
|
|
592
|
+
type: 'info',
|
|
593
|
+
message:
|
|
594
|
+
itemCount > 1
|
|
595
|
+
? i18next.t('text.items_removed_from_tote', { count: itemCount })
|
|
596
|
+
: i18next.t('text.item_removed_from_tote')
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
// Refresh once after all items are removed
|
|
600
|
+
await this._fetchToteList()
|
|
601
|
+
await this.grist.fetch()
|
|
602
|
+
} catch (e) {
|
|
603
|
+
this._showToast(e)
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async _unsortItem(orderToteItemId, showToast = true) {
|
|
608
|
+
try {
|
|
609
|
+
const response = await client.mutate({
|
|
610
|
+
mutation: gql`
|
|
611
|
+
mutation unsortItem($orderToteItemId: String!, $reason: String) {
|
|
612
|
+
unsortItem(orderToteItemId: $orderToteItemId, reason: $reason)
|
|
613
|
+
}
|
|
614
|
+
`,
|
|
615
|
+
variables: {
|
|
616
|
+
orderToteItemId,
|
|
617
|
+
reason: 'Removed by user from tote popup'
|
|
618
|
+
}
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
if (!response.errors) {
|
|
622
|
+
if (showToast) {
|
|
623
|
+
this._showToast({
|
|
624
|
+
type: 'info',
|
|
625
|
+
message: i18next.t('text.item_removed_from_tote')
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
// Refresh the tote list and grist
|
|
629
|
+
await this._fetchToteList()
|
|
630
|
+
await this.grist.fetch()
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
} catch (e) {
|
|
634
|
+
this._showToast(e)
|
|
635
|
+
throw e // Re-throw to stop batch processing
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
449
639
|
_showToast({ type, message }) {
|
|
450
640
|
document.dispatchEvent(
|
|
451
641
|
new CustomEvent('notify', {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Mutation = exports.Query = void 0;
|
|
4
4
|
const get_loading_task_1 = require("./get-loading-task");
|
|
5
5
|
const sort_item_1 = require("./sort-item");
|
|
6
|
+
const unsort_item_1 = require("./unsort-item");
|
|
6
7
|
const find_loadable_release_good_1 = require("./find-loadable-release-good");
|
|
7
8
|
const find_loadable_release_good_by_bin_1 = require("./find-loadable-release-good-by-bin");
|
|
8
9
|
const loading_worksheet_v2_1 = require("./loading-worksheet-v2");
|
|
@@ -13,5 +14,5 @@ const undo_load_1 = require("./undo-load");
|
|
|
13
14
|
const warehouse_return_1 = require("./warehouse-return");
|
|
14
15
|
const complete_load_1 = require("./complete-load");
|
|
15
16
|
exports.Query = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, find_loadable_release_good_1.findLoadableReleaseGood), find_loadable_release_good_by_bin_1.findLoadableReleaseGoodByBin), loading_worksheet_v2_1.loadingWorksheetv2), my_loading_assignment_status_1.myLoadingAssignmentStatus), get_delivery_orders_1.getDeliveryOrders);
|
|
16
|
-
exports.Mutation = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, get_loading_task_1.getLoadingTask), sort_item_1.sortItem), load_by_qty_1.loadByQty), undo_load_1.undoLoad), warehouse_return_1.warehouseReturn), complete_load_1.completeLoad);
|
|
17
|
+
exports.Mutation = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, get_loading_task_1.getLoadingTask), sort_item_1.sortItem), unsort_item_1.unsortItem), load_by_qty_1.loadByQty), undo_load_1.undoLoad), warehouse_return_1.warehouseReturn), complete_load_1.completeLoad);
|
|
17
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../server/graphql/resolvers/outbound/index.ts"],"names":[],"mappings":";;;AAAA,yDAAmD;AACnD,2CAAsC;AACtC,6EAAsE;AACtE,2FAAkF;AAClF,iEAA2D;AAC3D,+CAAyC;AACzC,iFAA0E;AAC1E,+DAAyD;AACzD,2CAAsC;AACtC,yDAAoD;AACpD,mDAA8C;AAEjC,QAAA,KAAK,6EACb,oDAAuB,GACvB,gEAA4B,GAC5B,yCAAkB,GAClB,wDAAyB,GACzB,uCAAiB,EACrB;AAEY,QAAA,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../server/graphql/resolvers/outbound/index.ts"],"names":[],"mappings":";;;AAAA,yDAAmD;AACnD,2CAAsC;AACtC,+CAA0C;AAC1C,6EAAsE;AACtE,2FAAkF;AAClF,iEAA2D;AAC3D,+CAAyC;AACzC,iFAA0E;AAC1E,+DAAyD;AACzD,2CAAsC;AACtC,yDAAoD;AACpD,mDAA8C;AAEjC,QAAA,KAAK,6EACb,oDAAuB,GACvB,gEAA4B,GAC5B,yCAAkB,GAClB,wDAAyB,GACzB,uCAAiB,EACrB;AAEY,QAAA,QAAQ,yGAChB,iCAAc,GACd,oBAAQ,GACR,wBAAU,GACV,uBAAS,GACT,oBAAQ,GACR,kCAAe,GACf,4BAAY,EAChB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unsortItem = void 0;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
const sales_base_1 = require("@things-factory/sales-base");
|
|
6
|
+
exports.unsortItem = {
|
|
7
|
+
async unsortItem(_, { orderToteItemId, reason }, context) {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
const { domain, user, tx } = context.state;
|
|
10
|
+
// Find the order tote item with relations
|
|
11
|
+
const orderToteItem = (await tx.getRepository(sales_base_1.OrderToteItem).findOne({
|
|
12
|
+
where: { domain, id: orderToteItemId },
|
|
13
|
+
relations: ['orderTote', 'orderInventory']
|
|
14
|
+
}));
|
|
15
|
+
if (!orderToteItem) {
|
|
16
|
+
throw new Error('Order tote item not found');
|
|
17
|
+
}
|
|
18
|
+
// Check if tote is sealed - cannot undo if sealed
|
|
19
|
+
if ((_a = orderToteItem.orderTote) === null || _a === void 0 ? void 0 : _a.closedDate) {
|
|
20
|
+
throw new Error('Cannot remove item from sealed tote.');
|
|
21
|
+
}
|
|
22
|
+
const orderInventory = orderToteItem.orderInventory;
|
|
23
|
+
const qtyToRemove = orderToteItem.qty;
|
|
24
|
+
if (!orderInventory) {
|
|
25
|
+
throw new Error('Associated order inventory not found');
|
|
26
|
+
}
|
|
27
|
+
// Validate that we can reduce the sorted quantity
|
|
28
|
+
if (orderInventory.sortedQty < qtyToRemove) {
|
|
29
|
+
throw new Error(`Cannot remove ${qtyToRemove} items. Current sorted quantity is ${orderInventory.sortedQty}`);
|
|
30
|
+
}
|
|
31
|
+
// Calculate new sorted quantity
|
|
32
|
+
const newSortedQty = orderInventory.sortedQty - qtyToRemove;
|
|
33
|
+
// Determine new status - revert to PICKED if no more sorted items
|
|
34
|
+
let newStatus = orderInventory.status;
|
|
35
|
+
if (newSortedQty === 0 && orderInventory.status === sales_base_1.ORDER_INVENTORY_STATUS.LOADING) {
|
|
36
|
+
// Check if there are other order tote items for this order inventory
|
|
37
|
+
const otherToteItems = await tx.getRepository(sales_base_1.OrderToteItem).count({
|
|
38
|
+
where: {
|
|
39
|
+
domain,
|
|
40
|
+
orderInventory: { id: orderInventory.id },
|
|
41
|
+
id: (0, typeorm_1.Not)(orderToteItemId)
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
// Only revert to PICKED if this is the last tote item (no more sorted items)
|
|
45
|
+
if (otherToteItems === 0) {
|
|
46
|
+
newStatus = sales_base_1.ORDER_INVENTORY_STATUS.SORTING;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Update the order inventory
|
|
50
|
+
await tx.getRepository(sales_base_1.OrderInventory).update(orderInventory.id, {
|
|
51
|
+
sortedQty: newSortedQty,
|
|
52
|
+
status: newStatus,
|
|
53
|
+
updatedAt: new Date(),
|
|
54
|
+
updater: user
|
|
55
|
+
});
|
|
56
|
+
// Delete the order tote item
|
|
57
|
+
await tx.getRepository(sales_base_1.OrderToteItem).delete(orderToteItem.id);
|
|
58
|
+
// Log the undo action if reason provided
|
|
59
|
+
if (reason) {
|
|
60
|
+
console.log(`[UNSORT] User ${user.name} removed ${qtyToRemove} items from tote ${(_b = orderToteItem.orderTote) === null || _b === void 0 ? void 0 : _b.name}. Reason: ${reason}`);
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=unsort-item.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsort-item.js","sourceRoot":"","sources":["../../../../server/graphql/resolvers/outbound/unsort-item.ts"],"names":[],"mappings":";;;AAAA,qCAA4C;AAG5C,2DAAkG;AAErF,QAAA,UAAU,GAAG;IACxB,KAAK,CAAC,UAAU,CAAC,CAAM,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,OAAY;;QAChE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAsD,OAAO,CAAC,KAAK,CAAA;QAE7F,0CAA0C;QAC1C,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,0BAAa,CAAC,CAAC,OAAO,CAAC;YACnE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,eAAe,EAAE;YACtC,SAAS,EAAE,CAAC,WAAW,EAAE,gBAAgB,CAAC;SAC3C,CAAC,CAAyB,CAAA;QAE3B,IAAI,CAAC,aAAa,EAAE;YAClB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;SAC7C;QAED,kDAAkD;QAClD,IAAI,MAAA,aAAa,CAAC,SAAS,0CAAE,UAAU,EAAE;YACvC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;SACxD;QAED,MAAM,cAAc,GAAmB,aAAa,CAAC,cAAc,CAAA;QACnE,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAA;QAErC,IAAI,CAAC,cAAc,EAAE;YACnB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;SACxD;QAED,kDAAkD;QAClD,IAAI,cAAc,CAAC,SAAS,GAAG,WAAW,EAAE;YAC1C,MAAM,IAAI,KAAK,CAAC,iBAAiB,WAAW,sCAAsC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAA;SAC9G;QAED,gCAAgC;QAChC,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,GAAG,WAAW,CAAA;QAE3D,kEAAkE;QAClE,IAAI,SAAS,GAAG,cAAc,CAAC,MAAM,CAAA;QACrC,IAAI,YAAY,KAAK,CAAC,IAAI,cAAc,CAAC,MAAM,KAAK,mCAAsB,CAAC,OAAO,EAAE;YAClF,qEAAqE;YACrE,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,0BAAa,CAAC,CAAC,KAAK,CAAC;gBACjE,KAAK,EAAE;oBACL,MAAM;oBACN,cAAc,EAAE,EAAE,EAAE,EAAE,cAAc,CAAC,EAAE,EAAE;oBACzC,EAAE,EAAE,IAAA,aAAG,EAAC,eAAe,CAAC;iBACzB;aACF,CAAC,CAAA;YAEF,6EAA6E;YAC7E,IAAI,cAAc,KAAK,CAAC,EAAE;gBACxB,SAAS,GAAG,mCAAsB,CAAC,OAAO,CAAA;aAC3C;SACF;QAED,6BAA6B;QAC7B,MAAM,EAAE,CAAC,aAAa,CAAC,2BAAc,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE;YAC/D,SAAS,EAAE,YAAY;YACvB,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,OAAO,EAAE,IAAI;SACd,CAAC,CAAA;QAEF,6BAA6B;QAC7B,MAAM,EAAE,CAAC,aAAa,CAAC,0BAAa,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;QAE9D,yCAAyC;QACzC,IAAI,MAAM,EAAE;YACV,OAAO,CAAC,GAAG,CACT,iBAAiB,IAAI,CAAC,IAAI,YAAY,WAAW,oBAAoB,MAAA,aAAa,CAAC,SAAS,0CAAE,IAAI,aAAa,MAAM,EAAE,CACxH,CAAA;SACF;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAA"}
|
|
@@ -63,14 +63,17 @@ exports.loadingQcReport = {
|
|
|
63
63
|
.leftJoin('order_totes', 'ot', 'ot.id = oti.order_tote_id')
|
|
64
64
|
.leftJoin('order_tote_seals', 'ots', 'ots.order_tote_id = ot.id')
|
|
65
65
|
.innerJoin('product_details', 'pd', 'oi.product_detail_id = pd.id')
|
|
66
|
-
.where('
|
|
67
|
-
.andWhere('bizplace.id = :bizplaceId', { bizplaceId: bizplaceFilter.value })
|
|
66
|
+
.where('bizplace.id = :bizplaceId', { bizplaceId: bizplaceFilter.value })
|
|
68
67
|
.andWhere('rg.release_date <= :toDate', { toDate: toDateFilter.value })
|
|
69
68
|
.andWhere('rg.release_date >= :fromDate', { fromDate: fromDateFilter.value })
|
|
70
69
|
.andWhere('w.type = :type', { type: 'LOADING' })
|
|
71
70
|
.andWhere('rg.domain_id = :domainId', { domainId: domain.id })
|
|
71
|
+
.andWhere('rg.name IS NOT NULL')
|
|
72
|
+
.andWhere('rg.status NOT IN (:...excludedStatuses)', {
|
|
73
|
+
excludedStatuses: ['PENDING_CANCEL', 'CANCELLED', 'PENDING_WORKSHEET', 'READY_TO_PICK']
|
|
74
|
+
})
|
|
72
75
|
.groupBy('bizplace.name')
|
|
73
|
-
.addGroupBy('rg.name
|
|
76
|
+
.addGroupBy('rg.name')
|
|
74
77
|
.addGroupBy('rg.route_id')
|
|
75
78
|
.addGroupBy('rg.stop_id')
|
|
76
79
|
.addGroupBy('rg.release_date')
|
|
@@ -99,15 +102,20 @@ exports.loadingQcReport = {
|
|
|
99
102
|
console.timeEnd('loadingQcReport');
|
|
100
103
|
items = items.reduce((prev, curr, idx) => {
|
|
101
104
|
if (curr.tote) {
|
|
102
|
-
|
|
105
|
+
// Find duplicate by releaseGoodNo, tote, and additional fields to ensure proper grouping
|
|
106
|
+
let reducedItemIndex = prev.findIndex(itm => itm.releaseGoodNo === curr.releaseGoodNo &&
|
|
107
|
+
itm.tote === curr.tote &&
|
|
108
|
+
itm.bizplaceName === curr.bizplaceName);
|
|
103
109
|
if (reducedItemIndex == -1) {
|
|
104
|
-
|
|
105
|
-
prev
|
|
110
|
+
// Ensure numeric fields are properly typed when adding new item
|
|
111
|
+
prev.push(Object.assign(Object.assign({}, curr), { orderQuantity: Number(curr.orderQuantity) || 0, totalCarton: Number(curr.totalCarton) || 0, totalSeal: curr.sealId ? 1 : 0 }));
|
|
106
112
|
}
|
|
107
113
|
else {
|
|
108
|
-
|
|
109
|
-
prev[reducedItemIndex].
|
|
110
|
-
|
|
114
|
+
// Aggregate quantities with explicit numeric conversion
|
|
115
|
+
prev[reducedItemIndex].orderQuantity += Number(curr.orderQuantity) || 0;
|
|
116
|
+
prev[reducedItemIndex].totalCarton += Number(curr.totalCarton) || 0;
|
|
117
|
+
// Concatenate seal IDs only if they're unique and not null
|
|
118
|
+
if (curr.sealId && !prev[reducedItemIndex].sealId.includes(curr.sealId)) {
|
|
111
119
|
prev[reducedItemIndex].sealId = prev[reducedItemIndex].sealId.concat(', ', curr.sealId);
|
|
112
120
|
prev[reducedItemIndex].totalSeal++;
|
|
113
121
|
}
|
|
@@ -115,7 +123,8 @@ exports.loadingQcReport = {
|
|
|
115
123
|
return prev;
|
|
116
124
|
}
|
|
117
125
|
else {
|
|
118
|
-
|
|
126
|
+
// Ensure numeric fields are properly typed for items without totes
|
|
127
|
+
prev.push(Object.assign(Object.assign({}, curr), { orderQuantity: Number(curr.orderQuantity) || 0, totalCarton: Number(curr.totalCarton) || 0, totalSeal: 0 }));
|
|
119
128
|
return prev;
|
|
120
129
|
}
|
|
121
130
|
}, []);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tote-loading-qc-report.js","sourceRoot":"","sources":["../../../../server/graphql/resolvers/reports/tote-loading-qc-report.ts"],"names":[],"mappings":";;;AAAA,qCAA0E;AAK1E,2DAAwD;AAE3C,QAAA,eAAe,GAAG;IAC7B,KAAK,CAAC,eAAe,CAAC,CAAM,EAAE,MAAiB,EAAE,OAAY;QAC3D,IAAI;YACF,MAAM,EAAE,MAAM,EAAE,GAAuB,OAAO,CAAC,KAAK,CAAA;YAEpD,IAAI,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;YAC9E,IAAI,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;YAC9E,IAAI,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;YAC1E,IAAI,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;YAC5E,IAAI,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;YAC1E,IAAI,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;YAE1E,IAAI,eAAe,CAAA;YACnB,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;YACvF,IAAI,iBAAiB,IAAI,CAAC,EAAE;gBAC1B,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBAE5F,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAA;aAC5C;YAED,IAAI,aAAa,CAAA;YACjB,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;YACnF,IAAI,eAAe,IAAI,CAAC,EAAE;gBACxB,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,CAAA;gBAErD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAA;aAC1C;YAED,MAAM,EAAE,GAAoC,IAAA,uBAAa,EAAC,wBAAW,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;YAE/F,EAAE,CAAC,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC;iBACvC,SAAS,CAAC,SAAS,EAAE,eAAe,CAAC;iBACrC,SAAS,CAAC,iBAAiB,EAAE,aAAa,CAAC;iBAC3C,SAAS,CAAC,aAAa,EAAE,SAAS,CAAC;iBACnC,SAAS,CAAC,YAAY,EAAE,QAAQ,CAAC;iBACjC,SAAS,CAAC,iBAAiB,EAAE,aAAa,CAAC;iBAC3C,SAAS,CACR;;;;;;;;;;;;;;;;;;;cAmBI,EACJ,aAAa,
|
|
1
|
+
{"version":3,"file":"tote-loading-qc-report.js","sourceRoot":"","sources":["../../../../server/graphql/resolvers/reports/tote-loading-qc-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,10 @@ exports.Mutation = `
|
|
|
20
20
|
worksheetDetails: [Object!]
|
|
21
21
|
toteNo: String
|
|
22
22
|
): Boolean @transaction
|
|
23
|
+
unsortItem(
|
|
24
|
+
orderToteItemId: String!
|
|
25
|
+
reason: String
|
|
26
|
+
): Boolean @transaction
|
|
23
27
|
loadByQty(worksheetDetailPatch: LoadingWorksheetDetails): Boolean @transaction
|
|
24
28
|
undoLoad(doId: String!): Boolean @transaction
|
|
25
29
|
warehouseReturn (releaseGoodNo: String!, worksheetDetails: [Object!]): Worksheet @privilege(category: "worksheet_control", privilege: "mutation") @transaction
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../server/graphql/types/outbound/index.ts"],"names":[],"mappings":";;;AAAA,qEAAoF;AACpF,yEAAkE;AAClE,6DAA8D;AAEjD,QAAA,KAAK,GAAG;;;;;;CAMpB,CAAA;AAEY,QAAA,QAAQ,GAAG
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../server/graphql/types/outbound/index.ts"],"names":[],"mappings":";;;AAAA,qEAAoF;AACpF,yEAAkE;AAClE,6DAA8D;AAEjD,QAAA,KAAK,GAAG;;;;;;CAMpB,CAAA;AAEY,QAAA,QAAQ,GAAG;;;;;;;;;;;;;;;;;;CAkBvB,CAAA;AAEY,QAAA,KAAK,GAAG,CAAC,yCAAgB,EAAE,gDAAuB,EAAE,gDAAqB,EAAE,4CAAuB,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/operato-wms",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.762",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -117,5 +117,5 @@
|
|
|
117
117
|
"cypress-localstorage-commands": "^1.6.1",
|
|
118
118
|
"eslint-plugin-cypress": "^2.12.1"
|
|
119
119
|
},
|
|
120
|
-
"gitHead": "
|
|
120
|
+
"gitHead": "baadfe656ab39bd1a0623465d4e722e4f6afdbda"
|
|
121
121
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getLoadingTask } from './get-loading-task'
|
|
2
2
|
import { sortItem } from './sort-item'
|
|
3
|
+
import { unsortItem } from './unsort-item'
|
|
3
4
|
import { findLoadableReleaseGood } from './find-loadable-release-good'
|
|
4
5
|
import { findLoadableReleaseGoodByBin } from './find-loadable-release-good-by-bin'
|
|
5
6
|
import { loadingWorksheetv2 } from './loading-worksheet-v2'
|
|
@@ -21,6 +22,7 @@ export const Query = {
|
|
|
21
22
|
export const Mutation = {
|
|
22
23
|
...getLoadingTask,
|
|
23
24
|
...sortItem,
|
|
25
|
+
...unsortItem,
|
|
24
26
|
...loadByQty,
|
|
25
27
|
...undoLoad,
|
|
26
28
|
...warehouseReturn,
|
|
@@ -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": "跟踪号已存在于其他清单列表中",
|