@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.
- package/client/pages/inbound/unload-product.js +30 -33
- package/client/pages/outbound/loading-product.js +2 -3
- package/client/pages/outbound/loading-v2/loading-execution-base.js +568 -56
- package/client/pages/outbound/loading-v2/loading-execution.js +258 -8
- package/client/pages/outbound/loading-v2/loading.js +82 -12
- package/client/pages/outbound/picking-product.js +11 -6
- package/client/pages/outbound/picking-v2/picking-execution-base.js +8 -8
- package/client/pages/outbound/picking-v2/picking-execution.js +75 -0
- package/client/pages/outbound/route-label-history-dialog.js +253 -0
- package/client/pages/outbound/route-label-popup.js +637 -131
- package/client/pages/outbound/show-ro-list-popup.js +8 -4
- package/client/pages/outbound/single-outbound-worksheet.js +55 -6
- package/client/pages/outbound/sorting-product.js +71 -25
- package/client/pages/outbound/zone-worksheet.js +62 -3
- package/dist-server/entities/index.js +9 -0
- package/dist-server/entities/index.js.map +1 -0
- package/dist-server/entities/route-label-sequence-counter.js +75 -0
- package/dist-server/entities/route-label-sequence-counter.js.map +1 -0
- package/dist-server/entities/route-label-sequence-log.js +74 -0
- package/dist-server/entities/route-label-sequence-log.js.map +1 -0
- package/dist-server/graphql/resolvers/index.js +3 -0
- package/dist-server/graphql/resolvers/index.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/complete-load.js +40 -3
- package/dist-server/graphql/resolvers/outbound/complete-load.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js +50 -11
- package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/find-loadable-release-good.js +90 -48
- package/dist-server/graphql/resolvers/outbound/find-loadable-release-good.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js +15 -7
- package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js.map +1 -1
- package/dist-server/graphql/resolvers/outbound/sort-item.js +95 -16
- package/dist-server/graphql/resolvers/outbound/sort-item.js.map +1 -1
- package/dist-server/graphql/resolvers/route-label/index.js +7 -0
- package/dist-server/graphql/resolvers/route-label/index.js.map +1 -0
- package/dist-server/graphql/resolvers/route-label/route-label.js +220 -0
- package/dist-server/graphql/resolvers/route-label/route-label.js.map +1 -0
- package/dist-server/graphql/resolvers/zone-worksheet/index.js.map +1 -1
- package/dist-server/graphql/resolvers/zone-worksheet/zone-picking.js +123 -27
- package/dist-server/graphql/resolvers/zone-worksheet/zone-picking.js.map +1 -1
- package/dist-server/graphql/resolvers/zone-worksheet/zone-worksheet.js.map +1 -1
- package/dist-server/graphql/types/index.js +4 -0
- package/dist-server/graphql/types/index.js.map +1 -1
- package/dist-server/graphql/types/outbound/index.js +7 -1
- package/dist-server/graphql/types/outbound/index.js.map +1 -1
- package/dist-server/graphql/types/route-label/index.js +14 -0
- package/dist-server/graphql/types/route-label/index.js.map +1 -0
- package/dist-server/graphql/types/route-label/route-label-sequence.js +53 -0
- package/dist-server/graphql/types/route-label/route-label-sequence.js.map +1 -0
- package/dist-server/index.js +5 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/validators/setting-validators.js +519 -0
- package/dist-server/validators/setting-validators.js.map +1 -0
- package/helps/operato-wms/outbound/picking-product.en.md +156 -158
- package/package.json +50 -50
- package/server/entities/index.ts +6 -0
- package/server/entities/route-label-sequence-counter.ts +56 -0
- package/server/entities/route-label-sequence-log.ts +47 -0
- package/server/graphql/resolvers/index.ts +3 -0
- package/server/graphql/resolvers/outbound/complete-load.ts +42 -1
- package/server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.ts +56 -13
- package/server/graphql/resolvers/outbound/find-loadable-release-good.ts +120 -57
- package/server/graphql/resolvers/outbound/loading-worksheet-v2.ts +9 -2
- package/server/graphql/resolvers/outbound/sort-item.ts +125 -21
- package/server/graphql/resolvers/route-label/index.ts +16 -0
- package/server/graphql/resolvers/route-label/route-label.ts +264 -0
- package/server/graphql/resolvers/zone-worksheet/index.ts +2 -2
- package/server/graphql/resolvers/zone-worksheet/zone-picking.ts +156 -39
- package/server/graphql/resolvers/zone-worksheet/zone-worksheet.ts +1 -2
- package/server/graphql/types/index.ts +4 -0
- package/server/graphql/types/outbound/index.ts +7 -1
- package/server/graphql/types/route-label/index.ts +18 -0
- package/server/graphql/types/route-label/route-label-sequence.ts +50 -0
- package/server/index.ts +7 -1
- package/server/validators/setting-validators.ts +668 -0
- package/translations/en.json +280 -252
- package/translations/ko.json +29 -1
- package/translations/ms.json +28 -0
- package/translations/zh.json +28 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '@things-factory/barcode-ui'
|
|
2
2
|
import '@things-factory/grist-ui'
|
|
3
3
|
import { css, html } from 'lit-element'
|
|
4
|
+
import { gql } from 'graphql-tag'
|
|
4
5
|
import { MultiColumnFormStyles, SingleColumnFormStyles } from '@things-factory/form-ui'
|
|
5
6
|
import { WORKSHEET_STATUS } from '../../constants'
|
|
6
7
|
import { i18next } from '@things-factory/i18n-base'
|
|
@@ -8,11 +9,13 @@ import { isMobileDevice } from '@things-factory/utils'
|
|
|
8
9
|
import { LoadingExecutionBase } from './loading-execution-base'
|
|
9
10
|
import '../../components/list-view/product-list-view'
|
|
10
11
|
import { fetchSettingRule } from '../../../fetch-setting-value'
|
|
12
|
+
import { client, gqlContext } from '@things-factory/shell'
|
|
11
13
|
|
|
12
14
|
// Constants
|
|
13
15
|
const SETTINGS = {
|
|
14
16
|
QC_SKU: 'qc-sku',
|
|
15
|
-
DISABLE_QC: 'disable-QC-in-loading'
|
|
17
|
+
DISABLE_QC: 'disable-QC-in-loading',
|
|
18
|
+
ENABLE_TOTE_SCANNING: 'enable-tote-scanning'
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
const TAB_TYPES = {
|
|
@@ -24,6 +27,16 @@ const TAB_TYPES = {
|
|
|
24
27
|
const DEFAULT_TABS = [TAB_TYPES.QC, TAB_TYPES.LOADING, TAB_TYPES.LOADED]
|
|
25
28
|
const LOADING_TABS = [TAB_TYPES.LOADING, TAB_TYPES.LOADED]
|
|
26
29
|
|
|
30
|
+
const VALIDATE_QC_SEALS = gql`
|
|
31
|
+
query validateQcSeals($releaseGoodNo: String!) {
|
|
32
|
+
validateQcSeals(releaseGoodNo: $releaseGoodNo) {
|
|
33
|
+
valid
|
|
34
|
+
error
|
|
35
|
+
minimumSealNumber
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`
|
|
39
|
+
|
|
27
40
|
// List View Configurations
|
|
28
41
|
const QC_LIST_CONFIG = {
|
|
29
42
|
selectable: true,
|
|
@@ -121,13 +134,14 @@ const LOADING_LIST_CONFIG = {
|
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
136
|
|
|
124
|
-
|
|
125
137
|
export class LoadingExecution extends LoadingExecutionBase {
|
|
126
138
|
static get properties() {
|
|
127
139
|
return {
|
|
128
140
|
...super.properties,
|
|
129
141
|
_useQcSku: { type: Boolean },
|
|
130
|
-
_currentReleaseGood: { type: Object }
|
|
142
|
+
_currentReleaseGood: { type: Object },
|
|
143
|
+
_tabCounts: { type: Object },
|
|
144
|
+
_qcBannerState: { type: Object }
|
|
131
145
|
}
|
|
132
146
|
}
|
|
133
147
|
|
|
@@ -255,6 +269,52 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
255
269
|
align-items: center;
|
|
256
270
|
padding: 0 12px 8px;
|
|
257
271
|
}
|
|
272
|
+
.loading-banner {
|
|
273
|
+
background-color: #fff3cd;
|
|
274
|
+
border: 1px solid #ffc107;
|
|
275
|
+
border-radius: 4px;
|
|
276
|
+
padding: 12px 16px;
|
|
277
|
+
margin: 10px;
|
|
278
|
+
display: flex;
|
|
279
|
+
justify-content: space-between;
|
|
280
|
+
align-items: center;
|
|
281
|
+
color: #856404;
|
|
282
|
+
}
|
|
283
|
+
.loading-banner button {
|
|
284
|
+
background-color: #007e7f;
|
|
285
|
+
color: white;
|
|
286
|
+
border: none;
|
|
287
|
+
padding: 6px 12px;
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
font-weight: bold;
|
|
291
|
+
}
|
|
292
|
+
.loading-banner button:hover {
|
|
293
|
+
background-color: #005f60;
|
|
294
|
+
}
|
|
295
|
+
.qc-banner {
|
|
296
|
+
background-color: #fff3cd;
|
|
297
|
+
border: 1px solid #ffc107;
|
|
298
|
+
border-radius: 4px;
|
|
299
|
+
padding: 12px 16px;
|
|
300
|
+
margin: 10px;
|
|
301
|
+
display: flex;
|
|
302
|
+
justify-content: space-between;
|
|
303
|
+
align-items: center;
|
|
304
|
+
color: #856404;
|
|
305
|
+
}
|
|
306
|
+
.qc-banner button {
|
|
307
|
+
background-color: #007e7f;
|
|
308
|
+
color: white;
|
|
309
|
+
border: none;
|
|
310
|
+
padding: 6px 12px;
|
|
311
|
+
border-radius: 4px;
|
|
312
|
+
cursor: pointer;
|
|
313
|
+
font-weight: bold;
|
|
314
|
+
}
|
|
315
|
+
.qc-banner button:hover {
|
|
316
|
+
background-color: #005f60;
|
|
317
|
+
}
|
|
258
318
|
`
|
|
259
319
|
]
|
|
260
320
|
}
|
|
@@ -264,6 +324,7 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
264
324
|
this._executionData = { ...this._executionData, ownCollection: false }
|
|
265
325
|
this._useQcSku = false
|
|
266
326
|
this._currentReleaseGood = null
|
|
327
|
+
this._qcBannerState = { show: false, needsSealing: false, minimumSealNumber: 0 }
|
|
267
328
|
this._deliveryOrderConfig = {
|
|
268
329
|
columnLeft: {
|
|
269
330
|
size: 8,
|
|
@@ -306,6 +367,12 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
306
367
|
startedAt: this.worksheet?.startedAt
|
|
307
368
|
}
|
|
308
369
|
}
|
|
370
|
+
// Update banner state when QC or loading data changes
|
|
371
|
+
if (changedProperties.has('qcData') || changedProperties.has('loadingData')) {
|
|
372
|
+
if (this._updateQcBannerState) {
|
|
373
|
+
this._updateQcBannerState()
|
|
374
|
+
}
|
|
375
|
+
}
|
|
309
376
|
}
|
|
310
377
|
|
|
311
378
|
combineSameProducts(data) {
|
|
@@ -331,16 +398,33 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
331
398
|
const disableQCSetting = await fetchSettingRule(SETTINGS.DISABLE_QC)
|
|
332
399
|
this._disableQC = disableQCSetting === true || disableQCSetting === 'true'
|
|
333
400
|
this._useQcSku = await fetchSettingRule(SETTINGS.QC_SKU)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
401
|
+
this._enableToteScanningValue = parseInt((await fetchSettingRule(SETTINGS.ENABLE_TOTE_SCANNING)) || '0', 10)
|
|
402
|
+
this._enableToteScanning = this._enableToteScanningValue >= 1
|
|
403
|
+
this.toteNo = ''
|
|
404
|
+
|
|
405
|
+
// UX-safe tab selection: Auto-switch to LOADED if LOADING is empty but LOADED has items
|
|
406
|
+
let activeTab = !this._disableQC ? TAB_TYPES.QC : TAB_TYPES.LOADING
|
|
407
|
+
if (this._tabCounts && activeTab === TAB_TYPES.LOADING) {
|
|
408
|
+
const loadingCounts = this._tabCounts.LOADING || { total: 0, pending: 0 }
|
|
409
|
+
const loadedCounts = this._tabCounts.LOADED || { total: 0, pending: 0 }
|
|
410
|
+
if (loadingCounts.total === 0 && loadedCounts.total > 0) {
|
|
411
|
+
activeTab = TAB_TYPES.LOADED
|
|
412
|
+
}
|
|
413
|
+
}
|
|
337
414
|
this.state = { ...this.state, activeTab }
|
|
338
415
|
|
|
339
416
|
// Process worksheet details
|
|
340
417
|
const processedDetails = this._useQcSku ? this.combineSameProducts(this.worksheetDetails) : this.worksheetDetails
|
|
341
418
|
|
|
419
|
+
// Check if any order inventory has refWorksheetId (when data comes from props)
|
|
420
|
+
if (this.worksheetDetails && this.worksheetDetails.length > 0) {
|
|
421
|
+
this._hasRefWorksheetId = this.worksheetDetails.some(
|
|
422
|
+
item => item?.relatedOrderInv?.refWorksheetId != null && item?.relatedOrderInv?.refWorksheetId !== ''
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
|
|
342
426
|
// Filter data for different tabs
|
|
343
|
-
this.qcData = processedDetails.filter(data => data?.sortedQty
|
|
427
|
+
this.qcData = processedDetails.filter(data => data?.sortedQty < data?.pickedQty && data.status !== 'LOADED') || []
|
|
344
428
|
this.loadingData =
|
|
345
429
|
processedDetails.filter(data => data?.sortedQty > 0 && data.status != WORKSHEET_STATUS.DONE.value) || []
|
|
346
430
|
this.loadedData = this.deliveryOrdersInfo || []
|
|
@@ -352,7 +436,11 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
352
436
|
worksheetId: this.worksheet?.worksheet?.id
|
|
353
437
|
}
|
|
354
438
|
|
|
439
|
+
// Check seal status after resetting view (needed for Tote button visibility)
|
|
440
|
+
await this._checkAndUpdateSealStatus()
|
|
355
441
|
this.getButton()
|
|
442
|
+
// Update QC banner state after resetting view
|
|
443
|
+
await this._updateQcBannerState()
|
|
356
444
|
}
|
|
357
445
|
|
|
358
446
|
get qcListItemConfig() {
|
|
@@ -367,6 +455,106 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
367
455
|
return this._deliveryOrderConfig
|
|
368
456
|
}
|
|
369
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Determines if the "Go to LOADED" banner should be shown.
|
|
460
|
+
* Banner appears when:
|
|
461
|
+
* - Current tab is LOADING
|
|
462
|
+
* - LOADING tab has no items (loadingData.length === 0) - uses SAME source as Total SKU
|
|
463
|
+
* - LOADED tab has items (loadedData.deliveryOrders.length > 0)
|
|
464
|
+
*
|
|
465
|
+
* Uses the same data source as Total SKU display to ensure consistency.
|
|
466
|
+
*/
|
|
467
|
+
_shouldShowLoadingBanner() {
|
|
468
|
+
// Banner only shows on LOADING tab
|
|
469
|
+
if (this.state?.activeTab !== TAB_TYPES.LOADING) {
|
|
470
|
+
return false
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Use the SAME source as Total SKU display: this.loadingData?.length
|
|
474
|
+
const loadingItemCount = this.loadingData?.length || 0
|
|
475
|
+
const loadedItemCount = this.loadedData?.deliveryOrders?.length || 0
|
|
476
|
+
|
|
477
|
+
// Show banner when LOADING is empty but LOADED has items
|
|
478
|
+
return loadingItemCount === 0 && loadedItemCount > 0
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Checks if totes need to be sealed by calling the validation query
|
|
483
|
+
* Returns: { needsSealing: boolean, error?: string, minimumSealNumber?: number }
|
|
484
|
+
*/
|
|
485
|
+
async _checkToteSealStatus() {
|
|
486
|
+
if (!this._enableToteScanning) {
|
|
487
|
+
return { needsSealing: false, minimumSealNumber: 0 }
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const orderNo = this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name
|
|
491
|
+
if (!orderNo) {
|
|
492
|
+
return { needsSealing: false, minimumSealNumber: 0 }
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
const validationResponse = await client.query({
|
|
497
|
+
query: VALIDATE_QC_SEALS,
|
|
498
|
+
variables: { releaseGoodNo: orderNo },
|
|
499
|
+
context: gqlContext()
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
|
|
503
|
+
const { valid, error, minimumSealNumber } = validationResponse.data.validateQcSeals
|
|
504
|
+
const minSealNum = minimumSealNumber || 0
|
|
505
|
+
// If minimumSealNumber is 0, sealing is not required even if valid is false
|
|
506
|
+
const needsSealing = !valid && minSealNum > 0
|
|
507
|
+
return { needsSealing, error, minimumSealNumber: minSealNum }
|
|
508
|
+
}
|
|
509
|
+
} catch (e) {
|
|
510
|
+
// If validation fails, assume sealing is needed
|
|
511
|
+
return { needsSealing: true, error: e.message || 'Seal validation failed', minimumSealNumber: 0 }
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return { needsSealing: false, minimumSealNumber: 0 }
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Updates the QC banner state based on current data
|
|
519
|
+
*/
|
|
520
|
+
async _updateQcBannerState() {
|
|
521
|
+
// Banner only shows on QC tab
|
|
522
|
+
if (this.state?.activeTab !== TAB_TYPES.QC) {
|
|
523
|
+
this._qcBannerState = { show: false, needsSealing: false, minimumSealNumber: 0 }
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Use the SAME source as Total SKU display: this.qcData?.length
|
|
528
|
+
const qcItemCount = this.qcData?.length || 0
|
|
529
|
+
const loadingItemCount = this.loadingData?.length || 0
|
|
530
|
+
|
|
531
|
+
// Show banner when QC is empty but LOADING has items
|
|
532
|
+
const shouldShow = qcItemCount === 0 && loadingItemCount > 0
|
|
533
|
+
|
|
534
|
+
if (!shouldShow) {
|
|
535
|
+
this._qcBannerState = { show: false, needsSealing: false, minimumSealNumber: 0 }
|
|
536
|
+
return
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Check if totes need sealing
|
|
540
|
+
const sealStatus = await this._checkToteSealStatus()
|
|
541
|
+
|
|
542
|
+
this._qcBannerState = {
|
|
543
|
+
show: true,
|
|
544
|
+
needsSealing: sealStatus.needsSealing,
|
|
545
|
+
error: sealStatus.error,
|
|
546
|
+
minimumSealNumber: sealStatus.minimumSealNumber || 0
|
|
547
|
+
}
|
|
548
|
+
this.requestUpdate()
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Determines if the QC banner should be shown (synchronous getter for render)
|
|
553
|
+
*/
|
|
554
|
+
get _shouldShowQcBanner() {
|
|
555
|
+
return this._qcBannerState?.show || false
|
|
556
|
+
}
|
|
557
|
+
|
|
370
558
|
render() {
|
|
371
559
|
if (!this.state) {
|
|
372
560
|
this.resetView()
|
|
@@ -388,6 +576,12 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
388
576
|
${this.state?.activeTab === TAB_TYPES.QC
|
|
389
577
|
? html`
|
|
390
578
|
<!-- QC Tab Inputs -->
|
|
579
|
+
${this._enableToteScanning && this._disableQC === true
|
|
580
|
+
? html`
|
|
581
|
+
<label class="font-bold">${i18next.t('field.tote')}</label>
|
|
582
|
+
<input name="toteNo" custom-input disabled .value="${this.toteNo || ''}" />
|
|
583
|
+
`
|
|
584
|
+
: ''}
|
|
391
585
|
<label class="font-bold">${i18next.t('label.product_barcode')}</label>
|
|
392
586
|
<barcode-scanable-input
|
|
393
587
|
name="productBarcode"
|
|
@@ -447,7 +641,14 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
447
641
|
}}
|
|
448
642
|
>
|
|
449
643
|
<label class="text-14 font-bold order-no"
|
|
450
|
-
>Order No.:
|
|
644
|
+
>Order No.:
|
|
645
|
+
${this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name
|
|
646
|
+
? this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name
|
|
647
|
+
: this.worksheet?.isReleaseGoodScan
|
|
648
|
+
? this.worksheet?.releaseGood?.name
|
|
649
|
+
: this._useQcSku
|
|
650
|
+
? this.worksheet?.binLocationName
|
|
651
|
+
: ''}</label
|
|
451
652
|
>
|
|
452
653
|
<mwc-icon style="color:#007E7F;">info</mwc-icon>
|
|
453
654
|
</button>
|
|
@@ -473,6 +674,55 @@ export class LoadingExecution extends LoadingExecutionBase {
|
|
|
473
674
|
>
|
|
474
675
|
</div>
|
|
475
676
|
|
|
677
|
+
${this._shouldShowLoadingBanner()
|
|
678
|
+
? html`
|
|
679
|
+
<div class="loading-banner">
|
|
680
|
+
<span
|
|
681
|
+
>${i18next.t('text.all_items_loaded_proceed_to_loaded', {
|
|
682
|
+
defaultValue: 'All items loaded. Proceed to LOADED to complete dispatch.'
|
|
683
|
+
})}</span
|
|
684
|
+
>
|
|
685
|
+
<button @click=${async e => await this.openTab(e, TAB_TYPES.LOADED)}>
|
|
686
|
+
${i18next.t('button.go_to_loaded', { defaultValue: 'Go to LOADED' })}
|
|
687
|
+
</button>
|
|
688
|
+
</div>
|
|
689
|
+
`
|
|
690
|
+
: ''}
|
|
691
|
+
${this._shouldShowQcBanner
|
|
692
|
+
? html`
|
|
693
|
+
<div class="qc-banner">
|
|
694
|
+
<span
|
|
695
|
+
>${this._qcBannerState?.needsSealing
|
|
696
|
+
? this._qcBannerState?.error ||
|
|
697
|
+
(this._qcBannerState?.minimumSealNumber > 0
|
|
698
|
+
? i18next.t('text.tote_needs_to_be_sealed_with_count', {
|
|
699
|
+
defaultValue: `Tote needs to be sealed before proceeding to LOADING. Minimum ${this._qcBannerState.minimumSealNumber} seal(s) required.`,
|
|
700
|
+
count: this._qcBannerState.minimumSealNumber
|
|
701
|
+
})
|
|
702
|
+
: i18next.t('text.tote_needs_to_be_sealed', {
|
|
703
|
+
defaultValue: 'Tote needs to be sealed before proceeding to LOADING.'
|
|
704
|
+
}))
|
|
705
|
+
: i18next.t('text.all_items_qc_complete_proceed_to_loading', {
|
|
706
|
+
defaultValue: 'All items QC complete. Proceed to LOADING to continue.'
|
|
707
|
+
})}</span
|
|
708
|
+
>
|
|
709
|
+
<button
|
|
710
|
+
@click=${async e => {
|
|
711
|
+
if (this._qcBannerState?.needsSealing) {
|
|
712
|
+
await this.openTotePopup()
|
|
713
|
+
} else {
|
|
714
|
+
await this.openTab(e, TAB_TYPES.LOADING)
|
|
715
|
+
}
|
|
716
|
+
}}
|
|
717
|
+
>
|
|
718
|
+
${this._qcBannerState?.needsSealing
|
|
719
|
+
? i18next.t('button.seal_tote', { defaultValue: 'Seal Tote' })
|
|
720
|
+
: i18next.t('button.go_to_loading', { defaultValue: 'Go to LOADING' })}
|
|
721
|
+
</button>
|
|
722
|
+
</div>
|
|
723
|
+
`
|
|
724
|
+
: ''}
|
|
725
|
+
|
|
476
726
|
<div id="qc" class="tab-content ${this.state?.activeTab === TAB_TYPES.QC ? 'active' : ''}">
|
|
477
727
|
<product-list-view id="qc" .config=${this.qcListItemConfig} .data=${this.qcData || []}></product-list-view>
|
|
478
728
|
</div>
|
|
@@ -54,6 +54,18 @@ const FIND_LOADABLE_RELEASE_GOOD = gql`
|
|
|
54
54
|
total
|
|
55
55
|
pending
|
|
56
56
|
}
|
|
57
|
+
tabCounts {
|
|
58
|
+
LOADING {
|
|
59
|
+
total
|
|
60
|
+
completed
|
|
61
|
+
pending
|
|
62
|
+
}
|
|
63
|
+
LOADED {
|
|
64
|
+
total
|
|
65
|
+
completed
|
|
66
|
+
pending
|
|
67
|
+
}
|
|
68
|
+
}
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
71
|
`
|
|
@@ -71,6 +83,7 @@ const FIND_LOADABLE_RELEASE_GOOD_BY_BIN = gql`
|
|
|
71
83
|
bizplaceName
|
|
72
84
|
startedAt
|
|
73
85
|
binLocationName
|
|
86
|
+
isReleaseGoodScan
|
|
74
87
|
releaseGood {
|
|
75
88
|
id
|
|
76
89
|
name
|
|
@@ -79,6 +92,9 @@ const FIND_LOADABLE_RELEASE_GOOD_BY_BIN = gql`
|
|
|
79
92
|
attentionTo
|
|
80
93
|
deliveryAddress1
|
|
81
94
|
deliveryAddress2
|
|
95
|
+
refNo
|
|
96
|
+
refNo2
|
|
97
|
+
refNo3
|
|
82
98
|
}
|
|
83
99
|
}
|
|
84
100
|
worksheetDetailInfos {
|
|
@@ -101,6 +117,7 @@ const FIND_LOADABLE_RELEASE_GOOD_BY_BIN = gql`
|
|
|
101
117
|
id
|
|
102
118
|
batchId
|
|
103
119
|
packingType
|
|
120
|
+
refWorksheetId
|
|
104
121
|
product {
|
|
105
122
|
id
|
|
106
123
|
sku
|
|
@@ -161,6 +178,7 @@ const LOADING_WORKSHEET_V2 = gql`
|
|
|
161
178
|
id
|
|
162
179
|
batchId
|
|
163
180
|
packingType
|
|
181
|
+
refWorksheetId
|
|
164
182
|
product {
|
|
165
183
|
id
|
|
166
184
|
sku
|
|
@@ -244,6 +262,7 @@ const GET_LOADING_TASK = gql`
|
|
|
244
262
|
id
|
|
245
263
|
batchId
|
|
246
264
|
packingType
|
|
265
|
+
refWorksheetId
|
|
247
266
|
product {
|
|
248
267
|
id
|
|
249
268
|
sku
|
|
@@ -265,7 +284,8 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
265
284
|
_data: Object,
|
|
266
285
|
_context: Object,
|
|
267
286
|
_qcSku: Boolean,
|
|
268
|
-
_disableQC: Boolean
|
|
287
|
+
_disableQC: Boolean,
|
|
288
|
+
_tabCounts: Object
|
|
269
289
|
}
|
|
270
290
|
}
|
|
271
291
|
|
|
@@ -375,6 +395,7 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
375
395
|
.worksheetDetails=${this._data?.worksheetDetailInfos}
|
|
376
396
|
.deliveryOrdersInfo=${this._data?.deliveryOrderInfoList}
|
|
377
397
|
._useQcSku=${this._qcSku}
|
|
398
|
+
._tabCounts=${this._tabCounts}
|
|
378
399
|
></loading-execution>`
|
|
379
400
|
} else {
|
|
380
401
|
return html` <div class="${isMobileDevice() ? 'single-column-form' : 'multi-column-form'}">
|
|
@@ -423,7 +444,12 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
423
444
|
|
|
424
445
|
async fetchOrder(orderNo) {
|
|
425
446
|
try {
|
|
426
|
-
|
|
447
|
+
// Use FIND_LOADABLE_RELEASE_GOOD if:
|
|
448
|
+
// 1. qc-sku is false, OR
|
|
449
|
+
// 2. qc-sku is true but disable-QC-in-loading is true (QC tab is hidden, so use order number scanning)
|
|
450
|
+
const shouldUseOrderNumberQuery = !this._qcSku || this._disableQC === true || this._disableQC === 'true'
|
|
451
|
+
|
|
452
|
+
if (shouldUseOrderNumberQuery) {
|
|
427
453
|
const defaultTabName = this._disableQC === true || this._disableQC === 'true' ? 'LOADING' : 'QC'
|
|
428
454
|
|
|
429
455
|
const response = await client.query({
|
|
@@ -432,7 +458,8 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
432
458
|
orderNo,
|
|
433
459
|
tabName: defaultTabName
|
|
434
460
|
},
|
|
435
|
-
context: gqlContext()
|
|
461
|
+
context: gqlContext(),
|
|
462
|
+
fetchPolicy: 'network-only' // Always fetch fresh data to show updated item counts
|
|
436
463
|
})
|
|
437
464
|
|
|
438
465
|
if (!response.errors) {
|
|
@@ -445,6 +472,7 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
445
472
|
}
|
|
446
473
|
}
|
|
447
474
|
} else {
|
|
475
|
+
// Use FIND_LOADABLE_RELEASE_GOOD_BY_BIN when qc-sku is true and QC is enabled
|
|
448
476
|
const response = await client.query({
|
|
449
477
|
query: FIND_LOADABLE_RELEASE_GOOD_BY_BIN,
|
|
450
478
|
variables: { orderNo },
|
|
@@ -485,8 +513,17 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
485
513
|
<show-ro-list-popup
|
|
486
514
|
.releaseOrders=${releaseGoods}
|
|
487
515
|
.sorting=${false}
|
|
488
|
-
@ro-selected-loading=${e => {
|
|
489
|
-
|
|
516
|
+
@ro-selected-loading=${async e => {
|
|
517
|
+
const selectedRO = e.detail.ro
|
|
518
|
+
// Store tabCounts for passing to loading-execution component
|
|
519
|
+
if (selectedRO?.tabCounts) {
|
|
520
|
+
this._tabCounts = selectedRO.tabCounts
|
|
521
|
+
}
|
|
522
|
+
// Ensure settings are loaded before fetching worksheet to use correct filter
|
|
523
|
+
if (this._disableQC === undefined || this._disableQC === null) {
|
|
524
|
+
await this.initializeSettings()
|
|
525
|
+
}
|
|
526
|
+
await this.fetchWorksheet(selectedRO?.name)
|
|
490
527
|
}}
|
|
491
528
|
></show-ro-list-popup>
|
|
492
529
|
`,
|
|
@@ -500,15 +537,48 @@ class LoadingProductV2 extends connect(store)(localize(i18next)(PageView)) {
|
|
|
500
537
|
|
|
501
538
|
async fetchWorksheet(orderNo) {
|
|
502
539
|
try {
|
|
503
|
-
|
|
540
|
+
// Ensure settings are loaded before determining filter
|
|
541
|
+
// fetchSettingRule returns boolean true/false, so check for boolean true
|
|
542
|
+
const disableQC = this._disableQC === true
|
|
543
|
+
|
|
544
|
+
// Fetch BOTH QC and LOADING data so the banner can properly determine state
|
|
545
|
+
// This ensures loadingData is available even when QC is complete
|
|
546
|
+
const [qcResponse, loadingResponse] = await Promise.all([
|
|
547
|
+
client.query({
|
|
548
|
+
query: LOADING_WORKSHEET_V2,
|
|
549
|
+
variables: { orderNo, filter: FILTER_TYPES.QC },
|
|
550
|
+
fetchPolicy: 'network-only'
|
|
551
|
+
}),
|
|
552
|
+
client.query({
|
|
553
|
+
query: LOADING_WORKSHEET_V2,
|
|
554
|
+
variables: { orderNo, filter: FILTER_TYPES.LOADING },
|
|
555
|
+
fetchPolicy: 'network-only'
|
|
556
|
+
})
|
|
557
|
+
])
|
|
504
558
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
})
|
|
559
|
+
if (!qcResponse.errors && !loadingResponse.errors) {
|
|
560
|
+
const qcData = qcResponse.data?.loadingWorksheetv2
|
|
561
|
+
const loadingData = loadingResponse.data?.loadingWorksheetv2
|
|
509
562
|
|
|
510
|
-
|
|
511
|
-
|
|
563
|
+
// Combine worksheetDetailInfos from both queries, removing duplicates by id
|
|
564
|
+
const qcDetails = qcData?.worksheetDetailInfos || []
|
|
565
|
+
const loadingDetails = loadingData?.worksheetDetailInfos || []
|
|
566
|
+
const detailsMap = new Map()
|
|
567
|
+
|
|
568
|
+
// Add QC details first
|
|
569
|
+
qcDetails.forEach(detail => {
|
|
570
|
+
if (detail?.id) detailsMap.set(detail.id, detail)
|
|
571
|
+
})
|
|
572
|
+
// Add loading details (will overwrite duplicates)
|
|
573
|
+
loadingDetails.forEach(detail => {
|
|
574
|
+
if (detail?.id) detailsMap.set(detail.id, detail)
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
// Use worksheetInfo from whichever has it (prefer QC, fallback to loading)
|
|
578
|
+
let items = {
|
|
579
|
+
worksheetInfo: qcData?.worksheetInfo || loadingData?.worksheetInfo,
|
|
580
|
+
worksheetDetailInfos: Array.from(detailsMap.values())
|
|
581
|
+
}
|
|
512
582
|
items.deliveryOrderInfoList = await this.fetchDeliveryOrders(orderNo)
|
|
513
583
|
this._data = items
|
|
514
584
|
}
|
|
@@ -70,7 +70,8 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
70
70
|
toteNo: String,
|
|
71
71
|
_pageSettings: Object,
|
|
72
72
|
_isReplenishment: Boolean,
|
|
73
|
-
_hasVas: Boolean
|
|
73
|
+
_hasVas: Boolean,
|
|
74
|
+
_enableToteScanningValue: Number
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -148,10 +149,11 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
148
149
|
})
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
// Tote button only appears when enable-tote-scanning is exactly 1 (not 2 or higher)
|
|
151
153
|
if (
|
|
152
154
|
this.refPickingType === PICKING_TYPES.PICKING.value &&
|
|
153
155
|
this.releaseGoodNo &&
|
|
154
|
-
this.
|
|
156
|
+
this._enableToteScanningValue === 1 &&
|
|
155
157
|
!this._isReplenishment
|
|
156
158
|
) {
|
|
157
159
|
actions.push({
|
|
@@ -328,6 +330,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
328
330
|
this._isLastUnit = false
|
|
329
331
|
this._isReplenishment = false
|
|
330
332
|
this._hasVas = false
|
|
333
|
+
this._enableToteScanningValue = 0
|
|
331
334
|
}
|
|
332
335
|
|
|
333
336
|
render() {
|
|
@@ -1076,7 +1079,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
1076
1079
|
binLocation:
|
|
1077
1080
|
this._hasBinPicking && (!this.toteNo || this.tote == '') ? this.binLocationInput.value : null,
|
|
1078
1081
|
serialNumber: this._isRequireSerialNumberScanningOutbound ? this.serialInput.value : null,
|
|
1079
|
-
toteNo: this.
|
|
1082
|
+
toteNo: this._enableToteScanningValue >= 1 && this.toteNo !== '' ? this.toteNo : null
|
|
1080
1083
|
}
|
|
1081
1084
|
})
|
|
1082
1085
|
|
|
@@ -1167,7 +1170,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
1167
1170
|
serialNumber: this._isRequireSerialNumberScanningOutbound ? this.serialInput.value : null,
|
|
1168
1171
|
binLocation:
|
|
1169
1172
|
this._hasBinPicking && (!this.toteNo || this.tote == '') ? this.binLocationInput.value : null,
|
|
1170
|
-
toteNo: this.
|
|
1173
|
+
toteNo: this._enableToteScanningValue >= 1 && this.toteNo !== '' ? this.toteNo : null,
|
|
1171
1174
|
pickingQty: qty
|
|
1172
1175
|
}
|
|
1173
1176
|
})
|
|
@@ -2306,7 +2309,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
2306
2309
|
const settingsMap = new Map(pageSettings.settings.map(setting => [setting.name, setting.value]))
|
|
2307
2310
|
|
|
2308
2311
|
this._enableProductScanning = settingsMap.get('enable-product-scanning') === 'true'
|
|
2309
|
-
this.
|
|
2312
|
+
this._enableToteScanningValue = parseInt(settingsMap.get('enable-tote-scanning') || '0', 10)
|
|
2310
2313
|
this._enableInputQty = settingsMap.get('enable-input-qty')
|
|
2311
2314
|
this._qrProcessing = settingsMap.get('qr-code-scanning')
|
|
2312
2315
|
|
|
@@ -2351,10 +2354,12 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
|
|
|
2351
2354
|
const settingsMap = new Map(pageSettings.settings.map(setting => [setting.name, setting.value]))
|
|
2352
2355
|
|
|
2353
2356
|
this._enableProductScanning = settingsMap.get('enable-product-scanning') === 'true'
|
|
2354
|
-
this.
|
|
2357
|
+
this._enableToteScanningValue = parseInt(settingsMap.get('enable-tote-scanning') || '0', 10)
|
|
2355
2358
|
this._enableInputQty = settingsMap.get('enable-input-qty')
|
|
2356
2359
|
this._qrProcessing = settingsMap.get('qr-code-scanning')
|
|
2357
2360
|
|
|
2361
|
+
// Update context after settings are loaded to ensure tote button visibility is correct
|
|
2362
|
+
this.updateContext()
|
|
2358
2363
|
this.focusOnInput()
|
|
2359
2364
|
}
|
|
2360
2365
|
|
|
@@ -272,14 +272,14 @@ export class PickingExecutionBase extends LitElement {
|
|
|
272
272
|
this.qtyInput.value = ''
|
|
273
273
|
this._focusOnCartonNoInput()
|
|
274
274
|
|
|
275
|
-
//fetch data
|
|
276
|
-
|
|
277
|
-
this._executionData.worksheetNo,
|
|
278
|
-
this.parent._context.locationSortingRules,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
this.
|
|
275
|
+
//fetch data for both tabs to ensure banner condition is accurate
|
|
276
|
+
const [pickingData, pickedData] = await Promise.all([
|
|
277
|
+
this.fetchWorksheet(this._executionData.worksheetNo, this.parent._context.locationSortingRules, 'PICKING'),
|
|
278
|
+
this.fetchWorksheet(this._executionData.worksheetNo, this.parent._context.locationSortingRules, 'PICKED')
|
|
279
|
+
])
|
|
280
|
+
|
|
281
|
+
this.pickingData = pickingData.worksheetDetailInfos
|
|
282
|
+
this.pickedData = pickedData.worksheetDetailInfos
|
|
283
283
|
}
|
|
284
284
|
} catch (e) {
|
|
285
285
|
showToast(e)
|