@things-factory/operato-wms 4.3.752 → 4.3.755

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/client/pages/inbound/unload-product.js +30 -33
  2. package/client/pages/outbound/loading-product.js +2 -3
  3. package/client/pages/outbound/loading-v2/loading-execution-base.js +568 -56
  4. package/client/pages/outbound/loading-v2/loading-execution.js +258 -8
  5. package/client/pages/outbound/loading-v2/loading.js +82 -12
  6. package/client/pages/outbound/picking-product.js +11 -6
  7. package/client/pages/outbound/picking-v2/picking-execution-base.js +8 -8
  8. package/client/pages/outbound/picking-v2/picking-execution.js +75 -0
  9. package/client/pages/outbound/route-label-history-dialog.js +253 -0
  10. package/client/pages/outbound/route-label-popup.js +637 -131
  11. package/client/pages/outbound/show-ro-list-popup.js +8 -4
  12. package/client/pages/outbound/single-outbound-worksheet.js +55 -6
  13. package/client/pages/outbound/sorting-product.js +71 -25
  14. package/client/pages/outbound/zone-worksheet.js +62 -3
  15. package/dist-server/entities/index.js +9 -0
  16. package/dist-server/entities/index.js.map +1 -0
  17. package/dist-server/entities/route-label-sequence-counter.js +75 -0
  18. package/dist-server/entities/route-label-sequence-counter.js.map +1 -0
  19. package/dist-server/entities/route-label-sequence-log.js +74 -0
  20. package/dist-server/entities/route-label-sequence-log.js.map +1 -0
  21. package/dist-server/graphql/resolvers/index.js +3 -0
  22. package/dist-server/graphql/resolvers/index.js.map +1 -1
  23. package/dist-server/graphql/resolvers/outbound/complete-load.js +40 -3
  24. package/dist-server/graphql/resolvers/outbound/complete-load.js.map +1 -1
  25. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js +50 -11
  26. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.js.map +1 -1
  27. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good.js +90 -48
  28. package/dist-server/graphql/resolvers/outbound/find-loadable-release-good.js.map +1 -1
  29. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js +15 -7
  30. package/dist-server/graphql/resolvers/outbound/loading-worksheet-v2.js.map +1 -1
  31. package/dist-server/graphql/resolvers/outbound/sort-item.js +95 -16
  32. package/dist-server/graphql/resolvers/outbound/sort-item.js.map +1 -1
  33. package/dist-server/graphql/resolvers/route-label/index.js +7 -0
  34. package/dist-server/graphql/resolvers/route-label/index.js.map +1 -0
  35. package/dist-server/graphql/resolvers/route-label/route-label.js +220 -0
  36. package/dist-server/graphql/resolvers/route-label/route-label.js.map +1 -0
  37. package/dist-server/graphql/resolvers/zone-worksheet/index.js.map +1 -1
  38. package/dist-server/graphql/resolvers/zone-worksheet/zone-picking.js +123 -27
  39. package/dist-server/graphql/resolvers/zone-worksheet/zone-picking.js.map +1 -1
  40. package/dist-server/graphql/resolvers/zone-worksheet/zone-worksheet.js.map +1 -1
  41. package/dist-server/graphql/types/index.js +4 -0
  42. package/dist-server/graphql/types/index.js.map +1 -1
  43. package/dist-server/graphql/types/outbound/index.js +7 -1
  44. package/dist-server/graphql/types/outbound/index.js.map +1 -1
  45. package/dist-server/graphql/types/route-label/index.js +14 -0
  46. package/dist-server/graphql/types/route-label/index.js.map +1 -0
  47. package/dist-server/graphql/types/route-label/route-label-sequence.js +53 -0
  48. package/dist-server/graphql/types/route-label/route-label-sequence.js.map +1 -0
  49. package/dist-server/index.js +5 -0
  50. package/dist-server/index.js.map +1 -1
  51. package/dist-server/validators/setting-validators.js +519 -0
  52. package/dist-server/validators/setting-validators.js.map +1 -0
  53. package/helps/operato-wms/outbound/picking-product.en.md +156 -158
  54. package/package.json +50 -50
  55. package/server/entities/index.ts +6 -0
  56. package/server/entities/route-label-sequence-counter.ts +56 -0
  57. package/server/entities/route-label-sequence-log.ts +47 -0
  58. package/server/graphql/resolvers/index.ts +3 -0
  59. package/server/graphql/resolvers/outbound/complete-load.ts +42 -1
  60. package/server/graphql/resolvers/outbound/find-loadable-release-good-by-bin.ts +56 -13
  61. package/server/graphql/resolvers/outbound/find-loadable-release-good.ts +120 -57
  62. package/server/graphql/resolvers/outbound/loading-worksheet-v2.ts +9 -2
  63. package/server/graphql/resolvers/outbound/sort-item.ts +125 -21
  64. package/server/graphql/resolvers/route-label/index.ts +16 -0
  65. package/server/graphql/resolvers/route-label/route-label.ts +264 -0
  66. package/server/graphql/resolvers/zone-worksheet/index.ts +2 -2
  67. package/server/graphql/resolvers/zone-worksheet/zone-picking.ts +156 -39
  68. package/server/graphql/resolvers/zone-worksheet/zone-worksheet.ts +1 -2
  69. package/server/graphql/types/index.ts +4 -0
  70. package/server/graphql/types/outbound/index.ts +7 -1
  71. package/server/graphql/types/route-label/index.ts +18 -0
  72. package/server/graphql/types/route-label/route-label-sequence.ts +50 -0
  73. package/server/index.ts +7 -1
  74. package/server/validators/setting-validators.ts +668 -0
  75. package/translations/en.json +280 -252
  76. package/translations/ko.json +29 -1
  77. package/translations/ms.json +28 -0
  78. package/translations/zh.json +28 -0
