@things-factory/operato-wms 3.7.2 → 3.7.7

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 (84) hide show
  1. package/client/pages/components/select-product-popup.js +5 -1
  2. package/client/pages/constants/order.js +10 -0
  3. package/client/pages/inbound/putaway-product.js +162 -139
  4. package/client/pages/inventory-check/inspecting-product.js +146 -80
  5. package/client/pages/manifest/create-loading-manifest.js +314 -0
  6. package/client/pages/manifest/dispatcher-verification-popup.js +86 -0
  7. package/client/pages/manifest/loading-manifest-detail.js +345 -0
  8. package/client/pages/manifest/loading-manifest-list.js +305 -0
  9. package/client/pages/order/arrival-notice/create-arrival-notice.js +8 -1
  10. package/client/pages/order/goods-received-note/received-note-list.js +138 -6
  11. package/client/pages/order/release-order/b2b/b2b-order-list.js +45 -5
  12. package/client/pages/order/release-order/b2b/b2b-order-requests.js +45 -5
  13. package/client/pages/order/release-order/b2c/b2c-order-requests.js +181 -21
  14. package/client/pages/order/release-order/bulk-print-packing-list.js +23 -0
  15. package/client/pages/order/release-order/create-release-order.js +25 -4
  16. package/client/pages/order/release-order/receive-release-order-request.js +27 -24
  17. package/client/pages/order/release-order/release-extra-product-popup.js +189 -56
  18. package/client/pages/order/release-order/release-order-detail.js +292 -71
  19. package/client/pages/order/reverse-kitting-order/reverse-kitting-order-detail.js +115 -50
  20. package/client/pages/order/reverse-kitting-order/reverse-kitting-order-list.js +1 -6
  21. package/client/pages/order/reverse-kitting-order/reverse-kitting-pallet-label-popup.js +239 -0
  22. package/client/pages/order/transport-order/delivery-order-list.js +99 -59
  23. package/client/pages/outbound/loading-product.js +4 -1
  24. package/client/pages/outbound/merged-outbound-worksheet.js +39 -1
  25. package/client/pages/outbound/packing-product.js +4 -1
  26. package/client/pages/outbound/packing-worksheet-list.js +39 -1
  27. package/client/pages/outbound/picking-product.js +622 -290
  28. package/client/pages/outbound/serial-number-popup.js +280 -0
  29. package/client/pages/outbound/worksheet-picking.js +28 -2
  30. package/client/pages/report/product-label-report.js +401 -0
  31. package/client/pages/support/inventory-comparison.js +296 -0
  32. package/client/pages/vas/print-product-label.js +298 -132
  33. package/client/pages/vas/reverse-kitting/execute-reverse-kitting.js +30 -12
  34. package/client/pages/vas/reverse-kitting/popup-combination-sets.js +75 -5
  35. package/client/pages/vas/reverse-kitting/popup-reverse-kitting-template.js +113 -18
  36. package/client/route.js +23 -0
  37. package/dist-server/graphql/resolvers/custom/elccl/elccl-inventory-history-summary-report.js +64 -54
  38. package/dist-server/graphql/resolvers/custom/elccl/elccl-inventory-history-summary-report.js.map +1 -1
  39. package/dist-server/graphql/resolvers/extra/add-release-good-products.js +34 -9
  40. package/dist-server/graphql/resolvers/extra/add-release-good-products.js.map +1 -1
  41. package/dist-server/graphql/resolvers/index.js +3 -1
  42. package/dist-server/graphql/resolvers/index.js.map +1 -1
  43. package/dist-server/graphql/resolvers/inventory-comparison/index.js +6 -0
  44. package/dist-server/graphql/resolvers/inventory-comparison/index.js.map +1 -0
  45. package/dist-server/graphql/resolvers/inventory-comparison/inventory-comparison-list.js +78 -0
  46. package/dist-server/graphql/resolvers/inventory-comparison/inventory-comparison-list.js.map +1 -0
  47. package/dist-server/graphql/resolvers/reports/index.js +2 -1
  48. package/dist-server/graphql/resolvers/reports/index.js.map +1 -1
  49. package/dist-server/graphql/resolvers/reports/product-label-report.js +56 -0
  50. package/dist-server/graphql/resolvers/reports/product-label-report.js.map +1 -0
  51. package/dist-server/graphql/types/index.js +5 -2
  52. package/dist-server/graphql/types/index.js.map +1 -1
  53. package/dist-server/graphql/types/inventory-comparison/index.js +13 -0
  54. package/dist-server/graphql/types/inventory-comparison/index.js.map +1 -0
  55. package/dist-server/graphql/types/inventory-comparison/inventory-comparison-list.js +14 -0
  56. package/dist-server/graphql/types/inventory-comparison/inventory-comparison-list.js.map +1 -0
  57. package/dist-server/graphql/types/inventory-comparison/inventory-comparison.js +18 -0
  58. package/dist-server/graphql/types/inventory-comparison/inventory-comparison.js.map +1 -0
  59. package/dist-server/graphql/types/reports/index.js +6 -1
  60. package/dist-server/graphql/types/reports/index.js.map +1 -1
  61. package/dist-server/graphql/types/reports/product-label-report-list.js +14 -0
  62. package/dist-server/graphql/types/reports/product-label-report-list.js.map +1 -0
  63. package/dist-server/graphql/types/reports/product-label-report.js +16 -0
  64. package/dist-server/graphql/types/reports/product-label-report.js.map +1 -0
  65. package/package.json +65 -65
  66. package/server/graphql/resolvers/custom/elccl/elccl-inventory-history-summary-report.ts +75 -65
  67. package/server/graphql/resolvers/extra/add-release-good-products.ts +57 -15
  68. package/server/graphql/resolvers/index.ts +3 -1
  69. package/server/graphql/resolvers/inventory-comparison/index.ts +5 -0
  70. package/server/graphql/resolvers/inventory-comparison/inventory-comparison-list.ts +99 -0
  71. package/server/graphql/resolvers/reports/index.ts +3 -1
  72. package/server/graphql/resolvers/reports/product-label-report.ts +56 -0
  73. package/server/graphql/types/index.ts +5 -2
  74. package/server/graphql/types/inventory-comparison/index.ts +11 -0
  75. package/server/graphql/types/inventory-comparison/inventory-comparison-list.ts +8 -0
  76. package/server/graphql/types/inventory-comparison/inventory-comparison.ts +12 -0
  77. package/server/graphql/types/reports/index.ts +6 -1
  78. package/server/graphql/types/reports/product-label-report-list.ts +8 -0
  79. package/server/graphql/types/reports/product-label-report.ts +10 -0
  80. package/things-factory.config.js +24 -0
  81. package/translations/en.json +64 -12
  82. package/translations/ko.json +50 -1
  83. package/translations/ms.json +51 -1
  84. package/translations/zh.json +50 -1
@@ -3,6 +3,7 @@ import '@things-factory/grist-ui'
3
3
  import './outbound-reusable-pallet'
4
4
  import './picking-replacement-popup'
5
5
  import './show-my-worksheet-list-popup'
6
+ import './serial-number-popup'
6
7
 
7
8
  import gql from 'graphql-tag'
8
9
  import { css, html } from 'lit-element'
@@ -11,13 +12,13 @@ import { connect } from 'pwa-helpers/connect-mixin.js'
11
12
  import { MultiColumnFormStyles, SingleColumnFormStyles } from '@things-factory/form-ui'
12
13
  import { i18next, localize } from '@things-factory/i18n-base'
13
14
  import { openPopup } from '@things-factory/layout-base'
14
- import { client, CustomAlert, navigate, PageView, store } from '@things-factory/shell'
15
+ import { client, CustomAlert, gqlContext, PageView, store } from '@things-factory/shell'
15
16
  import { CommonButtonStyles } from '@things-factory/styles'
16
17
  import { isMobileDevice } from '@things-factory/utils'
17
18
 
18
19
  import { fetchLocationSortingRule } from '../../fetch-location-sorting-rule'
19
20
  import { fetchSettingRule } from '../../fetch-setting-value'
20
- import { BATCH_PICKING, LOCATION_SORTING_RULE, PICKING, WORKSHEET_STATUS } from '../constants'
21
+ import { BATCH_PICKING, LOCATION_SORTING_RULE, PICKING } from '../constants'
21
22
 
22
23
  const PICKING_TYPES = { PICKING, BATCH_PICKING }
23
24
  class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
@@ -27,22 +28,24 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
27
28
  worksheetNo: String,
