@things-factory/operato-wms 4.3.795 → 4.3.798

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/client/pages/components/worksheet-information-popup.js +60 -8
  2. package/client/pages/constants/order.js +7 -1
  3. package/client/pages/order/release-order/b2b/b2b-order-list.js +17 -1
  4. package/client/pages/order/release-order/release-order-detail.js +19 -0
  5. package/client/pages/order/transport-order/print-delivery-note.js +19 -1
  6. package/client/pages/outbound/loading-v2/load-by-qty.js +10 -0
  7. package/client/pages/outbound/loading-v2/loading-execution-base.js +22 -8
  8. package/client/pages/outbound/loading-v2/loading-execution.js +37 -8
  9. package/client/pages/outbound/loading-v2/loading.js +6 -0
  10. package/client/pages/outbound/loading-v2/release-good-info.js +3 -0
  11. package/client/pages/outbound/picking-product.js +138 -17
  12. package/client/pages/outbound/picking-same-carton-popup.js +217 -0
  13. package/client/pages/outbound/picking-v2/picking-execution-base.js +20 -2
  14. package/client/pages/outbound/picking-v2/picking-execution.js +4 -3
  15. package/client/pages/outbound/picking-v2/picking.js +3 -0
  16. package/client/pages/outbound/single-outbound-worksheet.js +18 -1
  17. package/client/pages/outbound/sorting-product.js +24 -1
  18. package/client/pages/outbound/worksheet-zone-picking.js +1 -0
  19. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js +9 -4
  20. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js.map +1 -1
  21. package/dist-server/graphql/resolvers/outbound/load-by-qty.js +1 -1
  22. package/dist-server/graphql/resolvers/outbound/load-by-qty.js.map +1 -1
  23. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js +5 -1
  24. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js.map +1 -1
  25. package/dist-server/graphql/resolvers/zone-worksheet/zone-worksheet.js +8 -2
  26. package/dist-server/graphql/resolvers/zone-worksheet/zone-worksheet.js.map +1 -1
  27. package/package.json +8 -8
  28. package/server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.ts +7 -1
  29. package/server/graphql/resolvers/outbound/load-by-qty.ts +1 -1
  30. package/server/graphql/resolvers/outbound/loading-worksheet-v2.ts +9 -2
  31. package/server/graphql/resolvers/zone-worksheet/zone-worksheet.ts +32 -23
  32. package/translations/en.json +2 -0
  33. package/translations/ko.json +2 -0
  34. package/translations/ms.json +2 -0
  35. package/translations/zh.json +2 -0
@@ -7,6 +7,7 @@ import '../components/worksheet-information-popup'
7
7
  import './serial-number-popup'
8
8
  import './tote-popup'
9
9
  import './vas-list-popup'
10
+ import './picking-same-carton-popup'
10
11
 
11
12
  import gql from 'graphql-tag'
12
13
  import { css, html } from 'lit-element'
@@ -580,7 +581,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
580
581
  settings: {
581
582
  name: 'name',
582
583
  operator: 'in',
583
- value: ['enable-bin-picking', 'camera-scan-only', 'enable-picking-random-seq', 'disable-missing-button']
584
+ value: ['enable-bin-picking', 'camera-scan-only', 'enable-picking-random-seq', 'disable-missing-button', 'display-conversion']
584
585
  }
585
586
  })
586
587
 
@@ -590,6 +591,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
590
591
  let cameraScanOnly = settingsMap.get('camera-scan-only') === 'true'
591
592
  let enablePickingRandomSeq = settingsMap.get('enable-picking-random-seq') === 'true'
592
593
  let disableMissingButton = settingsMap.get('disable-missing-button') === 'true'
594
+ let displayConversion = settingsMap.get('display-conversion') === 'true'
593
595
 
594
596
  const locationSortingTask = fetchLocationSortingRule(LOCATION_SORTING_RULE.PICKING_PRODUCT.value)
595
597
 
@@ -725,6 +727,14 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
725
727
  label: true,
726
728
  width: 140
727
729
  },