@@ -1,12 +1,67 @@
1
- import { EntityManager } from 'typeorm'
2
- import { Brackets } from 'typeorm'
1
+ import { EntityManager, Brackets, IsNull, In, Not } from 'typeorm'
3
2
 
4
3
  import { User } from '@things-factory/auth-base'
5
4
  import { Domain } from '@things-factory/shell'
6
- import { ORDER_INVENTORY_STATUS, OrderInventory } from '@things-factory/sales-base'
5
+ import {
6
+ ORDER_INVENTORY_STATUS,
7
+ OrderInventory,
8
+ OrderNoGenerator,
9
+ OrderTote,
10
+ OrderToteItem,
11
+ ReleaseGood
12
+ } from '@things-factory/sales-base'
13
+ import { Tote, TOTE_STATUS } from '@things-factory/warehouse-base'
14
+
15
+ /**
16
+ * Calculates the available quantity that can still be sorted for an order inventory
17
+ */
18
+ function getAvailableQty(oi: OrderInventory): number {
19
+ const available = Math.min(oi.releaseQty, oi.pickedQty) - oi.sortedQty
20
+ return available > 0 ? available : 0
21
+ }
22
+
23
+ /**
24
+ * Creates or updates an order tote item with the given quantity
25
+ */
26
+ async function upsertOrderToteItem(
27
+ tx: EntityManager,
28
+ domain: Domain,
29
+ user: User,
30
+ orderTote: OrderTote,
31
+ orderInventory: OrderInventory,
32
+ qtyToAdd: number
33
+ ): Promise<void> {
34
+ const orderToteItemRepo = tx.getRepository(OrderToteItem)
35
+ const existingItem = (await orderToteItemRepo.findOne({
36
+ where: {
37
+ domain,
38
+ orderTote,
39
+ orderInventory,
40
+ orderProduct: orderInventory.orderProduct
41
+ }
42
+ })) as OrderToteItem | null
43
+
44
+ if (!existingItem) {
45
+ await orderToteItemRepo.save({
46
+ domain,
47
+ name: OrderNoGenerator.orderToteItem(),
48
+ orderTote,
49
+ orderInventory,
50
+ orderProduct: orderInventory.orderProduct,
51
+ qty: qtyToAdd,
52
+ updater: user
53
+ })
54
+ } else {
55
+ await orderToteItemRepo.update(existingItem.id, {
56
+ qty: Number(existingItem.qty ?? 0) + qtyToAdd,
57
+ updater: user,
58
+ updatedAt: new Date()
59
+ } as any)
60
+ }
61
+ }
7
62
 
