@things-factory/operato-wms 4.3.769 → 4.3.770

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 (42) hide show
  1. package/client/pages/components/list-view/product-list-view.js +28 -3
  2. package/client/pages/components/mobile-condition-of-goods-listing.js +342 -0
  3. package/client/pages/components/unloading-worksheet-information-popup.js +299 -0
  4. package/client/pages/constants/order.js +7 -0
  5. package/client/pages/inbound/condition-of-goods-popup.js +646 -0
  6. package/client/pages/inbound/putaway/putaway-product.js +35 -6
  7. package/client/pages/inbound/unload-product-landing-page.js +282 -0
  8. package/client/pages/inbound/unload-product.js +707 -175
  9. package/client/pages/inbound/unloaded-inventories-popup.js +3 -0
  10. package/client/pages/inventory/inventory-adjustment-approval.js +28 -1
  11. package/client/pages/inventory/inventory-adjustment.js +31 -1
  12. package/client/pages/inventory/inventory-by-product-detail.js +15 -0
  13. package/client/pages/inventory/inventory-history.js +18 -0
  14. package/client/pages/inventory/onhand-inventory.js +14 -0
  15. package/client/pages/order/release-order/b2b/b2b-order-list.js +99 -0
  16. package/client/pages/order/release-order/packing-label-history-popup.js +378 -0
  17. package/client/pages/order/release-order/preview-print-packing-list.js +533 -0
  18. package/client/pages/order/release-order/print-quantity-popup.js +118 -0
  19. package/client/pages/outbound/loading-v2/loading-execution-base.js +29 -0
  20. package/client/pages/outbound/loading-v2/loading-execution.js +579 -2
  21. package/client/pages/outbound/loading-v2/loading-package-info-popup.js +182 -0
  22. package/client/pages/outbound/loading-v2/loading.js +18 -0
  23. package/client/pages/report/inbound-order-details-report.js +26 -1
  24. package/client/route.js +17 -0
  25. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js +9 -1
  26. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js.map +1 -1
  27. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js +11 -5
  28. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js.map +1 -1
  29. package/dist-server/graphql/resolvers/reports/inbound-order-details-report.js +2 -1
  30. package/dist-server/graphql/resolvers/reports/inbound-order-details-report.js.map +1 -1
  31. package/dist-server/graphql/types/reports/inbound-order-details-report.js +1 -0
  32. package/dist-server/graphql/types/reports/inbound-order-details-report.js.map +1 -1
  33. package/package.json +15 -15
  34. package/server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.ts +9 -1
  35. package/server/graphql/resolvers/outbound/loading-worksheet-v2.ts +13 -1
  36. package/server/graphql/resolvers/reports/inbound-order-details-report.ts +2 -1
  37. package/server/graphql/types/reports/inbound-order-details-report.ts +1 -0
  38. package/things-factory.config.js +8 -0
  39. package/translations/en.json +45 -1
  40. package/translations/ko.json +40 -0
  41. package/translations/ms.json +40 -0
  42. package/translations/zh.json +40 -0