28
29
  config: Object,
29
30
  data: Object,
31
+ isWholePicking: Boolean,
32
+ refPickingType: String,
33
+ locationNameSplit: Array,
34
+ locations: Array,
35
+ orderNo: String,
30
36
  _productName: String,
31
37
  _productSku: String,
32
- _selectedTaskStatus: String,
38
+ _isRequireSerialNumberScanning: Boolean,
39
+ // _selectedTaskStatus: String,
33
40
  _reusablePalletList: Object,
34
41
  _hasBinPicking: Boolean,
35
42
  _isCartonLabel: Boolean,
36
43
  _enableProductScanning: Boolean,
37
- isWholePicking: Boolean,
38
- isReplacement: Boolean,
39
- refPickingType: String,
40
- crossDocking: Boolean,
41
- incompleteLocationName: Boolean,
42
- locationNameSplit: Array,
43
- locations: Array,
44
- orderNo: String,
45
- _isExecuting: Boolean
44
+ _isReplacement: Boolean,
45
+ _crossDocking: Boolean,
46
+ _incompleteLocationName: Boolean,
47
+ _isExecuting: Boolean,
48
+ _currentBinLocation: String
46
49
  }
47
50
  }
48
51
 
@@ -105,7 +108,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
105
108
  padding-top: var(--grist-title-with-grid-padding);
106
109
  }
107
110
 
108
- div.reusable_pallet {
111
+ div.full_width {
109
112
  grid-column: span 12 / auto;
110
113
  display: inline-flex;
111
114
  align-items: center;
@@ -119,41 +122,23 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
119
122
  :host {
120
123
  display: block;
121
124
  }
122
- }
123
- `
124
- ]
125
- }
126
-
127
- get context() {
128
- var actions = []
129
-
130
- actions.push({
131
- title: i18next.t('button.my_worksheet_list'),
132
- action: this.showWorksheetList.bind(this),
133
- ...CommonButtonStyles.inspect
134
- })
135
-
136
- if ((this.releaseGoodNo && this.completed) || (this.taskNo && this.completed)) {
137
- actions.push({
138
- title: i18next.t('button.complete'),
139
- action: this._complete.bind(this),
140
- ...CommonButtonStyles.submit
141
- })
142
- }
143
125
 
144
- return {
145
- title: i18next.t('title.picking'),
146
- help: 'operato-wms/outbound/picking-product',
147
- actions
148
- }
149
- }
126
+ ////// For Small Mobile View temporarily disabled
127
+ // .grist {
128
+ // display: block;
129
+ // flex: none;
130
+ // }
150
131
 
151
- get releaseGoodNoInput() {
152
- return this.shadowRoot.querySelector('barcode-scanable-input[name=releaseGoodNo]').shadowRoot.querySelector('input')
153
- }
132
+ // data-grist {
133
+ // max-width: 90%;
134
+ // }
154
135
 
155
- get taskNoInput() {
156
- return this.shadowRoot.querySelector('barcode-scanable-input[name=taskNo]').shadowRoot.querySelector('input')
136
+ // .left-column {
137
+ // max-height: 200px;
138
+ // }
139
+ }
140
+ `
141
+ ]
157
142
  }
158
143
 
159
144
  get infoForm() {
@@ -168,26 +153,40 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
168
153
  return this.shadowRoot.querySelector('data-grist')
169
154
  }
170
155
 
156
+ get releaseGoodNoInput() {
157
+ return this.shadowRoot
158
+ .querySelector('barcode-scanable-input[name=releaseGoodNo]')
159
+ ?.shadowRoot?.querySelector('input')
160
+ }
161
+
162
+ get taskNoInput() {
163
+ return this.shadowRoot.querySelector('barcode-scanable-input[name=taskNo]')?.shadowRoot.querySelector('input')
164
+ }
165
+
166
+ get binLocationInput() {
167
+ return this.shadowRoot.querySelector('barcode-scanable-input[name=binLocation]')?.shadowRoot.querySelector('input')
168
+ }
169
+
171
170
  get palletInput() {
172
- return this.shadowRoot.querySelector('barcode-scanable-input[name=palletId]').shadowRoot.querySelector('input')
171
+ return this.shadowRoot.querySelector('barcode-scanable-input[name=palletId]')?.shadowRoot.querySelector('input')
173
172
  }
174
173
 
175
174
  get cartonInput() {
176
- return this.shadowRoot.querySelector('barcode-scanable-input[name=cartonId]').shadowRoot.querySelector('input')
175
+ return this.shadowRoot.querySelector('barcode-scanable-input[name=cartonId]')?.shadowRoot.querySelector('input')
177
176
  }
178
177
 
179
178
  get locationInput() {
180
- return this.shadowRoot.querySelector('barcode-scanable-input[name=locationName]').shadowRoot.querySelector('input')
179
+ return this.shadowRoot.querySelector('barcode-scanable-input[name=locationName]')?.shadowRoot.querySelector('input')
181
180
  }
182
181
 
183
182
  get productBarcodeInput() {
184
183
  return this.shadowRoot
185
- .querySelector('barcode-scanable-input[name=productBarcode]')
186
- .shadowRoot.querySelector('input')
184
+ ?.querySelector('barcode-scanable-input[name=productBarcode]')
185
+ ?.shadowRoot.querySelector('input')
187
186
  }
188
187
 
