@things-factory/operato-mms 4.0.0-alpha.7 → 4.0.0-beta.1

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 (71) hide show
  1. package/Dockerfile +2 -1
  2. package/client/bootstrap.js +32 -16
  3. package/client/components/marketplace-order-max-weight.js +158 -0
  4. package/client/components/mms-home-cards.js +1 -1
  5. package/client/components/stock-allocation-ratio.js +17 -23
  6. package/client/pages/catalogue/products.js +2 -1
  7. package/client/pages/catalogue/warehouse-products.js +2 -1
  8. package/client/pages/catalogue-variation/product-variations.js +72 -15
  9. package/client/pages/inventory/inventory-by-product.js +3 -2
  10. package/client/pages/order/logistics/batch-init-popup.js +566 -0
  11. package/client/pages/order/logistics/lazada-order-init-batch-popup.js +233 -21
  12. package/client/pages/order/logistics/order-shipping-items.js +47 -16
  13. package/client/pages/order/logistics/order-shipping.js +7 -4
  14. package/client/pages/order/logistics/request-ro-popup.js +104 -17
  15. package/client/pages/order/logistics/shopee-order-init-batch-popup.js +431 -129
  16. package/client/pages/order/order-by-store.js +176 -149
  17. package/client/pages/setting/marketplace-setting.js +12 -0
  18. package/dist-server/graphql/index.js +1 -1
  19. package/dist-server/graphql/resolvers/index.js +1 -1
  20. package/dist-server/graphql/resolvers/interface-with-hub/add-release-order.js +74 -13
  21. package/dist-server/graphql/resolvers/interface-with-hub/add-release-order.js.map +1 -1
  22. package/dist-server/graphql/resolvers/interface-with-hub/auto-add-release-order.js +74 -13
  23. package/dist-server/graphql/resolvers/interface-with-hub/auto-add-release-order.js.map +1 -1
  24. package/dist-server/graphql/resolvers/interface-with-hub/generate-replenishment-order.js +1 -1
  25. package/dist-server/graphql/resolvers/interface-with-hub/generate-replenishment-order.js.map +1 -1
  26. package/dist-server/graphql/resolvers/interface-with-hub/warehouse-marketplace-products.js +53 -36
  27. package/dist-server/graphql/resolvers/interface-with-hub/warehouse-marketplace-products.js.map +1 -1
  28. package/dist-server/graphql/resolvers/warehouse-product/auto-link-warehouse-marketplace-product-variations.js +5 -5
  29. package/dist-server/graphql/resolvers/warehouse-product/auto-link-warehouse-marketplace-product-variations.js.map +1 -1
  30. package/dist-server/graphql/types/index.js +1 -1
  31. package/dist-server/graphql/types/interface-with-hub/new-stock-replenishment.js +1 -1
  32. package/dist-server/graphql/types/interface-with-hub/new-stock-replenishment.js.map +1 -1
  33. package/dist-server/graphql/types/interface-with-hub/replenishment-order-product.js +1 -1
  34. package/dist-server/graphql/types/interface-with-hub/replenishment-order-product.js.map +1 -1
  35. package/dist-server/graphql/types/interface-with-hub/stock-replenishment-list.js +1 -1
  36. package/dist-server/graphql/types/interface-with-hub/stock-replenishment-list.js.map +1 -1
  37. package/dist-server/graphql/types/interface-with-hub/stock-replenishment.js +1 -1
  38. package/dist-server/graphql/types/interface-with-hub/stock-replenishment.js.map +1 -1
  39. package/dist-server/graphql/types/interface-with-hub/warehouse-inventory.js +1 -1
  40. package/dist-server/graphql/types/interface-with-hub/warehouse-inventory.js.map +1 -1
  41. package/dist-server/graphql/types/interface-with-hub/warehouse-marketplace-product-list.js +1 -1
  42. package/dist-server/graphql/types/interface-with-hub/warehouse-marketplace-product-list.js.map +1 -1
  43. package/dist-server/graphql/types/interface-with-hub/warehouse-marketplace-product.js +1 -1
  44. package/dist-server/graphql/types/interface-with-hub/warehouse-marketplace-product.js.map +1 -1
  45. package/dist-server/graphql/types/warehouse-product/warehouse-product.js +1 -1
  46. package/dist-server/graphql/types/warehouse-product/warehouse-product.js.map +1 -1
  47. package/dist-server/index.js +1 -1
  48. package/dist-server/migrations/1599732967233-SeedCommonCode.js +5 -5
  49. package/dist-server/migrations/1599732967233-SeedCommonCode.js.map +1 -1
  50. package/dist-server/migrations/1608009991075-SeedUser.js +4 -4
  51. package/dist-server/migrations/1608009991075-SeedUser.js.map +1 -1
  52. package/dist-server/routers/etrax-router.js +7 -6
  53. package/dist-server/routers/etrax-router.js.map +1 -1
  54. package/dist-server/routers/shopify-pos-public-router.js +10 -10
  55. package/dist-server/routers/shopify-pos-public-router.js.map +1 -1
  56. package/dist-server/util/interface-helper.js +2 -2
  57. package/dist-server/util/interface-helper.js.map +1 -1
  58. package/dist-server/util/no-generator.js +2 -2
  59. package/dist-server/util/no-generator.js.map +1 -1
  60. package/package.json +65 -63
  61. package/server/graphql/resolvers/interface-with-hub/add-release-order.ts +83 -14
  62. package/server/graphql/resolvers/interface-with-hub/auto-add-release-order.ts +79 -11
  63. package/server/graphql/resolvers/interface-with-hub/warehouse-marketplace-products.ts +50 -33
  64. package/server/routers/etrax-router.ts +6 -4
  65. package/translations/en.json +16 -0
  66. package/translations/ko.json +14 -0
  67. package/translations/ms.json +14 -0
  68. package/translations/zh.json +14 -0
  69. package/views/auth-page.html +1 -1
  70. package/views/public/home.html +1 -1
  71. package/config.development.js +0 -54