730
+ {
731
+ type: 'string',
732
+ name: 'conversion',
733
+ header: i18next.t('field.pack_conversion'),
734
+ label: true,
735
+ hidden: !displayConversion,
736
+ width: 140
737
+ },
728
738
  {
729
739
  type: 'string',
730
740
  name: 'releaseProgress',
@@ -749,7 +759,8 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
749
759
  this._pageSettings = {
750
760
  ...this._pageSettings,
751
761
  enablePickingRandomSeq: (await enablePickingRandomSeq) || false,
752
- cameraScanOnly: (await cameraScanOnly) || false
762
+ cameraScanOnly: (await cameraScanOnly) || false,
763
+ displayConversion: (await displayConversion) || false
753
764
  }
754
765
  }
755
766
 
@@ -1311,14 +1322,27 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1311
1322
  const scannedCartonId = this.cartonInput.value.trim()
1312
1323
  if (scannedCartonId) {
1313
1324
  let candidates = (this.data?.records || []).filter(
1314
- rec => rec.cartonId === scannedCartonId && rec.status !== 'DONE' && rec.status !== 'MISSING'
1325
+ rec =>
1326
+ rec.cartonId === scannedCartonId &&
1327
+ rec.status !== 'DONE' &&
1328
+ rec.status !== 'MISSING' &&
1329
+ !rec.completed &&
1330
+ rec.pickedQty < rec.releaseQty
1315
1331
  )
1316
- candidates = this._filterCandidatesByInputQty(candidates)
1317
-
1318
1332
  // Validate carton id: must have at least one candidate
1319
1333
  if (!candidates || candidates.length === 0) {
1320
1334
  this._focusOnCartonNoInput()
1321
- throw new Error(i18next.t('text.invalid_carton_id'))
1335
+ const hasFullyPickedForCarton = (this.data?.records || []).some(
1336
+ rec =>
1337
+ rec.cartonId === scannedCartonId &&
1338
+ rec.status !== 'DONE' &&
1339
+ rec.status !== 'MISSING' &&
1340
+ !rec.completed &&
1341
+ rec.pickedQty >= rec.releaseQty
1342
+ )
1343
+ throw new Error(
1344
+ i18next.t(hasFullyPickedForCarton ? 'text.qty_exceed_release_qty' : 'text.invalid_carton_id')
1345
+ )
1322
1346
  }
1323
1347
 
1324
1348
  if (this._enableProductScanning) {
@@ -1328,11 +1352,14 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1328
1352
  throw new Error(i18next.t('text.empty_product_barcode'))
1329
1353
  }
1330
1354
 
1355
+ this._popupCancelled = false
1331
1356
  const matchedRecord = await this._selectRecordByGtinFromCandidates(
1332
1357
  this.productBarcodeInput.value.trim(),
1333
1358
  candidates
1334
1359
  )
1335
1360
 
1361
+ if (this._popupCancelled) return { value: false }
1362
+
1336
1363
  if (!matchedRecord) {
1337
1364
  this._focusOnProductBarcodeInput()
1338
1365
  this.productBarcodeInput.value = ''
@@ -1345,7 +1372,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1345
1372
  }
1346
1373
  } else {
1347
1374
  // Product scanning disabled: pick a deterministic candidate
1348
- const matchedRecord = this._pickBestCandidate(candidates)
1375
+ const matchedRecord = this._pickBestCandidate(this._filterCandidatesByInputQty(candidates))
1349
1376
  if (
1350
1377
  matchedRecord &&
1351
1378
  (!this._selectedOrderInventory || this._selectedOrderInventory.name !== matchedRecord.name)
@@ -1409,14 +1436,36 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1409
1436
  if (this._pageSettings.enablePickingRandomSeq == true) {
1410
1437
  let cartonInputValue = this.cartonInput.value
1411
1438
  let candidates = this.data.records.filter(
1412
- rec => rec.cartonId === cartonInputValue && rec.status !== 'DONE' && rec.status !== 'MISSING'
1439
+ rec =>
1440
+ rec.cartonId === cartonInputValue &&
1441
+ rec.status !== 'DONE' &&
1442
+ rec.status !== 'MISSING' &&
1443
+ !rec.completed &&
1444
+ rec.pickedQty < rec.releaseQty
1413
1445
  )
1414
- candidates = this._filterCandidatesByInputQty(candidates)
1446
+ // If no candidates remain, check if it's because all matching items are already fully picked
1447
+ if (!candidates.length) {
1448
+ const hasFullyPickedForCarton = this.data.records.some(
1449
+ rec =>
1450
+ rec.cartonId === cartonInputValue &&
1451
+ rec.status !== 'DONE' &&
1452
+ rec.status !== 'MISSING' &&
1453
+ !rec.completed &&
1454
+ rec.pickedQty >= rec.releaseQty
1455
+ )
1456
+ if (hasFullyPickedForCarton) {
1457
+ this._focusOnCartonNoInput()
1458
+ throw new Error(i18next.t('text.qty_exceed_release_qty'))
1459
+ }
1460
+ }
1461
+
1415
1462
  if (this._enableProductScanning && this.productBarcodeInput?.value) {
1463
+ this._popupCancelled = false
1416
1464
  foundRecord = await this._selectRecordByGtinFromCandidates(
1417
1465
  this.productBarcodeInput.value.trim(),
1418
1466
  candidates
1419
1467
  )
1468
+ if (this._popupCancelled) return { value: false }
1420
1469
  }
1421
1470
  // If product barcode is provided but no GTIN match, stop without fallback
1422
1471
  if (this._enableProductScanning && this.productBarcodeInput?.value && !foundRecord) {
@@ -1427,7 +1476,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1427
1476
 
1428
1477
  // No product barcode: deterministically choose best candidate among duplicates
1429
1478
  if (!foundRecord && (!this._enableProductScanning || !this.productBarcodeInput?.value)) {
1430
- foundRecord = this._pickBestCandidate(candidates)
1479
+ foundRecord = this._pickBestCandidate(this._filterCandidatesByInputQty(candidates))
1431
1480
  }
1432
1481
 
1433
1482
  if (foundRecord) {
@@ -1683,6 +1732,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1683
1732
  recall
1684
1733
  crossDocking
1685
1734
  type
1735
+ rtm
1686
1736
  }
1687
1737
  replenishment {
1688
1738
  id
@@ -1724,6 +1774,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1724
1774
  targetName
1725
1775
  packingType
1726
1776
  packingSize
1777
+ conversion
1727
1778
  location {
1728
1779
  name
1729
1780
  description
@@ -1779,7 +1830,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1779
1830
  this._crossDocking = worksheetInfo.releaseGood?._crossDocking
1780
1831
  this._bizplaceName = worksheetInfo?.bizplaceName
1781
1832
  this._orderNo = this.releaseGoodNo
1782
- this._worksheetInfo = worksheetInfo
1833
+ this._worksheetInfo = { ...worksheetInfo, rtm: worksheetInfo.releaseGood?.rtm }
1783
1834
  let data = { records: this.formatWorksheetDetailInfos(worksheetDetailInfos) }
1784
1835
  this.data = { ...data }
1785
1836
 
@@ -1871,6 +1922,14 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1871
1922
  partnerDomainId
1872
1923
  customerCompanyDomainId
1873
1924
  startedAt
1925
+ rtm
1926
+ refNo
1927
+ refNo2
1928
+ refNo3
1929
+ shippingProvider
1930
+ trackingNo
1931
+ marketplaceStatus
1932
+ containerNo
1874
1933
  }
1875
1934
  worksheetDetailInfos {
1876
1935
  hasMissingInventoryChanges
@@ -1892,6 +1951,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1892
1951
  status
1893
1952
  packingType
1894
1953
  packingSize
1954
+ conversion
1895
1955
  location {
1896
1956
  name
1897
1957
  }
@@ -2291,6 +2351,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
2291
2351
  targetName
2292
2352
  packingType
2293
2353
  packingSize
2354
+ conversion
2294
2355
  location {
2295
2356
  name
2296
2357
  description
@@ -2486,7 +2547,9 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
2486
2547
  _filterCandidatesByInputQty(candidates) {
2487
2548
  const typedQty = this._getTypedQty()
2488
2549
  if (!typedQty || !Array.isArray(candidates) || !candidates.length) return candidates
2489
- const matches = candidates.filter(rec => parseFloat(rec?.releaseQty) === typedQty)
2550
+ const matches = candidates.filter(
2551
+ rec => (parseFloat(rec?.releaseQty) - parseFloat(rec?.pickedQty ?? 0)) === typedQty
2552
+ )
2490
2553
  return matches.length ? matches : candidates
2491
2554
  }
2492
2555
 
@@ -2521,9 +2584,6 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
2521
2584
  try {
2522
2585
  if (!gtin || !candidates?.length) return null
2523
2586
 
2524
- // Apply qty-based narrowing first when duplicates exist
2525
- candidates = this._filterCandidatesByInputQty(candidates)
2526
-
2527
2587
  // Use checkGtin to resolve product-packing info to match candidate rows
2528
2588
  const response = await client.query({
2529
2589
  query: gql`
@@ -2563,16 +2623,77 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
2563
2623
  itm.packingSize === rec.packingSize
2564
2624
  )
2565
2625
  )
2566
- if (exactMatches.length) return this._pickBestCandidate(exactMatches)
2626
+ if (exactMatches.length) return await this._pickBestCandidateOrShowPopup(exactMatches)
2567
2627
 
2568
2628
  const productOnlyMatches = candidates.filter(rec => items.some(itm => itm.product?.id === rec?.product?.id))
2569
- if (productOnlyMatches.length) return this._pickBestCandidate(productOnlyMatches)
2629
+ if (productOnlyMatches.length) return await this._pickBestCandidateOrShowPopup(productOnlyMatches)
2570
2630
 
2571
2631
  return null
2572
2632
  } catch (e) {
2573
2633
  return null
2574
2634
  }
2575
2635
  }
2636
+
2637
+ _hasDuplicateSameProduct(candidates) {
2638
+ return this._getDuplicateSameProductRows(candidates).length > 0
2639
+ }
2640
+
2641
+ _getDuplicateSameProductRows(candidates) {
2642
+ if (!Array.isArray(candidates) || candidates.length <= 1) return []
2643
+
2644
+ // Group by cartonId + SKU, return only rows that belong to a duplicate group
2645
+ const groups = {}
2646
+ candidates.forEach(c => {
2647
+ const key = `${c?.cartonId || ''}|${c?.product?.sku || ''}`
2648
+ if (!groups[key]) groups[key] = []
2649
+ groups[key].push(c)
2650
+ })
2651
+
2652
+ const duplicateRows = []
2653
+ for (const rows of Object.values(groups)) {
2654
+ if (rows.length > 1) duplicateRows.push(...rows)
2655
+ }
2656
+ return duplicateRows
2657
+ }
2658
+
2659
+ _showPickingSameCartonPopup(candidates) {
2660
+ return new Promise(resolve => {
2661
+ openPopup(
2662
+ html`
2663
+ <picking-same-carton-popup
2664
+ .pickingInventories="${candidates}"
2665
+ @selection="${e => {
2666
+ resolve(e.detail)
2667
+ }}"
2668
+ ></picking-same-carton-popup>
2669
+ `,
2670
+ {
2671
+ backdrop: true,
2672
+ size: 'large',
2673
+ title: i18next.t('title.inventory_selection')
2674
+ }
2675
+ )
2676
+ })
2677
+ }
2678
+
2679
+ async _pickBestCandidateOrShowPopup(candidates) {
2680
+ if (!Array.isArray(candidates) || !candidates.length) return null
2681
+
2682
+ // If multiple candidates have the same cartonId + SKU, show popup for user to choose
2683
+ const duplicateRows = this._getDuplicateSameProductRows(candidates)
2684
+ if (duplicateRows.length) {
2685
+ const selectedRecord = await this._showPickingSameCartonPopup(duplicateRows)
2686
+ if (!selectedRecord) {
2687
+ // User cancelled the popup — signal callers to do nothing
2688
+ this._popupCancelled = true
2689
+ return null
2690
+ }
2691
+ return selectedRecord
2692
+ }
2693
+
2694
+ // Otherwise, use the existing logic to pick the best candidate
2695
+ return this._pickBestCandidate(candidates)
2696
+ }
2576
2697
  }
2577
2698
 
2578
2699
  window.customElements.define('picking-product', PickingProduct)
@@ -0,0 +1,217 @@
1
+ import '@things-factory/grist-ui'
2
+
3
+ import { css, html, LitElement } from 'lit-element'
4
+ import { connect } from 'pwa-helpers/connect-mixin'
5
+
6
+ import { SingleColumnFormStyles } from '@things-factory/form-ui'
7
+ import { i18next, localize } from '@things-factory/i18n-base'
8
+ import { store } from '@things-factory/shell'
9
+ import { isMobileDevice } from '@things-factory/utils'
10
+
11
+ class PickingSameCartonPopup extends connect(store)(localize(i18next)(LitElement)) {
12
+ static get properties() {
13
+ return {
14
+ config: Object,
15
+ data: Object,
16
+ pickingInventories: Array
17
+ }
18
+ }
19
+
20
+ constructor() {
21
+ super()
22
+ }
23
+
24
+ static get styles() {
25
+ return [
26
+ SingleColumnFormStyles,
27
+ css`
28
+ .button-container {
29
+ padding: 10px 0 12px 0;
30
+ text-align: center;
31
+ }
32
+ .button-container mwc-button {
33
+ margin: 0 5px;
34
+ }
35
+ data-grist {
36
+ overflow-y: hidden;
37
+ flex: 1;
38
+ }
39
+ .grist {
40
+ display: flex;
41
+ flex-direction: column;
42
+ flex: 1;
43
+ overflow-y: auto;
44
+ }
45
+ :host {
46
+ padding: 10px;
47
+ display: flex;
48
+ flex-direction: column;
49
+ overflow: hidden;
50
+ background-color: white;
51
+ }
52
+ .header {
53
+ padding: 10px;
54
+ font-weight: bold;
55
+ font-size: 14px;
56
+ color: var(--secondary-color);
57
+ border-bottom: 1px solid var(--secondary-color);
58
+ margin-bottom: 10px;
59
+ }
60
+ `
61
+ ]
62
+ }
63
+
64
+ get dataGrist() {
65
+ return this.shadowRoot.querySelector('data-grist')
66
+ }
67
+
68
+ updated(changeProps) {
69
+ if (changeProps.has('pickingInventories')) {
70
+ if (this.pickingInventories && this.pickingInventories.length) {
71
+ this.data = {
72
+ records: this.pickingInventories.map(inv => ({
73
+ ...inv,
74
+ locationName: inv.location?.name || ''
75
+ }))
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ render() {
82
+ return html`
83
+ <div class="header">
84
+ ${i18next.t('text.please_select_an_inventory')}
85
+ </div>
86
+
87
+ <div class="grist">
88
+ <data-grist
89
+ id="grist"
90
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
91
+ .config=${this.config}
92
+ .data="${this.data}"
93
+ ></data-grist>
94
+ </div>
95
+
96
+ <div class="button-container">
97
+ <mwc-button
98
+ raised
99
+ @click="${this._selectPickingInventory.bind(this)}"
100
+ label="${i18next.t('button.select')}"
101
+ ></mwc-button>
102
+ <mwc-button
103
+ @click="${() => {
104
+ this.dispatchEvent(new CustomEvent('selection', { detail: null }))
105
+ history.back()
106
+ }}"
107
+ label="${i18next.t('button.cancel')}"
108
+ ></mwc-button>
109
+ </div>
110
+ `
111
+ }
112
+
113
+ firstUpdated() {
114
+ this.config = {
115
+ pagination: { infinite: true },
116
+ rows: { selectable: { multiple: false }, appendable: false },
117
+ list: {
118
+ fields: [
119
+ 'locationName',
120
+ 'palletId',
121
+ 'batchId',
122
+ 'batchIdRef',
123
+ 'expirationDate',
124
+ 'qty',
125
+ 'releaseQty',
126
+ 'packingType'
127
+ ]
128
+ },
129
+ columns: [
130
+ { type: 'gutter', gutterName: 'sequence' },
131
+ { type: 'gutter', gutterName: 'row-selector', multiple: false, header: { renderer: () => '' } },
132
+ { name: 'name', hidden: true },
133
+ {
134
+ type: 'string',
135
+ name: 'locationName',
136
+ label: true,
137
+ header: i18next.t('field.location'),
138
+ width: 120
139
+ },
140
+ {
141
+ type: 'string',
142
+ name: 'palletId',
143
+ label: true,
144
+ header: i18next.t('field.pallet_id'),
145
+ width: 120
146
+ },
147
+ {
148
+ type: 'string',
149
+ name: 'batchId',
150
+ label: true,
151
+ header: i18next.t('field.batch_no'),
152
+ width: 100
153
+ },
154
+ {
155
+ type: 'string',
156
+ name: 'batchIdRef',
157
+ label: true,
158
+ header: i18next.t('field.lot_id'),
159
+ width: 100
160
+ },
161
+ {
162
+ type: 'date',
163
+ name: 'expirationDate',
164
+ label: true,
165
+ header: i18next.t('field.expiration'),
166
+ width: 100
167
+ },
168
+ {
169
+ type: 'float',
170
+ name: 'qty',
171
+ label: true,
172
+ header: i18next.t('field.available_qty'),
173
+ width: 80
174
+ },
175
+ {
176
+ type: 'float',
177
+ name: 'releaseQty',
178
+ label: true,
179
+ header: i18next.t('field.release_qty'),
180
+ width: 80
181
+ },
182
+ {
183
+ type: 'string',
184
+ name: 'packingType',
185
+ label: true,
186
+ header: i18next.t('field.packing_type'),
187
+ width: 100
188
+ }
189
+ ]
190
+ }
191
+ }
192
+
193
+ async _selectPickingInventory() {
194
+ try {
195
+ const selectedInventory = this.dataGrist.selected[0]
196
+ if (!selectedInventory) throw new Error(i18next.t('text.please_select_inventory'))
197
+
198
+ this.dispatchEvent(new CustomEvent('selection', { detail: selectedInventory }))
199
+ history.back()
200
+ } catch (e) {
201
+ this._showToast(e)
202
+ }
203
+ }
204
+
205
+ _showToast({ type, message }) {
206
+ document.dispatchEvent(
207
+ new CustomEvent('notify', {
208
+ detail: {
209
+ type,
210
+ message
211
+ }
212
+ })
213
+ )
214
+ }
215
+ }
216
+
217
+ window.customElements.define('picking-same-carton-popup', PickingSameCartonPopup)
@@ -12,7 +12,7 @@ import { openPopup } from '@things-factory/layout-base'
12
12
  import { client, CustomAlert, gqlContext, PageView, store } from '@things-factory/shell'
13
13
  import { CommonButtonStyles } from '@things-factory/styles'
14
14
 
15
- import { showToast } from '../../../util'
15
+ import { fetchPageSettings, showToast } from '../../../util'
16
16
  import { BATCH_PICKING, PICKING, ZONE_PICKING, ORDER_INVENTORY_STATUS } from '../../constants'
17
17
 
18
18
  const PICKING_TYPES = { PICKING, BATCH_PICKING, ZONE_PICKING }
@@ -25,10 +25,26 @@ export class PickingExecutionBase extends LitElement {
25
25
  data: Object,
26
26
  pickingData: Object,
27
27
  pickedData: Object,
28
- _executionData: Object
28
+ _executionData: Object,
29
+ _displayConversion: Boolean
29
30
  }
30
31
  }
31
32
 
33
+ async connectedCallback() {
34
+ super.connectedCallback()
35
+
36
+ const pageSettings = await fetchPageSettings({
37
+ settings: {
38
+ name: 'name',
39
+ operator: 'in',
40
+ value: ['display-conversion']
41
+ }
42
+ })
43
+
44
+ const settingsMap = new Map(pageSettings?.settings?.map(setting => [setting.name, setting.value]) || [])
45
+ this._displayConversion = settingsMap.get('display-conversion') === 'true'
46
+ }
47
+
32
48
  get backButton() {
33
49
  return {
34
50
  title: i18next.t('button.back'),
@@ -392,6 +408,7 @@ export class PickingExecutionBase extends LitElement {
392
408
  status
393
409
  packingType
394
410
  packingSize
411
+ conversion
395
412
  }
396
413
  inventoryChanges {
397
414
  id
@@ -451,6 +468,7 @@ async function mapWorksheetData(data) {
451
468
  status: orderInv.status,
452
469
  packingType: orderInv.packingType,
453
470
  packingSize: orderInv.packingSize,
471
+ conversion: orderInv.conversion,
454
472
  location: {
455
473
  name: orderInv.inventory.location.name,
456
474
  description: orderInv.inventory.location.description
@@ -182,14 +182,15 @@ export class PickingExecution extends PickingExecutionBase {
182
182
  field: 'product.name',
183
183
  type: 'string',
184
184
  style: '-webkit-line-clamp: 2;'
185
- }
185
+ },
186
+ { header: 'SKU', field: 'product.sku', type: 'string' }
186
187
  ]
187
188
  },
188
189
  {
189
190
  type: 'row',
190
191
  content: [
191
- { header: 'SKU', field: 'product.sku', type: 'string' },
192
- { header: 'Packing Type', field: 'packingType', type: 'string' }
192
+ { header: 'Packing Type', field: 'packingType', type: 'string' },
193
+ ...(this._displayConversion ? [{ header: 'Pack Conversion', field: 'conversion', type: 'string' }] : [])
193
194
  ]
194
195
  },
195
196
  {
@@ -192,6 +192,7 @@ class PickingProductV2 extends connect(store)(localize(i18next)(PageView)) {
192
192
  status
193
193
  packingType
194
194
  packingSize
195
+ conversion
195
196
  }
196
197
  inventoryChanges {
197
198
  id
@@ -269,6 +270,7 @@ async function fetchWorksheet(worksheetNo, locationSortingRules) {
269
270
  status
270
271
  packingType
271
272
  packingSize
273
+ conversion
272
274
  }
273
275
  inventoryChanges {
274
276
  id
@@ -354,6 +356,7 @@ function mapWorksheetData(data) {
354
356
  status: orderInv.status,
355
357
  packingType: orderInv.packingType,
356
358
  packingSize: orderInv.packingSize,
359
+ conversion: orderInv.conversion,
357
360
  location: {
358
361
  name: orderInv.inventory.location.name,
359
362
  description: orderInv.inventory.location.description
@@ -14,7 +14,7 @@ import { CommonButtonStyles, ScrollbarStyles } from '@things-factory/styles'
14
14
  import { isMobileDevice } from '@things-factory/utils'
15
15
 
16
16
  import { fetchSettingRule } from '../../fetch-setting-value'
17
- import { WORKSHEET_STATUS, WORKSHEET_TYPE } from '../constants'
17
+ import { RTM_OPTIONS, WORKSHEET_STATUS, WORKSHEET_TYPE } from '../constants'
18
18
 
19
19
  import { applyParamsToFields } from '../../util'
20
20
 
@@ -237,6 +237,13 @@ class SingleOutboundWorksheet extends localize(i18next)(PageView) {
237
237
  },
238
238
  value: new Date().toISOString().split('T')[0]
239
239
  },
240
+ {
241
+ label: i18next.t('field.rtm'),
242
+ name: 'releaseGoodRtm',
243
+ type: 'select',
244
+ options: [{ value: '' }, ...RTM_OPTIONS.map(option => ({ name: option.label, value: option.value }))],
245
+ props: { searchOper: 'eq' }
246
+ },
240
247
  {
241
248
  label: i18next.t('field.cross_docking'),
242
249
  name: 'releaseGoodCrossDocking',
@@ -317,6 +324,14 @@ class SingleOutboundWorksheet extends localize(i18next)(PageView) {
317
324
  sortable: true,
318
325
  width: 180
319
326
  },
327
+ {
328
+ type: 'string',
329
+ name: 'releaseGoodRtm',
330
+ header: i18next.t('field.rtm'),
331
+ imex: { header: i18next.t('field.rtm'), key: 'releaseGoodRtm', width: 40, type: 'string' },
332
+ sortable: true,
333
+ width: 120
334
+ },
320
335
  {
321
336
  type: 'boolean',
322
337
  name: 'releaseGoodCrossDocking',
@@ -484,6 +499,7 @@ class SingleOutboundWorksheet extends localize(i18next)(PageView) {
484
499
  name
485
500
  description
486
501
  refNo
502
+ rtm
487
503
  crossDocking
488
504
  courierOption
489
505
  packingOption
@@ -533,6 +549,7 @@ class SingleOutboundWorksheet extends localize(i18next)(PageView) {
533
549
  return {
534
550
  ...item,
535
551
  releaseRefNo: item.releaseGood.refNo || '',
552
+ releaseGoodRtm: item.releaseGood.rtm || '',
536
553
  releaseGoodCrossDocking: item.releaseGood.crossDocking,
537
554
  releaseGoodCourierOption: item.releaseGood.courierOption,
538
555
  releaseGoodPackingOption: item.releaseGood.packingOption,