189
- get binLocationInput() {
190
- return this.shadowRoot.querySelector('barcode-scanable-input[name=binLocation]').shadowRoot.querySelector('input')
188
+ get serialInput() {
189
+ return this.shadowRoot.querySelector('barcode-scanable-input[name=serialNumber]')?.shadowRoot.querySelector('input')
191
190
  }
192
191
 
193
192
  get newLocationInput() {
@@ -202,51 +201,123 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
202
201
  return this.shadowRoot.querySelector('input[name=releaseProgress]')
203
202
  }
204
203
 
205
- get scannable() {
206
- if (this.refPickingType === PICKING_TYPES.PICKING.value) {
207
- return this._selectedTaskStatus && this._selectedTaskStatus === WORKSHEET_STATUS.EXECUTING.value
208
- } else {
209
- return (
210
- this._selectedOrderInventory &&
211
- this._selectedOrderInventory.pickedQty != this._selectedOrderInventory.releaseQty
212
- )
213
- }
214
- }
215
-
216
- get completed() {
217
- return this.data.records.every(record => record.completed)
218
- }
219
-
220
204
  _focusOnNewLocationInput() {
221
- setTimeout(() => this.newLocationInput.focus(), 100)
205
+ setTimeout(() => this.newLocationInput.focus(), 50)
222
206
  }
223
207
 
224
208
  _focusOnLocationNameInput() {
225
- setTimeout(() => this.locationInput.focus(), 100)
209
+ setTimeout(() => this.locationInput.focus(), 50)
226
210
  }
227
211
 
228
212
  _focusOnReleaseGoodNoInput() {
229
- setTimeout(() => this.releaseGoodNoInput.focus(), 100)
213
+ setTimeout(() => this.releaseGoodNoInput.focus(), 50)
230
214
  }
231
215
 
232
216
  _focusOnTaskNoInput() {
233
- setTimeout(() => this.taskNoInput.focus(), 100)
217
+ setTimeout(() => this.taskNoInput.focus(), 50)
218
+ }
219
+
220
+ _focusOnBinLocationInput() {
221
+ setTimeout(() => this.binLocationInput.focus(), 50)
234
222
  }
235
223
 
236
224
  _focusOnLotNoInput() {
237
- setTimeout(() => this.palletInput.focus(), 100)
225
+ if (this.palletInput) {
226
+ setTimeout(() => this.palletInput.focus(), 50)
227
+ }
238
228
  }
239
229
 
240
230
  _focusOnCartonNoInput() {
241
- setTimeout(() => this.cartonInput.focus(), 100)
231
+ if (this.cartonInput) {
232
+ setTimeout(() => this.cartonInput.focus(), 50)
233
+ }
242
234
  }
243
235
 
244
236
  _focusOnProductBarcodeInput() {
245
- setTimeout(() => this.productBarcodeInput.focus(), 100)
237
+ if (this._enableProductScanning && this?.productBarcodeInput) {
238
+ setTimeout(() => this.productBarcodeInput.focus(), 50)
239
+ }
240
+ }
241
+
242
+ _focusOnSerialInput() {
243
+ if (this.serialInput) {
244
+ setTimeout(() => this.serialInput.focus(), 50)
245
+ }
246
246
  }
247
247
 
248
248
  _focusOnReleaseQtyInput() {
249
- setTimeout(() => this.releaseQtyInput.focus(), 100)
249
+ if (this.releaseQtyInput) {
250
+ setTimeout(() => this.releaseQtyInput.focus(), 50)
251
+ }
252
+ }
253
+
254
+ get context() {
255
+ var actions = []
256
+ if (this._hasBinPicking) {
257
+ actions.push({
258
+ title: i18next.t('button.get_task'),
259
+ action: this.getTask.bind(this),
260
+ ...CommonButtonStyles.assignment
261
+ })
262
+ }
263
+
264
+ actions.push({
265
+ title: i18next.t('button.my_worksheet_list'),
266
+ action: this.showWorksheetList.bind(this),
267
+ ...CommonButtonStyles.inspect
268
+ })
269
+
270
+ if ((this.releaseGoodNo && this.completed) || (this.taskNo && this.completed)) {
271
+ actions.push({
272
+ title: i18next.t('button.complete'),
273
+ action: this.complete.bind(this),
274
+ ...CommonButtonStyles.submit
275
+ })
276
+ }
277
+
278
+ return {
279
+ title: i18next.t('title.picking'),
280
+ help: 'operato-wms/outbound/picking-product',
281
+ actions
282
+ }
283
+ }
284
+
285
+ get scannable() {
286
+ let result =
287
+ this._selectedOrderInventory && this._selectedOrderInventory.pickedQty != this._selectedOrderInventory.releaseQty
288
+ return result
289
+ }
290
+
291
+ get completed() {
292
+ return this.data.records.every(record => record.completed)
293
+ }
294
+
295
+ constructor() {
296
+ super()
297
+
298
+ this.data = { records: [] }
299
+ this._reusablePalletList = { records: [] }
300
+
301
+ this.taskNo = ''
302
+ this.releaseGoodNo = ''
303
+ this._selectedOrderInventory = null
304
+ this.locationSortingRules = []
305
+ this.locationNameSplit = []
306
+
307
+ this._productName = ''
308
+
309
+ this.refPickingType = PICKING_TYPES.PICKING.value
310
+
311
+ this._crossDocking = false
312
+ this._isReplacement = false
313
+ this._hasBinPicking = false
314
+ this._isCartonLabel = false
315
+ this._enableProductScanning = false
316
+ this._isRequireSerialNumberScanning = false
317
+ this._incompleteLocationName = false
318
+
319
+ /* A flag to indicate either PICKING process is executing */
320
+ this._isExecuting = false
250
321
  }
251
322
 
252
323
  render() {
@@ -256,52 +327,40 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
256
327
  <legend>${i18next.t('title.scan_area')}</legend>
257
328
  <label>${i18next.t('label.picking_type')}</label>
258
329
  <select name="pickingType" @change="${e => this.pickingTypeChangeHandler(e.currentTarget.value)}">
259
- ${Object.keys(PICKING_TYPES).map(key =>
260
- this.refPickingType === PICKING_TYPES[key].value
261
- ? html`
262
- <option value="${PICKING_TYPES[key].value}" selected>
263
- ${i18next.t(`label.${PICKING_TYPES[key].name}`)}
264
- </option>
265
- `
266
- : html`
267
- <option value="${PICKING_TYPES[key].value}">
268
- ${i18next.t(`label.${PICKING_TYPES[key].name}`)}
269
- </option>
270
- `
330
+ ${Object.keys(PICKING_TYPES).map(
331
+ key =>
332
+ html`
333
+ <option
334
+ value="${PICKING_TYPES[key].value}"
335
+ ?selected=${this.refPickingType === PICKING_TYPES[key].value}
336
+ >
337
+ ${i18next.t(`label.${PICKING_TYPES[key].name}`)}
338
+ </option>
339
+ `
271
340
  )}
272
341
  </select>
273
342
 
274
- ${this.refPickingType === PICKING_TYPES.PICKING.value
275
- ? html`
276
- <label>${i18next.t('label.release_good_no')}</label>
277
- <barcode-scanable-input
278
- name="releaseGoodNo"
279
- custom-input
280
- @keypress="${async e => {
281
- if (e.keyCode === 13) {
282
- e.preventDefault()
283
- if (this.releaseGoodNoInput.value) {
284
- this._fetchInventories(this.releaseGoodNoInput.value)
285
- this._fetchPalletsHandler(this.releaseGoodNoInput.value)
286
- }
287
- }
288
- }}"
289
- ></barcode-scanable-input>
290
- `
291
- : html` <label>${i18next.t('label.task_no')}</label>
292
- <barcode-scanable-input
293
- name="taskNo"
294
- custom-input
295
- @keypress="${async e => {
296
- if (e.keyCode === 13) {
297
- e.preventDefault()
298
- if (this.taskNoInput.value) {
299
- this._fetchInventories(this.taskNoInput.value)
300
- this._fetchPalletsHandler(this.taskNoInput.value)
301
- }
302
- }
303
- }}"
304
- ></barcode-scanable-input>`}
343
+ <label>
344
+ ${this.refPickingType === PICKING_TYPES.PICKING.value
345
+ ? i18next.t('label.release_good_no')
346
+ : i18next.t('label.task_no')}
347
+ </label>
348
+ <barcode-scanable-input
349
+ name=${this.refPickingType === PICKING_TYPES.PICKING.value ? 'releaseGoodNo' : 'taskNo'}
350
+ custom-input
351
+ @keypress="${async e => {
352
+ if (e.keyCode === 13) {
353
+ e.preventDefault()
354
+ this._currentBinLocation = null
355
+ if (this.releaseGoodNoInput.value) {
356
+ this.releaseGoodNo = this.releaseGoodNoInput.value
357
+ await this.fetchAll(this.releaseGoodNo)
358
+ this.completeHandler()
359
+ }
360
+ }
361
+ }}"
362
+ >
363
+ </barcode-scanable-input>
305
364
  </fieldset>
306
365
 
307
366
  <fieldset>
@@ -313,7 +372,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
313
372
 
314
373
  <label>${i18next.t('label.ref_no_2')}</label>
315
374
  <input name="refNo2" readonly />`
316
- : html`<legend>${`${i18next.t('title.task_no')}: ${this.taskNo}`}</legend>`}
375
+ : html` <legend>${`${i18next.t('title.task_no')}: ${this.taskNo}`}</legend>`}
317
376
 
318
377
  <label>${i18next.t('label.company')}</label>
319
378
  <input name="bizplaceName" readonly />
@@ -321,9 +380,9 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
321
380
  <label>${i18next.t('label.started_at')}</label>
322
381
  <input name="startedAt" type="datetime-local" readonly />
323
382
 
324
- ${this.crossDocking
383
+ ${this._crossDocking
325
384
  ? html`
326
- <input name="crossDocking" type="checkbox" .checked="${this.crossDocking}" disabled />
385
+ <input name="_crossDocking" type="checkbox" .checked="${this._crossDocking}" disabled />
327
386
  <label>${i18next.t('label.cross_docking')}</label>
328
387
  `
329
388
  : ''}
@@ -341,7 +400,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
341
400
  </div>
342
401
 
343
402
  <div class="right-column">
344
- <form id="input-form" class="single-column-form" @keypress="${this._picking.bind(this)}">
403
+ <form id="input-form" class="single-column-form" @keypress="${this.picking.bind(this)}">
345
404
  <fieldset>
346
405
  <legend>${i18next.t('label.product')}: ${this._productName}</legend>
347
406
 
@@ -368,11 +427,11 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
368
427
  <input name="releaseProgress" readonly />
369
428
  </fieldset>
370
429
 
371
- ${this.releaseGoodNo
430
+ ${this.releaseGoodNo || ''
372
431
  ? html`
373
432
  <fieldset>
374
433
  <legend>${i18next.t('title.reusable_pallet')}</legend>
375
- <div class="reusable_pallet" @click="${this._openPalletOutbound.bind(this)}">
434
+ <div class="full_width" @click="${this.openPalletOutbound.bind(this)}">
376
435
  <mwc-icon>apps</mwc-icon>Plastic Pallets
377
436
  </div>
378
437
  </fieldset>
@@ -382,6 +441,21 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
382
441
  ? html`
383
442
  <fieldset>
384
443
  <legend>${i18next.t('title.input_section')}</legend>
444
+ ${this._hasBinPicking
445
+ ? html`
446
+ <label>${i18next.t('label.bin_location')}</label>
447
+ <barcode-scanable-input
448
+ name="binLocation"
449
+ custom-input
450
+ @keypress="${e => {
451
+ if (e.keyCode === 13) {
452
+ e.preventDefault()
453
+ this._currentBinLocation = this.binLocationInput.value
454
+ }
455
+ }}"
456
+ ></barcode-scanable-input>
457
+ `
458
+ : ''}
385
459
  ${this._isCartonLabel
386
460
  ? html`