@@ -0,0 +1,566 @@
1
+ import '@things-factory/grist-ui'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit-element'
5
+
6
+ import { MultiColumnFormStyles } from '@things-factory/form-ui'
7
+ import { i18next, localize } from '@things-factory/i18n-base'
8
+ import { client } from '@things-factory/shell'
9
+ import { isMobileDevice } from '@things-factory/utils'
10
+
11
+ class BatchInitPopup extends localize(i18next)(LitElement) {
12
+ static get properties() {
13
+ return {
14
+ _searchFields: Array,
15
+ config: Object,
16
+ shippingProviders: Array,
17
+ store: Object,
18
+ marketplaceSettings: Array
19
+ }
20
+ }
21
+
22
+ constructor() {
23
+ super()
24
+ this.shippingProviders = []
25
+ }
26
+
27
+ static get styles() {
28
+ return [
29
+ MultiColumnFormStyles,
30
+ css`
31
+ :host {
32
+ padding: 10px;
33
+ display: flex;
34
+ flex-direction: column;
35
+ overflow-x: overlay;
36
+ background-color: var(--main-section-background-color);
37
+ }
38
+ .grist {
39
+ background-color: var(--main-section-background-color);
40
+ display: flex;
41
+ flex-direction: column;
42
+ flex: 1;
43
+ overflow-y: auto;
44
+ }
45
+ data-grist {
46
+ overflow-y: hidden;
47
+ flex: 1;
48
+ }
49
+ .button-container {
50
+ padding: var(--button-container-padding);
51
+ margin: var(--button-container-margin);
52
+ text-align: var(--button-container-align);
53
+ background-color: var(--button-container-background);
54
+ height: var(--button-container-height);
55
+ }
56
+ search-form {
57
+ overflow: visible;
58
+ background-color: var(--main-section-background-color);
59
+ }
60
+ `
61
+ ]
62
+ }
63
+
64
+ render() {
65
+ const shippingProviders = [{ name: 'SeeBuu Logistics' }, { name: 'GDex' }]
66
+
67
+ return html`
68
+ <search-form id="search-form" .fields=${this._searchFields} @submit=${e => this.dataGrist.fetch()}></search-form>
69
+
70
+ <div name="batchUpdate" class="multi-column-form">
71
+ <label>${i18next.t('label.courier_service')}</label>
72
+ <select name="shippingProvider">
73
+ <option value="">${i18next.t('text.select_courier')}</option>
74
+ ${(shippingProviders || []).map(
75
+ shippingProvider => html` <option value="${shippingProvider.name}">${shippingProvider.name}</option> `
76
+ )}
77
+ </select>
78
+
79
+ <div class="set-button-container">
80
+ <mwc-button
81
+ @click="${e => this._batchUpdateSetHandler()}"
82
+ raised
83
+ label="${i18next.t('button.set')}"
84
+ ></mwc-button>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="grist">
89
+ <data-grist
90
+ id="grist"
91
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
92
+ .fetchHandler="${this.fetchHandler.bind(this)}"
93
+ .config=${this.config}
94
+ ></data-grist>
95
+ <div class="button-container">
96
+ <mwc-button
97
+ @click="${this.splitOrders.bind(this)}"
98
+ raised
99
+ label="${i18next.t('button.split_order')}"
100
+ ></mwc-button>
101
+ <mwc-button
102
+ @click="${this.initLogistics.bind(this)}"
103
+ raised
104
+ label="${i18next.t('button.initialise')}"
105
+ ></mwc-button>
106
+ </div>
107
+ </div>
108
+ `
109
+ }
110
+
111
+ get searchForm() {
112
+ return this.shadowRoot.querySelector('search-form')
113
+ }
114
+
115
+ get dataGrist() {
116
+ return this.shadowRoot.querySelector('data-grist')
117
+ }
118
+
119
+ get shippingProviderInput() {
120
+ return this.shadowRoot.querySelector('select[name=shippingProvider]')
121
+ }
122
+
123
+ async firstUpdated() {
124
+ const geoCities = await this.fetchCityList()
125
+ this.marketplaceSettings = await this.fetchMarketplaceSettings()
126
+ const maxWeightSetting = this.marketplaceSettings.filter(
127
+ setting => setting.category === 'marketplace_order_max_weight'
128
+ )
129
+ // this.shippingProviders = await this.fetchShippingProvider()
130
+
131
+ this._searchFields = [
132
+ {
133
+ label: i18next.t('field.city'),
134
+ name: 'city',
135
+ type: 'select',
136
+ options: [
137
+ { value: '' },
138
+ ...geoCities.map(geoCity => {
139
+ return {
140
+ name: geoCity.name,
141
+ value: geoCity.name
142
+ }
143
+ })
144
+ ],
145
+ props: { searchOper: 'eq' }
146
+ }
147
+ ]
148
+
149
+ this.config = {
150
+ list: {
151
+ fields: [
152
+ 'orderNoRef',
153
+ 'city',
154
+ 'postCode',
155
+ 'trackingNo',
156
+ 'shippingProvider',
157
+ 'totalVolume',
158
+ 'totalWeight',
159
+ 'status'
160
+ ]
161
+ },
162
+ rows: {
163
+ selectable: { multiple: true },
164
+ appendable: false,
165
+ classifier: function (record, rowIndex) {
166
+ const totalWeight = record['totalWeight']
167
+ const maxWeight = maxWeightSetting[0].value
168
+ var emphasized
169
+ if (totalWeight > maxWeight) {
170
+ emphasized = ['#F43838', 'yellow']
171
+ }
172
+
173
+ return { emphasized }
174
+ }
175
+ },
176
+ columns: [
177
+ { type: 'gutter', gutterName: 'sequence' },
178
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
179
+ {
180
+ type: 'string',
181
+ name: 'orderNoRef',
182
+ header: i18next.t('field.order_no'),
183
+ label: true,
184
+ sortable: true,
185
+ width: 130
186
+ },
187
+ {
188
+ type: 'string',
189
+ name: 'city',
190
+ header: i18next.t('field.city'),
191
+ label: true,
192
+ width: 120
193
+ },
194
+ {
195
+ type: 'string',
196
+ name: 'postCode',
197
+ header: i18next.t('field.postal_code'),
198
+ label: true,
199
+ width: 120
200
+ },
201
+ {
202
+ type: 'string',
203
+ name: 'trackingNo',
204
+ header: i18next.t('field.tracking_no'),
205
+ record: { editable: true },
206
+ label: true,
207
+ sortable: false,
208
+ width: 130
209
+ },
210
+ {
211
+ type: 'string',
212
+ name: 'ownTrackingNo',
213
+ header: i18next.t('field.own_tracking_no'),
214
+ record: { editable: true },
215
+ label: true,
216
+ sortable: false,
217
+ width: 130
218
+ },
219
+ {
220
+ type: 'string',
221
+ name: 'shippingProvider',
222
+ header: i18next.t('field.shipping_carrier'),
223
+ label: true,
224
+ sortable: true,
225
+ width: 130
226
+ },
227
+ {
228
+ type: 'string',
229
+ name: 'ownShippingProvider',
230
+ header: i18next.t('field.own_shipping_carrier'),
231
+ label: true,
232
+ sortable: true,
233
+ width: 130
234
+ },
235
+ {
236
+ type: 'number',
237
+ name: 'totalVolume',
238
+ header: i18next.t('field.total_volume'),
239
+ label: true,
240
+ width: 120
241
+ },
242
+ {
243
+ type: 'number',
244
+ name: 'totalWeight',
245
+ header: i18next.t('field.total_weight'),
246
+ label: true,
247
+ width: 120
248
+ },
249
+ {
250
+ type: 'string',
251
+ name: 'status',
252
+ header: i18next.t('field.status'),
253
+ label: true,
254
+ sortable: true,
255
+ width: 120
256
+ }
257
+ ]
258
+ }
259
+ }
260
+
261
+ async fetchCityList() {
262
+ const filters = []
263
+ const sortings = []
264
+
265
+ const response = await client.query({
266
+ query: gql`
267
+ query geoCities($filters: [Filter!], $sortings: [Sorting!]) {
268
+ geoCities(filters: $filters, sortings: $sortings) {
269
+ items {
270
+ id
271
+ name
272
+ }
273
+ }
274
+ }
275
+ `,
276
+ variables: { filters, sortings }
277
+ })
278
+
279
+ if (!response.errors) {
280
+ return response.data.geoCities.items || []
281
+ }
282
+ }
283
+
284
+ async fetchHandler({
285
+ page,
286
+ limit,
287
+ sorters = [
288
+ { name: 'totalWeight', desc: true },
289
+ { name: 'orderNoRef', desc: true }
290
+ ]
291
+ }) {
292
+ const filters = [
293
+ {
294
+ name: 'marketplaceStoreId',
295
+ operator: 'eq',
296
+ value: this.store.id
297
+ },
298
+ {
299
+ name: 'transporter',
300
+ operator: 'is_null',
301
+ value: ''
302
+ },
303
+ {
304
+ name: 'status',
305
+ operator: 'notin',
306
+ value: ['canceled', 'cancelled']
307
+ }
308
+ ]
309
+
310
+ const pagination = { page, limit }
311
+ const sortings = sorters
312
+
313
+ const response = await client.query({
314
+ query: gql`
315
+ query marketplaceOrderShippingsForBatchInit($filters: [Filter], $pagination: Pagination, $sortings: [Sorting]) {
316
+ marketplaceOrderShippingsForBatchInit(filters: $filters, pagination: $pagination, sortings: $sortings) {
317
+ items {
318
+ id
319
+ name
320
+ address1
321
+ address2
322
+ address3
323
+ address4
324
+ address5
325
+ city
326
+ postCode
327
+ country
328
+ state
329
+ phone1
330
+ phone2
331
+ attentionTo
332
+ trackingNo
333
+ ownTrackingNo
334
+ transporter
335
+ ownTransporter
336
+ orderNoRef
337
+ subOrderNoRef
338
+ totalWeight
339
+ weightUnit
340
+ totalVolume
341
+ volumeUnit
342
+ marketplaceStore {
343
+ id
344
+ name
345
+ platform
346
+ description
347
+ }
348
+ marketplaceOrder {
349
+ id
350
+ name
351
+ marketplaceOrderItems {
352
+ marketplaceProductVariation {
353
+ name
354
+ variationSku
355
+ }
356
+ qty
357
+ }
358
+ orderNo
359
+ type
360
+ paymentMethod
361
+ status
362
+ itemCount
363
+ shippingProvider
364
+ totalAmount
365
+ orderCreatedAt
366
+ orderUpdatedAt
367
+ updatedAt
368
+ }
369
+ }
370
+ total
371
+ }
372
+ }
373
+ `,
374
+ variables: { filters, pagination, sortings }
375
+ })
376
+
377
+ if (!response.errors) {
378
+ const marketplaceOrderShippings = response.data.marketplaceOrderShippingsForBatchInit.items || []
379
+
380
+ return {
381
+ total: marketplaceOrderShippings.length || 0,
382
+ records:
383
+ marketplaceOrderShippings.map(itm => {
384
+ return {
385
+ ...itm,
386
+ status: itm.marketplaceOrder.status,
387
+ shippingProvider: itm.transporter,
388
+ ownShippingProvider: itm.ownTransporter
389
+ }
390
+ }) || []
391
+ }
392
+ }
393
+ }
394
+
395
+ async fetchShippingProvider() {
396
+ const filters = []
397
+ const response = await client.query({
398
+ query: gql`
399
+ query courierServices($filters: [Filter]) {
400
+ courierServices(filters: $filters) {
401
+ items {
402
+ id
403
+ name
404
+ description
405
+ transporterId
406
+ }
407
+ total
408
+ }
409
+ }
410
+ `,
411
+ variables: { filters }
412
+ })
413
+
414
+ if (!response.errors) {
415
+ return response.data.courierServices
416
+ }
417
+ }
418
+
419
+ async fetchMarketplaceSettings() {
420
+ const filters = []
421
+ const sortings = []
422
+
423
+ const response = await client.query({
424
+ query: gql`
425
+ query marketplaceSettings($filters: [Filter], $sortings: [Sorting]) {
426
+ marketplaceSettings(filters: $filters, sortings: $sortings) {
427
+ items {
428
+ id
429
+ name
430
+ marketplaceStore {
431
+ id
432
+ name
433
+ storeName
434
+ platform
435
+ description
436
+ }
437
+ category
438
+ value
439
+ updatedAt
440
+ }
441
+ total
442
+ }
443
+ }
444
+ `,
445
+ variables: { filters, sortings }
446
+ })
447
+
448
+ return response.data.marketplaceSettings.items || []
449
+ }
450
+
451
+ _batchUpdateSetHandler() {
452
+ const selectedData = this.dataGrist.selected
453
+ const shippingProviderValue = this.shippingProviderInput?.value
454
+
455
+ this.dataGrist._data.records = [
456
+ ...this.dataGrist._data.records.map(item => {
457
+ if (selectedData.find(selected => selected.id == item.id) && shippingProviderValue) {
458
+ return {
459
+ ...item,
460
+ shippingProvider: shippingProviderValue,
461
+ ownShippingProvider: shippingProviderValue
462
+ }
463
+ } else {
464
+ return item
465
+ }
466
+ })
467
+ ]
468
+
469
+ this.dataGrist.refresh()
470
+ }
471
+
472
+ async initLogistics() {
473
+ try {
474
+ let selectedData = this.dataGrist.selected
475
+ this._validateInit(selectedData)
476
+
477
+ const patches = selectedData.map(patch => {
478
+ return {
479
+ id: patch.id,
480
+ orderNo: patch.marketplaceOrder.orderNo,
481
+ trackingNo: patch?.trackingNo,
482
+ shippingProvider: patch?.shippingProvider
483
+ }
484
+ })
485
+
486
+ const response = await client.mutate({
487
+ mutation: gql`
488
+ mutation initialiseLogisticsByBatch($patches: [LogisticsInfo]!) {
489
+ initialiseLogisticsByBatch(patches: $patches)
490
+ }
491
+ `,
492
+ variables: { patches }
493
+ })
494
+
495
+ if (!response.errors) {
496
+ this.dispatchEvent(new CustomEvent('order-updated'))
497
+ history.back()
498
+ }
499
+ } catch (e) {
500
+ this._showToast(e)
501
+ }
502
+ }
503
+
504
+ async splitOrders() {
505
+ try {
506
+ let selectedData = this.dataGrist.selected
507
+ this._validateSplitting(selectedData)
508
+
509
+ const patches = selectedData.map(patch => {
510
+ return {
511
+ id: patch.marketplaceOrder.id,
512
+ orderNo: patch.orderNo
513
+ }
514
+ })
515
+
516
+ const response = await client.mutate({
517
+ mutation: gql`
518
+ mutation splitOrdersByBatch($patches: [LogisticsInfo]!) {
519
+ splitOrdersByBatch(patches: $patches)
520
+ }
521
+ `,
522
+ variables: { patches }
523
+ })
524
+
525
+ if (!response.errors) {
526
+ this.dispatchEvent(new CustomEvent('order-updated'))
527
+ history.back()
528
+ }
529
+ } catch (e) {
530
+ this._showToast(e)
531
+ }
532
+ }
533
+
534
+ _validateInit(data) {
535
+ if (!data) {
536
+ throw new Error('No order to initiate logistics')
537
+ }
538
+
539
+ if (!this.shippingProviderInput?.value || this.shippingProviderInput.value == '') {
540
+ throw new Error('Shipping provider selection is required for initiate logistics')
541
+ }
542
+ }
543
+
544
+ _validateSplitting(data) {
545
+ if (!data) {
546
+ throw new Error('No order to split')
547
+ }
548
+
549
+ if (data.some(item => item.trackingNo)) {
550
+ throw new Error('Order with tracking no not allowed to split')
551
+ }
552
+ }
553
+
554
+ _showToast({ type, message }) {
555
+ document.dispatchEvent(
556
+ new CustomEvent('notify', {
557
+ detail: {
558
+ type,
559
+ message
560
+ }
561
+ })
562
+ )
563
+ }
564
+ }
565
+
566
+ window.customElements.define('batch-init-popup', BatchInitPopup)