@@ -0,0 +1,378 @@
1
+ import '@things-factory/form-ui'
2
+ import '@things-factory/grist-ui'
3
+ import './print-quantity-popup'
4
+
5
+ import gql from 'graphql-tag'
6
+ import { css, html, LitElement } from 'lit-element'
7
+
8
+ import { i18next, localize } from '@things-factory/i18n-base'
9
+ import { openPopup } from '@things-factory/layout-base'
10
+ import { client } from '@things-factory/shell'
11
+ import { ScrollbarStyles } from '@things-factory/styles'
12
+ import { isMobileDevice } from '@things-factory/utils'
13
+
14
+ import { showToast } from '../../../util'
15
+
16
+ class PackingLabelHistoryPopup extends localize(i18next)(LitElement) {
17
+ static get properties() {
18
+ return {
19
+ _searchFields: Array,
20
+ config: Object,
21
+ data: Object,
22
+ _packingLabelOptions: Array
23
+ }
24
+ }
25
+
26
+ static get styles() {
27
+ return [
28
+ ScrollbarStyles,
29
+ css`
30
+ :host {
31
+ display: flex;
32
+ flex-direction: column;
33
+ overflow: hidden;
34
+ background-color: white;
35
+ }
36
+
37
+ search-form {
38
+ overflow: visible;
39
+ }
40
+
41
+ .grist {
42
+ display: flex;
43
+ flex-direction: column;
44
+ flex: 1;
45
+ overflow-y: auto;
46
+ }
47
+
48
+ .grist-container {
49
+ overflow-y: hidden;
50
+ display: flex;
51
+ flex: 1;
52
+ }
53
+
54
+ data-grist {
55
+ overflow-y: hidden;
56
+ flex: 1;
57
+ }
58
+
59
+ .button-container {
60
+ padding: var(--button-container-padding);
61
+ margin: var(--button-container-margin);
62
+ text-align: var(--button-container-align);
63
+ background-color: var(--button-container-background);
64
+ height: var(--button-container-height);
65
+ }
66
+ `
67
+ ]
68
+ }
69
+
70
+ constructor() {
71
+ super()
72
+ this._packingLabelOptions = []
73
+ }
74
+
75
+ get searchForm() {
76
+ return this.shadowRoot.querySelector('search-form')
77
+ }
78
+
79
+ get dataGrist() {
80
+ return this.shadowRoot.querySelector('data-grist')
81
+ }
82
+
83
+ _initSearchFields() {
84
+ this._searchFields = [
85
+ {
86
+ label: i18next.t('field.order_no') || 'Order No.',
87
+ name: 'releaseGood.name',
88
+ type: 'text',
89
+ props: { searchOper: 'i_like' }
90
+ },
91
+ {
92
+ label: i18next.t('field.packing_label_no'),
93
+ name: 'displayNumber',
94
+ type: 'select',
95
+ options: this._packingLabelOptions,
96
+ props: { searchOper: 'eq' }
97
+ }
98
+ ]
99
+ }
100
+
101
+ _initConfig() {
102
+ this.config = {
103
+ rows: { selectable: { multiple: true }, appendable: false },
104
+ list: { fields: ['releaseGood', 'refNo', 'displayNumber', 'printedAt'] },
105
+ pagination: { limit: 50, pages: [20, 50, 100, 200] },
106
+ columns: [
107
+ { type: 'gutter', gutterName: 'sequence' },
108
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
109
+ {
110
+ type: 'gutter',
111
+ gutterName: 'button',
112
+ icon: 'reorder',
113
+ handlers: {
114
+ click: (_columns, _data, _column, record) => {}
115
+ }
116
+ },
117
+ {
118
+ type: 'object',
119
+ name: 'releaseGood',
120
+ header: i18next.t('field.order_no') || 'Release Orders',
121
+ sortable: true,
122
+ width: 180
123
+ },
124
+ {
125
+ type: 'string',
126
+ name: 'refNo',
127
+ header: i18next.t('field.ref_no') || 'Ref No.',
128
+ sortable: true,
129
+ width: 150
130
+ },
131
+ {
132
+ type: 'number',
133
+ name: 'displayNumber',
134
+ header: i18next.t('field.packing_label_no') || 'Packing Label No.',
135
+ sortable: true,
136
+ width: 120
137
+ },
138
+ {
139
+ type: 'datetime',
140
+ name: 'printedAt',
141
+ header: i18next.t('field.printed_at') || 'Printed At',
142
+ sortable: true,
143
+ width: 160
144
+ },
145
+ {
146
+ type: 'object',
147
+ name: 'printedBy',
148
+ header: i18next.t('field.printed_by') || 'Printed By',
149
+ sortable: true,
150
+ width: 160
151
+ }
152
+ ]
153
+ }
154
+ }
155
+
156
+ async firstUpdated() {
157
+ await this._fetchPackingLabelOptions()
158
+ this._initSearchFields()
159
+ this._initConfig()
160
+ }
161
+
162
+ async _fetchPackingLabelOptions() {
163
+ try {
164
+ const response = await client.query({
165
+ query: gql`
166
+ query {
167
+ maxPackingLabelCount
168
+ }
169
+ `
170
+ })
171
+
172
+ if (!response.errors) {
173
+ const maxCount = response.data.maxPackingLabelCount || 0
174
+ this._packingLabelOptions = [
175
+ { value: '', name: '' },
176
+ ...Array.from({ length: maxCount }, (_, i) => ({
177
+ value: String(i + 1),
178
+ name: String(i + 1)
179
+ }))
180
+ ]
181
+ }
182
+ } catch (e) {
183
+ console.error('Failed to fetch packing label options:', e)
184
+ this._packingLabelOptions = [{ value: '', name: '' }]
185
+ }
186
+ }
187
+
188
+ async fetchHandler({ page, limit, sorters = [] }) {
189
+ const filters = this.searchForm.queryFilters
190
+
191
+ const response = await client.query({
192
+ query: gql`
193
+ query printedLoadingPackagesHistory($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
194
+ printedLoadingPackagesHistory(filters: $filters, pagination: $pagination, sortings: $sortings) {
195
+ items {
196
+ id
197
+ name
198
+ displayNumber
199
+ releaseGood {
200
+ id
201
+ name
202
+ refNo
203
+ }
204
+ printedAt
205
+ printedBy {
206
+ id
207
+ name
208
+ description
209
+ }
210
+ }
211
+ total
212
+ }
213
+ }
214
+ `,
215
+ variables: {
216
+ filters,
217
+ pagination: { page, limit },
218
+ sortings: sorters
219
+ }
220
+ })
221
+
222
+ if (!response.errors) {
223
+ return {
224
+ total: response.data.printedLoadingPackagesHistory.total || 0,
225
+ records:
226
+ response.data.printedLoadingPackagesHistory.items.map(item => ({
227
+ ...item,
228
+ refNo: item.releaseGood?.refNo || ''
229
+ })) || []
230
+ }
231
+ }
232
+
233
+ return { total: 0, records: [] }
234
+ }
235
+
236
+ _handlePrint() {
237
+ try {
238
+ const selected = this.dataGrist.selected || []
239
+ if (selected.length === 0) {
240
+ showToast({
241
+ type: 'warning',
242
+ message: i18next.t('text.please_select_at_least_one_record') || 'Please select at least one record'
243
+ })
244
+ return
245
+ }
246
+
247
+ openPopup(
248
+ html`
249
+ <print-quantity-popup
250
+ @print="${e => {
251
+ const printQuantity = e.detail.printQuantity
252
+
253
+ // Validate print quantity
254
+ if (!printQuantity || printQuantity <= 0) {
255
+ showToast({
256
+ type: 'error',
257
+ message: i18next.t('text.print_qty_must_be_at_least_one') || 'Print quantity must be at least 1'
258
+ })
259
+ return
260
+ }
261
+
262
+ // Close popup and execute print
263
+ history.back()
264
+ this._executePrint(selected, printQuantity)
265
+ }}"
266
+ ></print-quantity-popup>
267
+ `,
268
+ {
269
+ backdrop: true,
270
+ size: 'small',
271
+ title: i18next.t('title.print_packing_label') || 'Print Packing Label'
272
+ }
273
+ )
274
+ } catch (e) {
275
+ showToast({ type: 'error', message: e.message || i18next.t('text.failed_to_print') })
276
+ }
277
+ }
278
+
279
+ async _executePrint(selected, printQuantity) {
280
+ const loadingPackageIds = selected.map(item => item.id).filter(Boolean)
281
+
282
+ if (loadingPackageIds.length === 0) {
283
+ showToast({ type: 'error', message: i18next.t('text.no_valid_packages_selected') || 'No valid packages selected' })
284
+ return
285
+ }
286
+
287
+ try {
288
+ const response = await fetch('/render_packing_label', {
289
+ method: 'POST',
290
+ credentials: 'include',
291
+ headers: { 'Content-Type': 'application/json' },
292
+ body: JSON.stringify({
293
+ loadingPackageIds,
294
+ printQuantity: Math.max(1, parseInt(printQuantity) || 1)
295
+ })
296
+ })
297
+
298
+ if (!response.ok) {
299
+ const errorText = await response.text()
300
+ throw new Error(errorText || `Failed to render packing label: ${response.statusText}`)
301
+ }
302
+
303
+ const renderedHtml = await response.text()
304
+ if (!renderedHtml) {
305
+ showToast({ type: 'error', message: i18next.t('text.failed_to_render_template') || 'Failed to render template' })
306
+ return
307
+ }
308
+
309
+ await this._printViaIframe(renderedHtml)
310
+ this.dataGrist.fetch()
311
+ } catch (error) {
312
+ console.error('Print error:', error)
313
+ showToast({ type: 'error', message: error.message || i18next.t('text.failed_to_print') || 'Failed to print' })
314
+ }
315
+ }
316
+
317
+ async _printViaIframe(htmlContent) {
318
+ return new Promise((resolve, reject) => {
319
+ const iframe = this._printIframe
320
+ if (!iframe) {
321
+ reject(new Error('Print iframe not found'))
322
+ return
323
+ }
324
+
325
+ iframe.onload = () => {
326
+ try {
327
+ iframe.contentWindow.focus()
328
+ iframe.contentWindow.print()
329
+ resolve()
330
+ } catch (e) {
331
+ reject(e)
332
+ }
333
+ }
334
+
335
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow.document
336
+ iframeDoc.open()
337
+ iframeDoc.write(htmlContent)
338
+ iframeDoc.close()
339
+ })
340
+ }
341
+
342
+ get _printIframe() {
343
+ return this.shadowRoot?.querySelector('#print-iframe')
344
+ }
345
+
346
+ _closePopup() {
347
+ history.back()
348
+ }
349
+
350
+ render() {
351
+ return html`
352
+ <search-form
353
+ id="search-form"
354
+ .fields=${this._searchFields}
355
+ @submit=${() => this.dataGrist.fetch()}
356
+ ></search-form>
357
+
358
+ <div class="grist-container">
359
+ <div class="grist">
360
+ <data-grist
361
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
362
+ .config=${this.config}
363
+ .fetchHandler=${this.fetchHandler.bind(this)}
364
+ ></data-grist>
365
+ </div>
366
+ </div>
367
+
368
+ <div class="button-container">
369
+ <mwc-button @click=${this._closePopup}>${i18next.t('button.close')}</mwc-button>
370
+ <mwc-button raised @click=${this._handlePrint}>${i18next.t('button.print')}</mwc-button>
371
+ </div>
372
+
373
+ <iframe id="print-iframe" style="display: none;"></iframe>
374
+ `
375
+ }
376
+ }
377
+
378
+ window.customElements.define('packing-label-history-popup', PackingLabelHistoryPopup)