387
461
  <label>${i18next.t('label.carton_id')}</label>
@@ -397,26 +471,28 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
397
471
  <barcode-scanable-input name="productBarcode" custom-input></barcode-scanable-input>
398
472
  `
399
473
  : html`
400
- <label>${i18next.t('label.picked_qty')}</label>
401
- <input type="number" min="1" name="confirmedQty" />
474
+ ${this._isRequireSerialNumberScanning
475
+ ? ''
476
+ : html`
477
+ <label>${i18next.t('label.picked_qty')}</label>
478
+ <input type="number" min="1" name="confirmedQty" />
479
+ `}
402
480
  `}
403
- ${this._hasBinPicking
404
- ? html`
405
- <label>${i18next.t('label.bin_location')}</label>
406
- <barcode-scanable-input name="binLocation" custom-input></barcode-scanable-input>
407
- `
408
- : ''}
409
- ${this.isWholePicking || this.crossDocking || this._isCartonLabel || this._enableProductScanning
481
+ ${this.isWholePicking ||
482
+ this._crossDocking ||
483
+ this._isCartonLabel ||
484
+ this._enableProductScanning ||
485
+ this._isRequireSerialNumberScanning
410
486
  ? ''
411
487
  : html`
412
- ${this.incompleteLocationName
488
+ ${this._incompleteLocationName
413
489
  ? ''
414
490
  : html`
415
491
  <label>${i18next.t('label.return_location')}</label>
416
492
  <barcode-scanable-input name="locationName" custom-input></barcode-scanable-input>
417
493
  `}
418
494
  <select
419
- ?hidden="${!this.incompleteLocationName}"
495
+ ?hidden="${!this._incompleteLocationName}"
420
496
  name="newLocationCode"
421
497
  @change="${e => (this.locationInput.value = e.currentTarget.value)}"
422
498
  >
@@ -432,44 +508,32 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
432
508
  )}
433
509
  </select>
434
510
  `}
511
+ ${this._isRequireSerialNumberScanning
512
+ ? html`
513
+ <label>${i18next.t('label.serial_number')}</label>
514
+ <barcode-scanable-input name="serialNumber" custom-input></barcode-scanable-input>
515
+ `
516
+ : ``}
435
517
  </fieldset>
436
518
  `
437
519
  : ''}
520
+ ${this._selectedOrderInventory && this._isRequireSerialNumberScanning
521
+ ? html`
522
+ <fieldset>
523
+ <fieldset>
524
+ <div class="full_width" @click="${this.openSerialNumberPopup.bind(this)}">
525
+ <mwc-icon>list</mwc-icon>${i18next.t('label.serial_number_list')}
526
+ </div>
527
+ </fieldset>
528
+ </fieldset>
529
+ `
530
+ : ``}
438
531
  </form>
439
532
  </div>
440
533
  </div>
441
534
  `
442
535
  }
443
536
 
444
- constructor() {
445
- super()
446
- this.data = { records: [] }
447
- this._productName = ''
448
- this.releaseGoodNo = ''
449
- this.taskNo = ''
450
- this._selectedOrderInventory = null
451
- this._selectedTaskStatus = null
452
- this._reusablePalletList = { records: [] }
453
- this.locationSortingRules = []
454
- this.refPickingType = PICKING_TYPES.PICKING.value
455
- this.crossDocking = false
456
- this.isReplacement = false
457
- this._hasBinPicking = false
458
- this._isCartonLabel = false
459
- this._enableProductScanning = false
460
- this.incompleteLocationName = false
461
- this.locationNameSplit = []
462
-
463
- /* A flag to indicate either PICKING process is executing */
464
- this._isExecuting = false
465
- }
466
-
467
- updated(changedProps) {
468
- if (changedProps.has('_selectedTaskStatus') && this._selectedTaskStatus) {
469
- this.updateContext()
470
- }
471
- }
472
-
473
537
  pageUpdated(changes, lifecycle) {
474
538
  if (this.active) {
475
539
  this.orderNo = lifecycle.params?.orderNo || null
@@ -479,35 +543,55 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
479
543
  }
480
544
 
481
545
  async pageInitialized() {
546
+ const [enableBinPickingTask, locationSortingTask] = [
547
+ fetchSettingRule('enable-bin-picking'),
548
+ fetchLocationSortingRule(LOCATION_SORTING_RULE.PICKING_PRODUCT.value)
549
+ ]
550
+
482
551
  this.config = {
483
552
  rows: {
484
553
  appendable: false,
554
+ classifier: (record, rowIndex) => {
555
+ return {
556
+ emphasized: record.completed == true
557
+ }
558
+ },
485
559
  handlers: {
486
560
  click: async (columns, data, column, record, rowIndex) => {
487
- if (data.records.length && record) {
488
- this._isExecuting = false
489
- this._selectedOrderInventory = record
490
- if (this.refPickingType === PICKING_TYPES.PICKING.value) {
491
- this._selectedTaskStatus = record.status
492
- }
493
- this._isCartonLabel = record.hasCartonLabel
494
- this._productName = record.product?.name
495
- ? `${record.product.sku + ' - ' + record.product.name} ${
496
- record.product?.description ? `(${record.product.description})` : ''
497
- }`
498
- : ''
499
- this.isWholePicking = this._selectedOrderInventory.releaseQty === this._selectedOrderInventory.qty
500
- this.isReplacement = false
501
-
502
- this._fillUpForm(this.inputForm, record)
503
- await this.updateComplete
504
- if (this.scannable) {
505
- if (this._enableProductScanning) this.productBarcodeInput.value = ''
506
- if (this._isCartonLabel) {
507
- this.cartonInput.value = ''
508
- this._focusOnCartonNoInput()
509
- } else this._focusOnLotNoInput()
561
+ try {
562
+ this.grist.showSpinner()
563
+ if (data.records.length && record) {
564
+ this.inputForm.reset()
565
+ this._isReplacement = false
566
+
567
+ if (this.binLocationInput) this.binLocationInput.value = ''
568
+ if (this.productBarcodeInput) this.productBarcodeInput.value = ''
569
+ if (this.cartonInput) this.cartonInput.value = ''
570
+ if (this.palletInput) this.palletInput.value = ''
571
+ if (this.serialInput) this.serialInput.value = ''
572
+
573
+ this._selectedOrderInventory = record
574
+ await this.fetchInventories(this.releaseGoodNo)
575
+ let updatedRecords = this._selectedOrderInventory
576
+ if (updatedRecords.releaseQty === updatedRecords.pickedQty) {
577
+ updatedRecords.status = 'DONE'
578
+ } else {
579
+ updatedRecords.status = 'PICKING'
580
+ }
581
+
582
+ // this._isCartonLabel = updatedRecords.hasCartonLabel
583
+ // this._productName = updatedRecords.product?.name
584
+ // ? `${updatedRecords.product.sku + ' - ' + updatedRecords.product.name} ${
585
+ // updatedRecords.product?.description ? `(${updatedRecords.product.description})` : ''
586
+ // }`
587
+ // : ''
588
+ // this._isRequireSerialNumberScanning = updatedRecords?.product?.isRequireSerialNumberScanning
589
+ // this.isWholePicking = updatedRecords.releaseQty === updatedRecords.qty
510
590
  }
591
+ } catch (ex) {
592
+ console.log(ex)
593
+ } finally {
594
+ this.grist.hideSpinner()
511
595
  }
512
596
  }
513
597
  }
@@ -563,16 +647,18 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
563
647
  ]
564
648
  }
565
649
 
566
- this.locationSortingRules = await fetchLocationSortingRule(LOCATION_SORTING_RULE.PICKING_PRODUCT.value)
650
+ let data = [await locationSortingTask, await enableBinPickingTask]
651
+
652
+ this.locationSortingRules = await locationSortingTask
653
+ this._hasBinPicking = await enableBinPickingTask
567
654
  }
568
655
 
569
656
  autoFetchPickingTasks(orderNo) {
570
657
  this.releaseGoodNoInput.value = orderNo
571
- this._fetchInventories(this.releaseGoodNoInput.value)
572
- this._fetchPalletsHandler(this.releaseGoodNoInput.value)
658
+ this.fetchAll(this.releaseGoodNoInput.value)
573
659
  }