8
63
  export const sortItem = {
9
- async sortItem(_: any, { releaseGoodNo, productBarcode, qty, worksheetDetails }, context: any) {
64
+ async sortItem(_: any, { releaseGoodNo, productBarcode, qty, worksheetDetails, toteNo }, context: any) {
10
65
  const { domain, user, tx }: { domain: Domain; user: User; tx: EntityManager } = context.state
11
66
 
12
67
  // Single query to get order inventories based on either releaseGood name or binLocation
@@ -15,21 +70,22 @@ export const sortItem = {
15
70
  .createQueryBuilder('orderInventory')
16
71
  .innerJoinAndSelect('orderInventory.productDetail', 'productDetail')
17
72
  .innerJoinAndSelect('productDetail.productBarcodes', 'productBarcode')
73
+ .leftJoinAndSelect('orderInventory.orderProduct', 'orderProduct')
18
74
  .leftJoinAndSelect('orderInventory.releaseGood', 'releaseGood')
75
+ .leftJoinAndSelect('releaseGood.bizplace', 'bizplace')
19
76
  .leftJoinAndSelect('orderInventory.binLocation', 'binLocation')
20
77
  .where('orderInventory.domain_id = :domainId', { domainId: domain.id })
21
78
  .andWhere('orderInventory.status IN (:...statuses)', {
22
- statuses: [
23
- ORDER_INVENTORY_STATUS.SORTING,
24
- ORDER_INVENTORY_STATUS.LOADING
25
- ]
79
+ statuses: [ORDER_INVENTORY_STATUS.SORTING, ORDER_INVENTORY_STATUS.LOADING]
26
80
  })
27
81
  .andWhere('productBarcode.gtin = :gtin', { gtin: productBarcode })
28
- .andWhere(new Brackets(qb => {
29
- qb
30
- .where('orderInventory.sortedQty < orderInventory.pickedQty')
31
- .andWhere('orderInventory.sortedQty < orderInventory.releaseQty')
32
- }))
82
+ .andWhere(
83
+ new Brackets(qb => {
84
+ qb.where('orderInventory.sortedQty < orderInventory.pickedQty').andWhere(
85
+ 'orderInventory.sortedQty < orderInventory.releaseQty'
86
+ )
87
+ })
88
+ )
33
89
  .andWhere(
34
90
  new Brackets(qb => {
35
91
  qb.where('releaseGood.name = :releaseGoodNo', { releaseGoodNo }).orWhere(
@@ -42,11 +98,55 @@ export const sortItem = {
42
98
 
43
99
  if (!orderInventories.length) throw new Error('No matching product barcode found')
44
100
 
45
- // Calculate total available quantity (minimum of releaseQty and pickedQty for each order inventory)
46
- const totalAvailableQty = orderInventories.reduce((sum, oi) => {
47
- const availableQty = Math.min(oi.releaseQty, oi.pickedQty) - oi.sortedQty
48
- return sum + (availableQty > 0 ? availableQty : 0)
49
- }, 0)
101
+ let resolvedReleaseGood: ReleaseGood | null = null
102
+ let resolvedOrderTote: OrderTote | null = null
103
+
104
+ if (toteNo) {
105
+ // Ensure we can identify a single release order when scanning by BIN
106
+ const uniqueReleaseGoodIds = [...new Set(orderInventories.map(oi => oi.releaseGoodId).filter(Boolean))]
107
+ if (uniqueReleaseGoodIds.length > 1) {
108
+ throw new Error('Multiple release orders detected. Please select a release order before tote scanning.')
109
+ }
110
+
111
+ resolvedReleaseGood = await tx.getRepository(ReleaseGood).findOne({
112
+ where: { domain, id: uniqueReleaseGoodIds[0] },
113
+ relations: ['bizplace']
114
+ })
115
+
116
+ if (!resolvedReleaseGood) {
117
+ throw new Error('Release good not found for tote scanning')
118
+ }
119
+
120
+ resolvedOrderTote = await tx.getRepository(OrderTote).findOne({
121
+ where: { domain, name: toteNo, releaseGood: resolvedReleaseGood }
122
+ })
123
+
124
+ if (!resolvedOrderTote) {
125
+ // Query for valid totes (NOT dispatched or damaged)
126
+ const foundTote: Tote = await tx.getRepository(Tote).findOne({
127
+ where: {
128
+ bizplace: resolvedReleaseGood.bizplace,
129
+ name: toteNo,
130
+ deletedAt: IsNull(),
131
+ status: Not(In([TOTE_STATUS.DISPATCHED, TOTE_STATUS.DAMAGED]))
132
+ }
133
+ })
134
+
135
+ resolvedOrderTote = await tx.getRepository(OrderTote).save({
136
+ name: toteNo,
137
+ domain,
138
+ releaseGood: resolvedReleaseGood,
139
+ tote: foundTote ?? null,
140
+ creator: user,
141
+ updater: user
142
+ })
143
+ } else if (resolvedOrderTote.closedDate) {
144
+ throw new Error('Tote has been sealed, please try another tote!')
145
+ }
146
+ }
147
+
148
+ // Calculate total available quantity using helper function
149
+ const totalAvailableQty = orderInventories.reduce((sum, oi) => sum + getAvailableQty(oi), 0)
50
150
 
51
151
  if (qty > totalAvailableQty) {
52
152
  throw new Error(`Sort qty (${qty}) exceeds total available quantity (${totalAvailableQty})`)
@@ -57,20 +157,24 @@ export const sortItem = {
57
157
  for (const orderInventory of orderInventories) {
58
158
  if (remainingQty <= 0) break
59
159
 
60
- const availableQty = Math.min(orderInventory.releaseQty, orderInventory.pickedQty) - orderInventory.sortedQty
160
+ const availableQty = getAvailableQty(orderInventory)
61
161
  if (availableQty <= 0) continue
62
162
 
63
163
  const qtyToAdd = Math.min(remainingQty, availableQty)
64
- const newSortQty = orderInventory.sortedQty + qtyToAdd
65
164
 
66
165
  // Update the order inventory
67
166
  await tx.getRepository(OrderInventory).update(orderInventory.id, {
68
- sortedQty: newSortQty,
167
+ sortedQty: orderInventory.sortedQty + qtyToAdd,
69
168
  status: ORDER_INVENTORY_STATUS.LOADING,
70
169
  updatedAt: new Date(),
71
170
  updater: user
72
171
  })
73
172
 
173
+ // Create / update order tote items to reflect tote allocation during QC
174
+ if (resolvedOrderTote) {
175
+ await upsertOrderToteItem(tx, domain, user, resolvedOrderTote, orderInventory, qtyToAdd)
176
+ }
177
+
74
178
  remainingQty -= qtyToAdd
75
179
  }
76
180
 
@@ -0,0 +1,16 @@
1
+ import {
2
+ reserveRouteLabelSequence,
3
+ routeLabelReleaseGoodsInExecutingWorksheet,
4
+ routeLabelSequenceLogs,
5
+ routeLabelSequenceStatuses
6
+ } from './route-label'
7
+
8
+ export const Query = {
9
+ ...routeLabelReleaseGoodsInExecutingWorksheet,
10
+ ...routeLabelSequenceStatuses,
11
+ ...routeLabelSequenceLogs
12
+ }
13
+
14
+ export const Mutation = {
15
+ ...reserveRouteLabelSequence
16
+ }
@@ -0,0 +1,264 @@
1
+ import { EntityManager, In } from 'typeorm'
2
+ import { Domain } from '@things-factory/biz-base'
3
+ import { WORKSHEET_STATUS } from '@things-factory/worksheet-base'
4
+ import { User } from '@things-factory/auth-base'
5
+
6
+ import { RouteLabelSequenceCounter } from '../../../entities/route-label-sequence-counter'
7
+ import { RouteLabelSequenceLog } from '../../../entities/route-label-sequence-log'
8
+
9
+ function ensureSeqField(field: string) {
10
+ if (!['refNo', 'refNo2', 'refNo3'].includes(field)) {
11
+ throw new Error('Invalid sequence field. Allowed: refNo, refNo2, refNo3')
12
+ }
13
+ }
14
+
15
+ function parseNonNegativeInt(value: any): number {
16
+ const n = parseInt(value ?? '0', 10)
17
+ return Number.isFinite(n) ? Math.max(0, n) : 0
18
+ }
19
+
20
+ export const routeLabelReleaseGoodsInExecutingWorksheet = {
21
+ async routeLabelReleaseGoodsInExecutingWorksheet(_: any, { partnerId, refNo, worksheetType, seqField }, context: any) {
22
+ const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
23
+
24
+ let filterRefNo = ''
25
+ if (refNo) {
26
+ const removeSymbol = refNo
27
+ .split('')
28
+ .filter(res => res !== '%')
29
+ .join('')
30
+
31
+ const filterInfos = removeSymbol
32
+ .toLowerCase()
33
+ .split(',')
34
+ .map(value => "'%" + value.trim().replace(/'/g, "''") + "%'")
35
+ .join(',')
36
+
37
+ filterRefNo = ` and (
38
+ lower(rg.ref_no) like any(array[${filterInfos}])
39
+ or lower(rg.ref_no_2) like any(array[${filterInfos}])
40
+ or lower(rg.ref_no_3) like any(array[${filterInfos}])
41
+ )`
42
+ }
43
+
44
+ const rows = await tx.query(
45
+ `
46
+ select distinct
47
+ rg.id as "id",
48
+ rg.name as "name",
49
+ rg.ref_no as "refNo",
50
+ rg.ref_no_2 as "refNo2",
51
+ rg.ref_no_3 as "refNo3",
52
+ rg.route_id as "routeId",
53
+ rg.store_id as "storeId",
54
+ rg.store_name as "storeName",
55
+ rg.stop_id as "stopId",
56
+ rg.created_at as "createdAt"
57
+ from worksheets ws
58
+ inner join worksheet_details wsd on wsd.worksheet_id = ws.id
59
+ inner join order_inventories oi on oi.id = wsd.target_inventory_id
60
+ inner join release_goods rg on rg.id = oi.release_good_id
61
+ where ws.domain_id = $1
62
+ and ws.type = $2
63
+ and ws.status = $3
64
+ and rg.domain_id = $1
65
+ and rg.bizplace_id = $4
66
+ ${filterRefNo}
67
+ order by rg.created_at desc
68
+ `,
69
+ [domain.id, worksheetType, WORKSHEET_STATUS.EXECUTING, partnerId]
70
+ )
71
+
72
+ const releaseGoods = rows || []
73
+
74
+ // If seqField is provided, fetch and merge sequence data
75
+ if (seqField && ['refNo', 'refNo2', 'refNo3'].includes(seqField) && releaseGoods.length > 0) {
76
+ const releaseGoodIds = releaseGoods.map((rg: any) => rg.id)
77
+
78
+ // Fetch lastSeq from counters
79
+ const lastSeqById = new Map<string, number>()
80
+ const counterRepo = tx.getRepository(RouteLabelSequenceCounter)
81
+ const counters = await counterRepo.find({
82
+ where: { domain, releaseGoodId: In(releaseGoodIds), field: seqField }
83
+ })
84
+ for (const c of counters) {
85
+ lastSeqById.set(c.releaseGoodId, parseNonNegativeInt(c.lastSeq))
86
+ }
87
+
88
+ // Fetch lastPrintedBy from logs (most recent per releaseGood)
89
+ const lastPrintedById = new Map<string, string>()
90
+ const logRepo = tx.getRepository(RouteLabelSequenceLog)
91
+ const logs = await logRepo.find({
92
+ where: { domain, releaseGoodId: In(releaseGoodIds), field: seqField },
93
+ relations: ['creator'],
94
+ order: { createdAt: 'DESC' },
95
+ take: 500
96
+ })
97
+
98
+ for (const log of logs) {
99
+ if (!lastPrintedById.has(log.releaseGoodId) && log.creator) {
100
+ lastPrintedById.set(log.releaseGoodId, log.creator.name || '')
101
+ }
102
+ }
103
+
104
+ // Merge sequence data into release goods
105
+ return releaseGoods.map((rg: any) => ({
106
+ ...rg,
107
+ lastSeq: lastSeqById.get(rg.id) || 0,
108
+ lastPrintedBy: lastPrintedById.get(rg.id) || null
109
+ }))
110
+ }
111
+
112
+ return releaseGoods
113
+ }
114
+ }
115
+
116
+ export const routeLabelSequenceStatuses = {
117
+ async routeLabelSequenceStatuses(_: any, { releaseGoodIds, field }, context: any) {
118
+ const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
119
+ ensureSeqField(field)
120
+
121
+ const lastSeqById = new Map<string, number>()
122
+ const counterRepo = tx.getRepository(RouteLabelSequenceCounter)
123
+ const counters = await counterRepo.find({
124
+ where: { domain, releaseGoodId: In(releaseGoodIds), field }
125
+ })
126
+ for (const c of counters) {
127
+ lastSeqById.set(c.releaseGoodId, parseNonNegativeInt(c.lastSeq))
128
+ }
129
+
130
+ const histById = new Map<string, string[]>()
131
+ const lastPrintedById = new Map<string, string>()
132
+ const logRepo = tx.getRepository(RouteLabelSequenceLog)
133
+ const logs = await logRepo.find({
134
+ where: { domain, releaseGoodId: In(releaseGoodIds), field },
135
+ relations: ['creator'],
136
+ order: { createdAt: 'DESC' },
137
+ take: 500
138
+ })
139
+
140
+ for (const log of logs) {
141
+ const list = histById.get(log.releaseGoodId) || []
142
+ if (list.length >= 5) continue
143
+ list.push(`${log.from}-${log.to}`)
144
+ histById.set(log.releaseGoodId, list)
145
+
146
+ // Store the creator name from the most recent log entry (first one due to DESC order)
147
+ if (!lastPrintedById.has(log.releaseGoodId) && log.creator) {
148
+ lastPrintedById.set(log.releaseGoodId, log.creator.name || '')
149
+ }
150
+ }
151
+
152
+ return releaseGoodIds.map((releaseGoodId: string) => {
153
+ return {
154
+ releaseGoodId,
155
+ field,
156
+ lastSeq: lastSeqById.get(releaseGoodId) || 0,
157
+ history: histById.get(releaseGoodId) || [],
158
+ lastPrintedBy: lastPrintedById.get(releaseGoodId) || null
159
+ }
160
+ })
161
+ }
162
+ }
163
+
164
+ export const reserveRouteLabelSequence = {
165
+ async reserveRouteLabelSequence(_: any, { releaseGoodId, field, qty }, context: any) {
166
+ const { domain, tx, user }: { domain: Domain; tx: EntityManager; user: User } = context.state
167
+ ensureSeqField(field)
168
+
169
+ const q = parseNonNegativeInt(qty)
170
+ if (q <= 0) {
171
+ throw new Error('Print qty must be greater than 0')
172
+ }
173
+
174
+ const counterRepo = tx.getRepository(RouteLabelSequenceCounter)
175
+ const logRepo = tx.getRepository(RouteLabelSequenceLog)
176
+
177
+ // Lock counter row to avoid duplicated sequences under concurrent prints.
178
+ let counter = await counterRepo
179
+ .createQueryBuilder('c')
180
+ .setLock('pessimistic_write')
181
+ .where('c.domain = :domainId and c.releaseGoodId = :releaseGoodId and c.field = :field', {
182
+ domainId: domain.id,
183
+ releaseGoodId,
184
+ field
185
+ })
186
+ .getOne()
187
+
188
+ if (!counter) {
189
+ try {
190
+ await counterRepo.save({
191
+ domain,
192
+ releaseGoodId,
193
+ field,
194
+ lastSeq: 0,
195
+ creator: user,
196
+ updater: user
197
+ })
198
+ } catch (e) {
199
+ // ignore: concurrent insert can violate unique index
200
+ }
201
+
202
+ counter = await counterRepo
203
+ .createQueryBuilder('c')
204
+ .setLock('pessimistic_write')
205
+ .where('c.domain = :domainId and c.releaseGoodId = :releaseGoodId and c.field = :field', {
206
+ domainId: domain.id,
207
+ releaseGoodId,
208
+ field
209
+ })
210
+ .getOne()
211
+ }
212
+
213
+ if (!counter) {
214
+ throw new Error('Failed to initialize route label sequence counter')
215
+ }
216
+
217
+ const last = parseNonNegativeInt(counter.lastSeq)
218
+ const from = last + 1
219
+ const to = last + q
220
+
221
+ await counterRepo.update(counter.id, { lastSeq: to, updater: user, updatedAt: new Date() })
222
+ await logRepo.save({ domain, releaseGoodId, field, from, to, qty: q, creator: user })
223
+
224
+ return { releaseGoodId, field, from, to }
225
+ }
226
+ }
227
+
228
+ export const routeLabelSequenceLogs = {
229
+ async routeLabelSequenceLogs(_: any, { releaseGoodId, field }, context: any) {
230
+ const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
231
+ ensureSeqField(field)
232
+
233
+ const rows =
234
+ (await tx.query(
235
+ `
236
+ select
237
+ l.id as "id",
238
+ l.release_good_id as "releaseGoodId",
239
+ l.field as "field",
240
+ l."from" as "from",
241
+ l."to" as "to",
242
+ l.qty as "qty",
243
+ l.created_at as "createdAt",
244
+ u.name as "creatorName"
245
+ from route_label_sequence_logs l
246
+ left join users u on u.id = l.creator_id
247
+ where l.domain_id = $1
248
+ and l.release_good_id = $2
249
+ and l.field = $3
250
+ order by l.created_at desc
251
+ limit 200
252
+ `,
253
+ [domain.id, releaseGoodId, field]
254
+ )) || []
255
+
256
+ return rows.map((row: any) => {
257
+ const createdAt = row.createdAt ? new Date(row.createdAt) : null
258
+ return {
259
+ ...row,
260
+ createdAt: createdAt ? createdAt.toISOString() : null
261
+ }
262
+ })
263
+ }
264
+ }
@@ -1,5 +1,5 @@
1
1
  import { zonePickingWorksheet, zoneWorksheetOverview } from './zone-worksheet'
2
- import { generateWorksheetZonePicking } from './generate-zone-worksheet'
2
+ import { generateWorksheetZonePicking } from './generate-zone-worksheet'
3
3
  import { activateBulkWorksheetZonePicking, activateWorksheetZonePicking } from './activate-zone-worksheet'
4
4
  import { zonePicking } from './zone-picking'
5
5
  import { assignSorter } from './assign-sorter'
@@ -7,7 +7,7 @@ import { getZonePickingTask } from './get-zone-picking-task'
7
7
 
8
8
  export const Query = {
9
9
  ...zoneWorksheetOverview,
10
- ...zonePickingWorksheet,
10
+ ...zonePickingWorksheet
11
11
  }
12
12
 
13
13
  export const Mutation = {