574
660
 
575
- async _fetchInventories(pickingRefNo) {
661
+ async fetchInventories(pickingRefNo) {
576
662
  if (!pickingRefNo) return
577
663
 
578
664
  let response
@@ -583,6 +669,9 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
583
669
  query pickingWorksheet($releaseGoodNo: String!, $locationSortingRules: [Sorting]) {
584
670
  pickingWorksheet(releaseGoodNo: $releaseGoodNo, locationSortingRules: $locationSortingRules) {
585
671
  worksheetInfo {
672
+ releaseGood {
673
+ name
674
+ }
586
675
  bizplaceName
587
676
  partnerDomainId
588
677
  refNo
@@ -602,6 +691,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
602
691
  sku
603
692
  name
604
693
  description
694
+ isRequireSerialNumberScanning
605
695
  }
606
696
  qty
607
697
  releaseQty
@@ -616,14 +706,24 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
616
706
  name
617
707
  description
618
708
  }
709
+ binLocation {
710
+ name
711
+ description
712
+ }
619
713
  relatedOrderInv {
714
+ id
620
715
  batchId
621
716
  batchIdRef
622
717
  packingType
718
+ binLocation {
719
+ id
720
+ name
721
+ }
623
722
  product {
624
723
  sku
625
724
  name
626
725
  description
726
+ isRequireSerialNumberScanning
627
727
  }
628
728
  }
629
729
  }
@@ -634,34 +734,42 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
634
734
  })
635
735
 
636
736
  if (!response.errors) {
637
- if (!this._selectedOrderInventory) this._clearView()
638
- this.releaseGoodNo = pickingRefNo
737
+ if (!this._selectedOrderInventory) this.clearView()
738
+ this.releaseGoodNo = response.data.pickingWorksheet.worksheetInfo.releaseGood.name
639
739
  if (!response.data?.pickingWorksheet) return
640
740
 
641
741
  const { worksheetInfo, worksheetDetailInfos } = response.data.pickingWorksheet
642
- this.crossDocking = worksheetInfo.releaseGood?.crossDocking
643
- this._fillUpForm(this.infoForm, { ...worksheetInfo, crossDocking: this.crossDocking })
742
+ this._crossDocking = worksheetInfo.releaseGood?._crossDocking
743
+ this.fillUpForm(this.infoForm, { ...worksheetInfo, _crossDocking: this._crossDocking })
644
744
  this.data = { records: this.formatWorksheetDetailInfos(worksheetDetailInfos) }
745
+
645
746
  if (this._selectedOrderInventory) {
646
747
  const record = this.data.records.find(record => record.name === this._selectedOrderInventory.name)
748
+ this.updateSelectedItemProp(record)
749
+ await this.updateComplete
750
+ this.fillUpForm(this.inputForm, {
751
+ ...record,
752
+ binLocation: record?.binLocation ? record.binLocation : ''
753
+ })
754
+
647
755
  if (record.pickedQty == record.releaseQty) {
648
- if (this._isCartonLabel && this.scannable) {
649
- this.cartonInput.value = ''
650
- this._focusOnCartonNoInput()
651
- }
652
- this._selectedOrderInventory = null
653
- this._selectedTaskStatus = null
654
- this.inputForm.reset()
756
+ if (this.cartonInput) this.cartonInput.value = ''
757
+ if (this.palletInput) this.palletInput.value = ''
758
+ // this.isWholePicking = true
655
759
  } else {
656
- if (this._enableProductScanning) this._focusOnProductBarcodeInput()
657
- this._fillUpForm(this.inputForm, record)
760
+ // pre-set bin location from last scan if none was set
761
+ if (
762
+ this._hasBinPicking &&
763
+ this.binLocationInput &&
764
+ (!this._currentBinLocation || this._currentBinLocation == '')
765
+ ) {
766
+ this._currentBinLocation = record?.binLocation ? record.binLocation : ''
767
+ }
768
+ this.focusOnInput()
658
769
  }
659
770
  }
660
- this._completeHandler()
661
771
 
662
772
  this._enableProductScanning = await fetchSettingRule('enable-product-scanning', worksheetInfo.partnerDomainId)
663
-
664
- this._hasBinPicking = await fetchSettingRule('enable-bin-picking', worksheetInfo.partnerDomainId)
665
773
  }
666
774
  break
667
775
 
@@ -684,6 +792,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
684
792
  name
685
793
  description
686
794
  sku
795
+ isRequireSerialNumberScanning
687
796
  }
688
797
  qty
689
798
  releaseQty
@@ -716,49 +825,54 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
716
825
  })
717
826
 
718
827
  if (!response.errors) {
719
- if (!this._selectedOrderInventory) this._clearView()
828
+ if (!this._selectedOrderInventory) this.clearView()
720
829
  this.taskNo = pickingRefNo
721
830
  if (!response.data?.batchPickingWorksheet) return
722
831
 
723
832
  const { worksheetInfo, worksheetDetailInfos } = response.data.batchPickingWorksheet
724
- this._fillUpForm(this.infoForm, { ...worksheetInfo })
833
+ await this.updateComplete
834
+ this.fillUpForm(this.infoForm, { ...worksheetInfo })
725
835
  this.data = { records: this.formatWorksheetDetailInfos(worksheetDetailInfos) }
836
+
837
+ this._enableProductScanning = await fetchSettingRule('enable-product-scanning', worksheetInfo.partnerDomainId)
838
+
726
839
  if (this._selectedOrderInventory) {
727
840
  const record = this.data.records.find(
728
841
  record => record.product.sku === this._selectedOrderInventory.product.sku
729
842
  )
843
+
844
+ this.updateSelectedItemProp(record)
845
+
730
846
  if (record.pickedQty == record.releaseQty) {
731
847
  if (this._isCartonLabel) {
732
848
  this.cartonInput.value = ''
733
849
  this._focusOnCartonNoInput()
734
850
  }
735
- this._selectedOrderInventory = null
736
851
  this.inputForm.reset()
737
852
  } else {
738
- if (this._enableProductScanning) this._focusOnProductBarcodeInput()
739
- this._fillUpForm(this.inputForm, record)
853
+ this._focusOnProductBarcodeInput()
854
+ this.fillUpForm(this.inputForm, record)
740
855
  }
741
856
  }
742
- this._completeHandler()
743
-
744
- this._enableProductScanning = await fetchSettingRule('enable-product-scanning', worksheetInfo.partnerDomainId)
745
-
746
- this._hasBinPicking = await fetchSettingRule('enable-bin-picking', worksheetInfo.partnerDomainId)
747
857
  }
748
858
  break
749
859
  }
860
+
861
+ this.updateContext()
750
862
  }
751
863
 
752
864
  formatWorksheetDetailInfos(wsdInfos) {
753
865
  return wsdInfos
754
866
  .map(record => {
755
- record.completed = Boolean(record?.status)
756
- ? record.status === WORKSHEET_STATUS.DONE.value
757
- : record.pickedQty == record.releaseQty
867
+ if (record.pickedQty == record.releaseQty) {
868
+ record.completed = true
869
+ } else {
870
+ record.completed = false
871
+ }
758
872
  record.locationName = record?.location?.name
759
873
  record.product = record?.relatedOrderInv && record?.relatedOrderInv?.product
760
874
 
761
- if (this.crossDocking) {
875
+ if (this._crossDocking) {
762
876
  record.batchId = record.relatedOrderInv.batchId
763
877
  record.packingType = record.relatedOrderInv.packingType
764
878
  }
@@ -779,7 +893,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
779
893
  .sort((a, b) => a.completed - b.completed)
780
894
  }
781
895
 
782
- async _fetchPalletsHandler(releaseGoodNo) {
896
+ async fetchPalletsHandler(releaseGoodNo) {
783
897
  const response = await client.query({
784
898
  query: gql`
785
899
  query pallets($filters: [Filter], $pagination: Pagination) {
@@ -813,21 +927,23 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
813
927
  }
814
928
  }
815
929
 
816
- _clearView() {
930
+ clearView() {
817
931
  this.data = { records: [] }
818
932
  this.infoForm.reset()
819
933
  this.inputForm.reset()
820
934
  this._productName = ''
821
- this.releaseGoodNo = ''
822
- this.taskNo = ''
823
- this.crossDocking = false
935
+ this._isRequireSerialNumberScanning = false
936
+ this.releaseGoodNo = null
937
+ this.taskNo = null
938
+ this._crossDocking = false
824
939
  this._selectedOrderInventory = null
825
- this._selectedTaskStatus = null
826
- this.incompleteLocationName = false
940
+ // this._selectedTaskStatus = null
941
+ this._incompleteLocationName = false
942
+ this._isExecuting = false
827
943
  }
828
944
 
829
- _fillUpForm(form, data) {
830
- this.incompleteLocationName = false
945
+ fillUpForm(form, data) {
946
+ this._incompleteLocationName = false
831
947
  form.reset()
832
948
  for (let key in data) {
833
949
  Array.from(form.querySelectorAll('input')).forEach(field => {
@@ -849,46 +965,51 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
849
965
  }
850
966
  }
851
967
 
852
- async _picking(e) {
968
+ async picking(e) {
853
969
  if (e.keyCode === 13) {
854
970
  try {
855
- await this._validatePicking()
971
+ await this.validatePicking()
856
972
 
857
- let response
973
+ let response = {}
858
974
  if (this.crossDocking) {
859
975
  response = await this.pickCrossDocking()
860
976
  } else if (this._enableProductScanning || this._isCartonLabel) {
861
977
  response = await this.productBarcodePicking()
862
978
  } else {
863
- if (this.isReplacement) {
979
+ if (this._isReplacement) {
864
980
  this.openPalletReplacementPopup()
981
+ return
865
982
  } else {
866
983
  response = await this.pickCommonPicking()
867
984
  }
868
985
  }
869
986
 
870
987
  if (!response.errors) {
871
- this._isExecuting = false
872
- if (this._enableProductScanning) this.productBarcodeInput.value = ''
988
+ if (this._isRequireSerialNumberScanning) this.serialInput.value = ''
989
+ else if (this._enableProductScanning && !this._isRequireSerialNumberScanning)
990
+ this.productBarcodeInput.value = ''
873
991
  else this.releaseQtyInput.value = ''
874
992
 
875
- if (!this._isCartonLabel) {
993
+ if (!this._isCartonLabel && !this._isRequireSerialNumberScanning) {
876
994
  this.palletInput.value = ''
877
- this._selectedTaskStatus = null
995
+ // this._selectedTaskStatus = null
878
996
  this._selectedOrderInventory = null
879
997
  }
880
998
 
881
- if (!this.isWholePicking && !this.crossDocking && !this._isCartonLabel) this.locationInput.value = ''
999
+ if (!this.isWholePicking && !this._crossDocking && !this._isCartonLabel && this.locationInput)
1000
+ this.locationInput.value = ''
882
1001
  if (this.refPickingType === PICKING_TYPES.PICKING.value) {
883
- this._fetchInventories(this.releaseGoodNo)
1002
+ this.fetchInventories(this.releaseGoodNo)
884
1003
  } else {
885
- this._fetchInventories(this.taskNo)
1004
+ this.fetchInventories(this.taskNo)
886
1005
  }
887
1006
  }
888
1007
 
889
1008
  this.isReplacement = false
890
1009
  } catch (e) {
891
1010
  this._showToast(e)
1011
+ } finally {
1012
+ this._isExecuting = false
892
1013
  }
893
1014
  }
894
1015
  }
@@ -902,15 +1023,16 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
902
1023
  .replacePalletId="${this.palletInput.value}"
903
1024
  @completed="${() => {
904
1025
  if (this.refPickingType === PICKING_TYPES.PICKING.value) {
905
- this._fetchInventories(this.releaseGoodNoInput.value)
906
- this._fetchPalletsHandler(this.releaseGoodNoInput.value)
1026
+ this.fetchInventories(this.releaseGoodNoInput.value)
1027
+ this.fetchPalletsHandler(this.releaseGoodNoInput.value)
907
1028
  } else {
908
- this._fetchInventories(this.taskNoInput.value)
909
- this._fetchPalletsHandler(this.taskNoInput.value)
1029
+ this.fetchInventories(this.taskNoInput.value)
1030
+ this.fetchPalletsHandler(this.taskNoInput.value)
910
1031
  }
911
- this._selectedTaskStatus = null
1032
+ // this._selectedTaskStatus = null
912
1033
  this._selectedOrderInventory = null
913
1034
  this.isReplacement = false
1035
+ this._isExecuting = false
914
1036
  }}"
915
1037
  ></picking-replacement-popup>
916
1038
  `,
@@ -922,8 +1044,34 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
922
1044
  )
923
1045
  }
924
1046
 
925
- async pickCrossDocking() {
926
- this._isExecuting = true
1047
+ openSerialNumberPopup() {
1048
+ openPopup(
1049
+ html`
1050
+ <serial-number-popup
1051
+ .orderInventory="${this?._selectedOrderInventory?.relatedOrderInv}"
1052
+ @completed="${() => {
1053
+ // if (this.refPickingType === PICKING_TYPES.PICKING.value) {
1054
+ // this.fetchInventories(this.releaseGoodNoInput.value)
1055
+ // this.fetchPalletsHandler(this.releaseGoodNoInput.value)
1056
+ // } else {
1057
+ // this.fetchInventories(this.taskNoInput.value)
1058
+ // this.fetchPalletsHandler(this.taskNoInput.value)
1059
+ // }
1060
+ // // this._selectedTaskStatus = null
1061
+ // this._selectedOrderInventory = null
1062
+ // this._isReplacement = false
1063
+ }}"
1064
+ ></serial-number-popup>
1065
+ `,
1066
+ {
1067
+ backdrop: true,
1068
+ size: 'large',
1069
+ title: i18next.t('title.serial_number_list')
1070
+ }
1071
+ )
1072
+ }
1073
+
1074
+ async pick_crossDocking() {
927
1075
  return await client.mutate({
928
1076
  mutation: gql`
929
1077
  mutation crossDockPicking(
@@ -950,7 +1098,8 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
950
1098
  }
951
1099
 
952
1100
  async pickCommonPicking() {
953
- const locationName = this.isWholePicking ? this._selectedOrderInventory.location.name : this.locationInput.value
1101
+ const locationName =
1102
+ this.isWholePicking || !this.locationInput ? this._selectedOrderInventory.location.name : this.locationInput.value
954
1103
  // 1. Check whether location is changed
955
1104
  if (this._selectedOrderInventory.location.name !== locationName) {
956
1105
  this.locationNameSplit = this.locationInput.value.split('-')
@@ -959,27 +1108,26 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
959
1108
  const columnPortions = this.locationNameSplit[2].match(/[a-zA-Z0-9]+/g)
960
1109
 
961
1110
  if (this.locationNameSplit.length === 3) {
962
- this.incompleteLocationName = true
963
- this.locations = await this._fetchLocations(zonePortions[0], rowPortions[0], columnPortions[0])
1111
+ this._incompleteLocationName = true
1112
+ this.locations = await this.fetchLocations(zonePortions[0], rowPortions[0], columnPortions[0])
964
1113
  this._focusOnNewLocationInput()
965
1114
  throw new Error(i18next.t('text.please_select_the_location_again'))
966
1115
  } else if (this.locationNameSplit.length === 4) {
967
- const result = await CustomAlert({
1116
+ let relocateConfirmation = await CustomAlert({
968
1117
  title: i18next.t('title.relocate'),
969
1118
  text: i18next.t('text.are_you_sure'),
970
1119
  confirmButton: { text: i18next.t('button.relocate') },
971
1120
  cancelButton: { text: i18next.t('button.cancel') }
972
1121
  })
973
1122
 
974
- if (!result.value) {
975
- this.incompleteLocationName = false
1123
+ if (!relocateConfirmation.isConfirmed) {
1124
+ this._incompleteLocationName = false
976
1125
  this.locationNameSplit = []
977
- return
1126
+ return false
978
1127
  }
979
1128
  }
980
1129
  }
981
1130
 
982
- this._isExecuting = true
983
1131
  switch (this.refPickingType) {
984
1132
  case PICKING_TYPES.PICKING.value:
985
1133
  return await client.mutate({
@@ -990,6 +1138,8 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
990
1138
  $palletId: String
991
1139
  $locationName: String!
992
1140
  $releaseQty: Int!
1141
+ $binLocation: String
1142
+ $serialNumber: String
993
1143
  ) {
994
1144
  picking(
995
1145
  worksheetDetailName: $worksheetDetailName
@@ -997,6 +1147,8 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
997
1147
  palletId: $palletId
998
1148
  locationName: $locationName
999
1149
  releaseQty: $releaseQty
1150
+ binLocation: $binLocation
1151
+ serialNumber: $serialNumber
1000
1152
  )
1001
1153
  }
1002
1154
  `,
@@ -1005,7 +1157,9 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1005
1157
  worksheetType: this.refPickingType,
1006
1158
  palletId: this.palletInput.value.trim(),
1007
1159
  locationName,
1008
- releaseQty: parseInt(this.releaseQtyInput.value)
1160
+ releaseQty: this._isRequireSerialNumberScanning ? 1 : parseInt(this.releaseQtyInput.value),
1161
+ binLocation: this._hasBinPicking ? this.binLocationInput.value : null,
1162
+ serialNumber: this._isRequireSerialNumberScanning ? this.serialInput.value : null
1009
1163
  }
1010
1164
  })
1011
1165
 
@@ -1043,7 +1197,6 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1043
1197
  }
1044
1198
 
1045
1199
  async productBarcodePicking() {
1046
- this._isExecuting = true
1047
1200
  switch (this.refPickingType) {
1048
1201
  case PICKING_TYPES.PICKING.value:
1049
1202
  return await client.mutate({
@@ -1054,6 +1207,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1054
1207
  $productBarcode: String!
1055
1208
  $cartonId: String!
1056
1209
  $binLocation: String
1210
+ $serialNumber: String
1057
1211
  ) {
1058
1212
  scanProductPicking(
1059
1213
  worksheetDetailName: $worksheetDetailName
@@ -1061,6 +1215,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1061
1215
  productBarcode: $productBarcode
1062
1216
  cartonId: $cartonId
1063
1217
  binLocation: $binLocation
1218
+ serialNumber: $serialNumber
1064
1219
  )
1065
1220
  }
1066
1221
  `,
@@ -1069,6 +1224,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1069
1224
  worksheetType: this.refPickingType,
1070
1225
  productBarcode: this.productBarcodeInput.value.trim(),
1071
1226
  cartonId: this.cartonInput.value.trim(),
1227
+ serialNumber: this._isRequireSerialNumberScanning ? this.serialInput.value : null,
1072
1228
  binLocation: this._hasBinPicking ? this.binLocationInput.value : null
1073
1229
  }
1074
1230
  })
@@ -1111,10 +1267,12 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1111
1267
  }
1112
1268
  }
1113
1269
 
1114
- async _validatePicking() {
1270
+ async validatePicking() {
1115
1271
  // validate for previous picking execution (multiple picking scanning)
1116
1272
  if (this._isExecuting) throw new Error(i18next.t('text.previous_picking_is_still_executing_please_wait'))
1117
1273
 
1274
+ this._isExecuting = true
1275
+
1118
1276
  // validate for order selection
1119
1277
  if (!this._selectedOrderInventory) throw new Error(i18next.t('text.target_is_not_selected'))
1120
1278
 
@@ -1128,39 +1286,41 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1128
1286
  const isInProgressing = await this.checkProgressingPallet(this.palletInput.value)
1129
1287
  if (isInProgressing) throw new Error(i18next.t('text.pallet_is_in_progressing'))
1130
1288
 
1131
- this.isReplacement = await this.checkProductIdenticality(
1289
+ this._isReplacement = await this.checkProductIdenticality(
1132
1290
  this._selectedOrderInventory.palletId,
1133
1291
  this.palletInput.value
1134
1292
  )
1135
1293
 
1136
- if (!this.isReplacement) {
1294
+ if (!this._isReplacement) {
1137
1295
  this._focusOnLotNoInput()
1138
1296
  throw new Error(i18next.t('text.wrong_lot_id'))
1139
- } else {
1140
- this.openPalletReplacementPopup()
1141
1297
  }
1142
1298
  }
1143
1299
  }
1144
1300
 
1145
1301
  // Release qty existing
1146
- if (!this.isReplacement) {
1147
- if (!parseInt(this.releaseQtyInput?.value)) {
1148
- this._focusOnReleaseQtyInput()
1149
- throw new Error(i18next.t('text.release_qty_is_empty'))
1150
- }
1302
+ if (!this._isReplacement) {
1303
+ if (!this._isRequireSerialNumberScanning) {
1304
+ if (!parseInt(this.releaseQtyInput?.value)) {
1305
+ this._focusOnReleaseQtyInput()
1306
+ throw new Error(i18next.t('text.release_qty_is_empty'))
1307
+ }
1151
1308
 
1152
- if (!this.releaseQtyInput.checkValidity()) throw new Error(i18next.t('text.release_qty_invalid'))
1309
+ if (!this.releaseQtyInput.checkValidity()) throw new Error(i18next.t('text.release_qty_invalid'))
1153
1310
 
1154
- // typed qty should be matched with release qty.
1155
- if (parseInt(this.releaseQtyInput.value) !== this._selectedOrderInventory.releaseQty) {
1156
- this._focusOnReleaseQtyInput()
1157
- throw new Error(i18next.t('text.wrong_release_qty'))
1311
+ // typed qty should be matched with release qty.
1312
+ if (parseInt(this.releaseQtyInput.value) !== this._selectedOrderInventory.releaseQty) {
1313
+ this._focusOnReleaseQtyInput()
1314
+ throw new Error(i18next.t('text.wrong_release_qty'))
1315
+ }
1158
1316
  }
1159
1317
 
1160
1318
  // location id existing
1161
- if (!this.isWholePicking && !this.locationInput.value) {
1162
- this._focusOnLocationNameInput()
1163
- throw new Error(i18next.t('text.location_id_is_empty'))
1319
+ if (!this.isWholePicking && this.locationInput) {
1320
+ if (!this.locationInput.value) {
1321
+ this._focusOnLocationNameInput()
1322
+ throw new Error(i18next.t('text.location_id_is_empty'))
1323
+ }
1164
1324
  }
1165
1325
  }
1166
1326
  } else {
@@ -1171,13 +1331,13 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1171
1331
 
1172
1332
  if (this._selectedOrderInventory.cartonId) {
1173
1333
  if (this._selectedOrderInventory.cartonId !== this.cartonInput.value) {
1174
- this.isReplacement = await this.checkCartonIdenticality(
1334
+ this._isReplacement = await this.checkCartonIdenticality(
1175
1335
  this._selectedOrderInventory.cartonId,
1176
1336
  this._selectedOrderInventory.palletId,
1177
1337
  this.cartonInput.value
1178
1338
  )
1179
1339
 
1180
- if (!this.isReplacement) {
1340
+ if (!this._isReplacement) {
1181
1341
  this.cartonInput.value = ''
1182
1342
  this._focusOnCartonNoInput()
1183
1343
  throw new Error(i18next.t('text.wrong_carton_id'))
@@ -1193,7 +1353,14 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1193
1353
  }
1194
1354
  }
1195
1355
 
1196
- if (this.crossDocking) {
1356
+ if (this._isRequireSerialNumberScanning) {
1357
+ if (!this.serialInput?.value) {
1358
+ this._focusOnSerialInput()
1359
+ throw new Error(i18next.t('text.empty_serial_number'))
1360
+ }
1361
+ }
1362
+
1363
+ if (this._crossDocking) {
1197
1364
  // typed qty should be smaller than release qty.
1198
1365
  if (parseInt(this.releaseQtyInput.value) > this._selectedOrderInventory.releaseQty) {
1199
1366
  this._focusOnReleaseQtyInput()
@@ -1247,7 +1414,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1247
1414
  }
1248
1415
  }
1249
1416
 
1250
- async _fetchLocations(zone, row, column) {
1417
+ async fetchLocations(zone, row, column) {
1251
1418
  const sortings = [{ name: 'name', desc: false }]
1252
1419
  const filters = [
1253
1420
  { name: 'zone', operator: 'eq', value: zone },
@@ -1281,7 +1448,7 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1281
1448
  }
1282
1449
  }
1283
1450
 
1284
- async _completeHandler() {
1451
+ async completeHandler() {
1285
1452
  if (!this.data?.records?.length || this.data.records.some(record => !record.completed)) return
1286
1453
 
1287
1454
  this.updateContext()
@@ -1292,14 +1459,14 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1292
1459
  cancelButton: { text: i18next.t('button.cancel') }
1293
1460
  })
1294
1461
 
1295
- if (!result.value) {
1462
+ if (!result?.value) {
1296
1463
  return
1297
1464
  }
1298
1465
 
1299
- this._complete()
1466
+ this.complete()
1300
1467
  }
1301
1468
 
1302
- async _complete() {
1469
+ async complete() {
1303
1470
  let response
1304
1471
  switch (this.refPickingType) {
1305
1472
  case PICKING_TYPES.PICKING.value:
@@ -1326,27 +1493,29 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1326
1493
  }
1327
1494
 
1328
1495
  if (!response.errors) {
1329
- this._clearView()
1496
+ this.clearView()
1497
+ this._currentBinLocation = ''
1498
+ this._selectedOrderInventory = null
1330
1499
  await CustomAlert({
1331
1500
  title: i18next.t('title.picking'),
1332
1501
  text: i18next.t('text.picking_is_completed'),
1333
1502
  confirmButton: { text: i18next.t('button.confirm') }
1334
1503
  })
1335
1504
 
1336
- this._clearView()
1337
1505
  if (!this.orderNo) {
1338
1506
  if (this.refPickingType === PICKING_TYPES.PICKING.value) {
1339
1507
  this.releaseGoodNoInput.value = ''
1340
- navigate('single_outbound_worksheets')
1508
+ // navigate('single_outbound_worksheets')
1341
1509
  } else {
1342
1510
  this.taskNoInput.value = ''
1343
- navigate('merged_outbound_worksheets')
1511
+ // navigate('merged_outbound_worksheets')
1344
1512
  }
1345
1513
  }
1514
+ this.updateContext()
1346
1515
  }
1347
1516
  }
1348
1517
 
1349
- _openPalletOutbound() {
1518
+ openPalletOutbound() {
1350
1519
  openPopup(
1351
1520
  html`
1352
1521
  <outbound-reusable-pallet
@@ -1367,12 +1536,110 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1367
1536
 
1368
1537
  pickingTypeChangeHandler(currentType) {
1369
1538
  this.refPickingType = currentType
1370
- this._clearView()
1539
+ this.clearView()
1371
1540
 
1372
1541
  if (this.refPickingType === PICKING_TYPES.PICKING.value) this._focusOnReleaseGoodNoInput()
1373
1542
  else this._focusOnTaskNoInput()
1374
1543
  }
1375
1544
 
1545
+ async getTask() {
1546
+ try {
1547
+ const response = await client.mutate({
1548
+ mutation: gql`
1549
+ mutation fetchAndAssignPickingTask {
1550
+ fetchAndAssignPickingTask {
1551
+ worksheetInfo {
1552
+ bizplaceName
1553
+ partnerDomainId
1554
+ refNo
1555
+ refNo2
1556
+ startedAt
1557
+ releaseGood {
1558
+ name
1559
+ crossDocking
1560
+ }
1561
+ }
1562
+ worksheetDetailInfos {
1563
+ name
1564
+ palletId
1565
+ cartonId
1566
+ batchId
1567
+ batchIdRef
1568
+ product {
1569
+ sku
1570
+ name
1571
+ description
1572
+ }
1573
+ qty
1574
+ releaseQty
1575
+ pickedQty
1576
+ expirationDate
1577
+ status
1578
+ description
1579
+ targetName
1580
+ packingType
1581
+ packingSize
1582
+ location {
1583
+ name
1584
+ description
1585
+ }
1586
+ relatedOrderInv {
1587
+ batchId
1588
+ batchIdRef
1589
+ packingType
1590
+ product {
1591
+ sku
1592
+ name
1593
+ description
1594
+ }
1595
+ }
1596
+ }
1597
+ }
1598
+ }
1599
+ `,
1600
+ variables: {},
1601
+ context: gqlContext()
1602
+ })
1603
+
1604
+ if (!response.errors) {
1605
+ if (!this._selectedOrderInventory) this.clearView()
1606
+ // this.releaseGoodNo = pickingRefNo
1607
+ if (!response.data?.fetchAndAssignPickingTask) return
1608
+
1609
+ const { worksheetInfo, worksheetDetailInfos } = response.data.fetchAndAssignPickingTask
1610
+ this._crossDocking = worksheetInfo.releaseGood?._crossDocking
1611
+ this.releaseGoodNo = worksheetInfo.releaseGood?.name
1612
+ this.data = { records: this.formatWorksheetDetailInfos(worksheetDetailInfos) }
1613
+
1614
+ this._enableProductScanning = await fetchSettingRule('enable-product-scanning', worksheetInfo.partnerDomainId)
1615
+
1616
+ if (this._selectedOrderInventory) {
1617
+ const record = this.data.records.find(record => record.name === this._selectedOrderInventory.name)
1618
+ if (record.pickedQty == record.releaseQty) {
1619
+ if (this._isCartonLabel && this.scannable) {
1620
+ this.cartonInput.value = ''
1621
+ this._focusOnCartonNoInput()
1622
+ }
1623
+ this._selectedOrderInventory = null
1624
+ // this._selectedTaskStatus = null
1625
+ this.inputForm.reset()
1626
+ } else {
1627
+ this._focusOnProductBarcodeInput()
1628
+ this.fillUpForm(this.inputForm, record)
1629
+ }
1630
+ }
1631
+ this.fillUpForm(this.infoForm, { ...worksheetInfo, _crossDocking: this._crossDocking })
1632
+ }
1633
+ } catch (e) {
1634
+ this._showToast(e)
1635
+ }
1636
+ }
1637
+
1638
+ async fetchAll(orderNo) {
1639
+ await this.fetchInventories(orderNo)
1640
+ await this.fetchPalletsHandler(orderNo)
1641
+ }
1642
+
1376
1643
  showWorksheetList() {
1377
1644
  openPopup(html` <show-my-worksheet-list-popup></show-my-worksheet-list-popup> `, {
1378
1645
  backdrop: true,
@@ -1381,6 +1648,71 @@ class PickingProduct extends connect(store)(localize(i18next)(PageView)) {
1381
1648
  })
1382
1649
  }
1383
1650
 
1651
+ // Prioritized input focus
1652
+ focusOnInput() {
1653
+ if (this.scannable) {
1654
+ // set bin location value if already has value else focus on bin input if bin picking setting is active
1655
+ if (this._currentBinLocation) {
1656
+ this.binLocationInput.value = this._currentBinLocation
1657
+ }
1658
+
1659
+ if (this._hasBinPicking && this.binLocationInput && this.binLocationInput.value == '') {
1660
+ this._focusOnBinLocationInput()
1661
+ return true
1662
+ } else {
1663
+ if (this._isCartonLabel) {
1664
+ if (!this._enableProductScanning) this.cartonInput.value = ''
1665
+
1666
+ if (this.cartonInput.value == '') {
1667
+ this._focusOnCartonNoInput()
1668
+ return true
1669
+ }
1670
+ } else {
1671
+ if (!this._enableProductScanning && !this._isRequireSerialNumberScanning) this.palletInput.value = ''
1672
+
1673
+ if (this.palletInput.value == '') {
1674
+ this._focusOnLotNoInput()
1675
+ return true
1676
+ }
1677
+ }
1678
+ }
1679
+
1680
+ if (this._enableProductScanning && !this._isRequireSerialNumberScanning) {
1681
+ this.productBarcodeInput.value = ''
1682
+ this._focusOnProductBarcodeInput()
1683
+ return true
1684
+ }
1685
+
1686
+ if (this._isRequireSerialNumberScanning) {
1687
+ this.serialInput.value = ''
1688
+ this._focusOnSerialInput()
1689
+ return true
1690
+ }
1691
+
1692
+ return true
1693
+ }
1694
+ }
1695
+
1696
+ updateSelectedItemProp(selectedRecords) {
1697
+ if (selectedRecords.releaseQty === selectedRecords.qty) {
1698
+ this.isWholePicking = true
1699
+ this._selectedOrderInventory = selectedRecords
1700
+ } else {
1701
+ this.isWholePicking = false
1702
+ this._selectedOrderInventory = selectedRecords
1703
+ }
1704
+
1705
+ this._isCartonLabel = selectedRecords.hasCartonLabel
1706
+ this._productName = selectedRecords.product?.name
1707
+ ? `${selectedRecords.product.sku + ' - ' + selectedRecords.product.name} ${
1708
+ selectedRecords.product?.description ? `(${selectedRecords.product.description})` : ''
1709
+ }`
1710
+ : ''
1711
+ this._isRequireSerialNumberScanning = selectedRecords?.product?.isRequireSerialNumberScanning
1712
+
1713
+ // this.isWholePicking = selectedRecords.releaseQty === selectedRecords.qty
1714
+ }
1715
+
1384
1716
  _showToast({ type, message }) {
1385
1717
  document.dispatchEvent(
1386
1718
  new CustomEvent('notify', {