@classytic/flow 0.1.4

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 (102) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/README.md +258 -0
  4. package/dist/allocation-policy-my_HfzdV.d.mts +23 -0
  5. package/dist/base-MWBqRFM2.mjs +16 -0
  6. package/dist/catalog-bridge-K8bdkncJ.d.mts +29 -0
  7. package/dist/cost-layer.port-iH9pvZqB.d.mts +30 -0
  8. package/dist/cost-layer.service-BQ1bs-XN.mjs +86 -0
  9. package/dist/cost-layer.service-DWmo9dQz.d.mts +53 -0
  10. package/dist/count.port-BRqwGbi3.d.mts +57 -0
  11. package/dist/counting/index.d.mts +2 -0
  12. package/dist/counting/index.mjs +2 -0
  13. package/dist/counting.service-BiQXqorv.mjs +232 -0
  14. package/dist/counting.service-CpAxU2G0.d.mts +74 -0
  15. package/dist/domain/contracts/index.d.mts +3 -0
  16. package/dist/domain/contracts/index.mjs +1 -0
  17. package/dist/domain/enums/index.d.mts +2 -0
  18. package/dist/domain/enums/index.mjs +4 -0
  19. package/dist/domain/index.d.mts +24 -0
  20. package/dist/domain/index.mjs +10 -0
  21. package/dist/domain/policies/index.d.mts +4 -0
  22. package/dist/domain/policies/index.mjs +1 -0
  23. package/dist/domain-D5cpMpR0.mjs +96 -0
  24. package/dist/domain-errors-D7S9ydNF.mjs +133 -0
  25. package/dist/enums-C3_z6aHC.mjs +82 -0
  26. package/dist/event-bus-BNmyoJb4.mjs +37 -0
  27. package/dist/event-bus-Um_xrcMY.d.mts +21 -0
  28. package/dist/event-emitter.port-BFh2pasY.d.mts +183 -0
  29. package/dist/event-types-BSqQOvXv.mjs +29 -0
  30. package/dist/events/index.d.mts +3 -0
  31. package/dist/events/index.mjs +3 -0
  32. package/dist/idempotency.port-CTC70JON.d.mts +55 -0
  33. package/dist/index-Bia4m8d2.d.mts +67 -0
  34. package/dist/index-BmNm3oNU2.d.mts +107 -0
  35. package/dist/index-C5PciI9P.d.mts +203 -0
  36. package/dist/index-CMTUKEK_.d.mts +308 -0
  37. package/dist/index-C_aEnozN.d.mts +220 -0
  38. package/dist/index-CulWO137.d.mts +107 -0
  39. package/dist/index-DFF0GJ4J.d.mts +36 -0
  40. package/dist/index-DsE7lZdO.d.mts +11 -0
  41. package/dist/index-DwO9IdNa.d.mts +1 -0
  42. package/dist/index-dtWUZr2a2.d.mts +350 -0
  43. package/dist/index.d.mts +128 -0
  44. package/dist/index.mjs +102 -0
  45. package/dist/insufficient-stock.error-Dyr4BYaV.mjs +15 -0
  46. package/dist/location.port-CValXIpb.d.mts +52 -0
  47. package/dist/lot.port-ChsmvZqs.d.mts +32 -0
  48. package/dist/models/index.d.mts +2 -0
  49. package/dist/models/index.mjs +2 -0
  50. package/dist/models-CHTMbp-G.mjs +1020 -0
  51. package/dist/move-group.port-DHGoQA3d.d.mts +56 -0
  52. package/dist/move-status-DkaFp2GD.mjs +38 -0
  53. package/dist/move.port-Qg1CYp7h.d.mts +89 -0
  54. package/dist/package.service-4tcAwBbr.mjs +95 -0
  55. package/dist/package.service-C605NaBQ.d.mts +42 -0
  56. package/dist/packaging/index.d.mts +2 -0
  57. package/dist/packaging/index.mjs +2 -0
  58. package/dist/procurement/index.d.mts +2 -0
  59. package/dist/procurement/index.mjs +2 -0
  60. package/dist/quant.port-BBa66PBT.d.mts +42 -0
  61. package/dist/removal-policy-BItBB8FD.d.mts +29 -0
  62. package/dist/replenishment-rule.port-DnEYtbyD.d.mts +78 -0
  63. package/dist/replenishment.service-BT9P-HKM.mjs +284 -0
  64. package/dist/replenishment.service-HO0sDhB_.d.mts +89 -0
  65. package/dist/reporting/index.d.mts +2 -0
  66. package/dist/reporting/index.mjs +2 -0
  67. package/dist/reporting-CL5ffrKM.mjs +243 -0
  68. package/dist/repositories/index.d.mts +2 -0
  69. package/dist/repositories/index.mjs +2 -0
  70. package/dist/repositories-nZXJKvLW.mjs +842 -0
  71. package/dist/reservation-status-ZfuTaWG0.mjs +22 -0
  72. package/dist/reservation.port-l9NFQ0si.d.mts +85 -0
  73. package/dist/reservations/index.d.mts +2 -0
  74. package/dist/reservations/index.mjs +2 -0
  75. package/dist/reservations-Cg4wN0QB.mjs +112 -0
  76. package/dist/routing/index.d.mts +362 -0
  77. package/dist/routing/index.mjs +582 -0
  78. package/dist/runtime-config-C0ggPkiK.mjs +40 -0
  79. package/dist/runtime-config-CQLtPPqY.d.mts +38 -0
  80. package/dist/scan-token-CNM9QVLY.d.mts +26 -0
  81. package/dist/scanning/index.d.mts +45 -0
  82. package/dist/scanning/index.mjs +228 -0
  83. package/dist/services/index.d.mts +8 -0
  84. package/dist/services/index.mjs +8 -0
  85. package/dist/services-_lLO4Xbl.mjs +1009 -0
  86. package/dist/stock-move-group-C0DqUfPY.mjs +88 -0
  87. package/dist/stock-package-BIarxbDS.d.mts +19 -0
  88. package/dist/stock-quant-CZhgvTu7.d.mts +41 -0
  89. package/dist/tenant-guard-6Ne-BILP.mjs +12 -0
  90. package/dist/tenant-isolation.error-D3OcKUdx.mjs +11 -0
  91. package/dist/trace.service-B9vAh-l-.d.mts +55 -0
  92. package/dist/trace.service-DE6Eh8_8.mjs +71 -0
  93. package/dist/traceability/index.d.mts +2 -0
  94. package/dist/traceability/index.mjs +2 -0
  95. package/dist/types/index.d.mts +2 -0
  96. package/dist/types/index.mjs +1 -0
  97. package/dist/unit-of-work.port-CWEkrDKu.d.mts +17 -0
  98. package/dist/valuation/index.d.mts +78 -0
  99. package/dist/valuation/index.mjs +103 -0
  100. package/dist/valuation-policy-Dco8c9Vw.d.mts +14 -0
  101. package/dist/virtual-locations-B9zXqPdi.d.mts +38 -0
  102. package/package.json +155 -0
@@ -0,0 +1,842 @@
1
+ import { h as ValidationError } from "./domain-errors-D7S9ydNF.mjs";
2
+ import { AggregationBuilder, createRepository } from "@classytic/mongokit";
3
+ import { Types } from "mongoose";
4
+ //#region src/repositories/types.ts
5
+ /**
6
+ * Cast the opaque TransactionSession to Mongoose ClientSession.
7
+ * This is the one unavoidable branded-type cast in the repo layer — the port
8
+ * uses an opaque type so services never touch mongoose directly.
9
+ */
10
+ function toSession(session) {
11
+ return session;
12
+ }
13
+ /** Returns true if the string is a valid MongoDB ObjectId. */
14
+ function isValidId(id) {
15
+ return Types.ObjectId.isValid(id);
16
+ }
17
+ /** Convert FlowContext organizationId (string) to ObjectId for MongoDB queries. */
18
+ function toOrgId(ctx) {
19
+ return new Types.ObjectId(ctx.organizationId);
20
+ }
21
+ /**
22
+ * Extract docs from MongoKit's PaginationResult or raw array.
23
+ * - Paginated result (offset, keyset, aggregate): returns `.docs`
24
+ * - Raw array (from noPagination: true / findAll): returns as-is
25
+ */
26
+ function extractDocs(result) {
27
+ if (Array.isArray(result)) return result;
28
+ return result.docs;
29
+ }
30
+ /**
31
+ * Narrow a PaginationResult to its offset variant with full metadata.
32
+ * Safe to call when you know the query used offset pagination (page param).
33
+ */
34
+ function asOffsetResult(result) {
35
+ return result;
36
+ }
37
+ //#endregion
38
+ //#region src/repositories/cost-layer.repository.ts
39
+ var CostLayerRepository = class {
40
+ repo;
41
+ constructor(model, plugins = []) {
42
+ this.repo = createRepository(model, plugins);
43
+ }
44
+ async create(input, session) {
45
+ return this.repo.create({ ...input }, {
46
+ session: toSession(session),
47
+ organizationId: input.organizationId
48
+ });
49
+ }
50
+ async drain(id, quantity, ctx, session) {
51
+ return this.repo.update(id, { $inc: { remainingQty: -quantity } }, {
52
+ session: toSession(session),
53
+ organizationId: ctx.organizationId,
54
+ lean: true
55
+ });
56
+ }
57
+ async findBySkuRef(skuRef, locationId, ctx, session) {
58
+ return this.repo.findAll({
59
+ skuRef,
60
+ locationId,
61
+ organizationId: toOrgId(ctx)
62
+ }, {
63
+ session: toSession(session),
64
+ lean: true,
65
+ organizationId: ctx.organizationId
66
+ });
67
+ }
68
+ async findMany(filter, ctx, session, options) {
69
+ return this.repo.findAll({
70
+ ...filter,
71
+ organizationId: toOrgId(ctx)
72
+ }, {
73
+ session: toSession(session),
74
+ lean: true,
75
+ organizationId: ctx.organizationId,
76
+ ...options
77
+ });
78
+ }
79
+ async findOrderedForConsumption(skuRef, locationId, method, ctx, session) {
80
+ const sortField = method === "fifo" ? "receivedAt" : "expiresAt";
81
+ return this.repo.findAll({
82
+ skuRef,
83
+ locationId,
84
+ remainingQty: { $gt: 0 },
85
+ organizationId: toOrgId(ctx)
86
+ }, {
87
+ session: toSession(session),
88
+ lean: true,
89
+ sort: { [sortField]: 1 }
90
+ });
91
+ }
92
+ };
93
+ //#endregion
94
+ //#region src/repositories/count.repository.ts
95
+ var CountRepository = class {
96
+ countRepo;
97
+ lineRepo;
98
+ constructor(countModel, lineModel, plugins = []) {
99
+ this.countRepo = createRepository(countModel, plugins);
100
+ this.lineRepo = createRepository(lineModel, plugins);
101
+ }
102
+ async create(input, session) {
103
+ return this.countRepo.create({ ...input }, {
104
+ session: toSession(session),
105
+ organizationId: input.organizationId
106
+ });
107
+ }
108
+ async findById(id, ctx, session) {
109
+ if (!isValidId(id)) return null;
110
+ return this.countRepo.getByQuery({
111
+ _id: id,
112
+ organizationId: toOrgId(ctx)
113
+ }, {
114
+ session: toSession(session),
115
+ lean: true,
116
+ throwOnNotFound: false
117
+ });
118
+ }
119
+ async updateStatus(id, status, updates, session) {
120
+ return this.countRepo.update(id, {
121
+ ...updates,
122
+ status
123
+ }, {
124
+ session: toSession(session),
125
+ lean: true
126
+ });
127
+ }
128
+ async submitLines(countId, lines, session) {
129
+ const enriched = lines.map((line) => ({
130
+ ...line,
131
+ countId
132
+ }));
133
+ return this.lineRepo.createMany(enriched, { session: toSession(session) });
134
+ }
135
+ async getLines(countId, ctx, session) {
136
+ return this.lineRepo.findAll({
137
+ countId,
138
+ organizationId: toOrgId(ctx)
139
+ }, {
140
+ session: toSession(session),
141
+ lean: true,
142
+ sort: { createdAt: -1 },
143
+ organizationId: ctx.organizationId
144
+ });
145
+ }
146
+ };
147
+ //#endregion
148
+ //#region src/domain/entities/location.ts
149
+ /**
150
+ * Validate that adding a parent doesn't create a cycle.
151
+ * Pass the full location list for the node to check hierarchy.
152
+ */
153
+ function validateLocationHierarchy(locationId, parentLocationId, allLocations) {
154
+ if (locationId === parentLocationId) throw new ValidationError(`Location ${locationId} cannot be its own parent`);
155
+ const visited = /* @__PURE__ */ new Set();
156
+ let current = parentLocationId;
157
+ while (current) {
158
+ if (current === locationId) throw new ValidationError(`Circular reference: setting parent would create cycle involving ${locationId}`);
159
+ if (visited.has(current)) break;
160
+ visited.add(current);
161
+ current = allLocations.find((l) => l._id === current)?.parentLocationId;
162
+ }
163
+ }
164
+ //#endregion
165
+ //#region src/repositories/location.repository.ts
166
+ var LocationRepository = class {
167
+ repo;
168
+ constructor(model, plugins = []) {
169
+ this.repo = createRepository(model, plugins);
170
+ }
171
+ async findById(id, ctx, session) {
172
+ if (!isValidId(id)) return null;
173
+ return this.repo.getByQuery({
174
+ _id: id,
175
+ organizationId: toOrgId(ctx)
176
+ }, {
177
+ session: toSession(session),
178
+ lean: true,
179
+ throwOnNotFound: false
180
+ });
181
+ }
182
+ async findByNode(nodeId, ctx, session) {
183
+ return this.repo.findAll({
184
+ nodeId,
185
+ organizationId: toOrgId(ctx)
186
+ }, {
187
+ session: toSession(session),
188
+ lean: true,
189
+ sort: { sortOrder: 1 },
190
+ organizationId: ctx.organizationId
191
+ });
192
+ }
193
+ async findByBarcode(barcode, ctx, session) {
194
+ return this.repo.getByQuery({
195
+ barcode,
196
+ organizationId: toOrgId(ctx)
197
+ }, {
198
+ session: toSession(session),
199
+ lean: true,
200
+ throwOnNotFound: false
201
+ });
202
+ }
203
+ async findByCode(code, nodeId, ctx, session) {
204
+ return this.repo.getByQuery({
205
+ code,
206
+ nodeId,
207
+ organizationId: toOrgId(ctx)
208
+ }, {
209
+ session: toSession(session),
210
+ lean: true,
211
+ throwOnNotFound: false
212
+ });
213
+ }
214
+ async getTree(nodeId, ctx, session) {
215
+ return this.repo.findAll({
216
+ nodeId,
217
+ organizationId: toOrgId(ctx)
218
+ }, {
219
+ session: toSession(session),
220
+ lean: true,
221
+ sort: { sortOrder: 1 },
222
+ organizationId: ctx.organizationId
223
+ });
224
+ }
225
+ async create(input, session) {
226
+ if (input.parentLocationId) {
227
+ const inputWithId = input;
228
+ if (inputWithId._id && input.parentLocationId === String(inputWithId._id)) throw new Error("Location cannot be its own parent");
229
+ }
230
+ return this.repo.create({ ...input }, {
231
+ session: toSession(session),
232
+ organizationId: input.organizationId
233
+ });
234
+ }
235
+ async update(id, updates, ctx, session) {
236
+ if (updates.parentLocationId) {
237
+ const current = await this.findById(id, ctx, session);
238
+ if (current) {
239
+ const allLocations = await this.getTree(current.nodeId, ctx, session);
240
+ validateLocationHierarchy(id, updates.parentLocationId, allLocations);
241
+ }
242
+ }
243
+ return this.repo.update(id, { ...updates }, {
244
+ session: toSession(session),
245
+ organizationId: ctx.organizationId,
246
+ lean: true
247
+ });
248
+ }
249
+ };
250
+ //#endregion
251
+ //#region src/repositories/lot.repository.ts
252
+ var LotRepository = class {
253
+ repo;
254
+ constructor(model, plugins = []) {
255
+ this.repo = createRepository(model, plugins);
256
+ }
257
+ async findById(id, ctx, session) {
258
+ if (!isValidId(id)) return null;
259
+ return this.repo.getByQuery({
260
+ _id: id,
261
+ organizationId: toOrgId(ctx)
262
+ }, {
263
+ session: toSession(session),
264
+ lean: true,
265
+ throwOnNotFound: false
266
+ });
267
+ }
268
+ async findByCode(lotCode, skuRef, ctx, session) {
269
+ const query = {
270
+ lotCode,
271
+ organizationId: toOrgId(ctx)
272
+ };
273
+ if (skuRef) query.skuRef = skuRef;
274
+ return this.repo.getByQuery(query, {
275
+ session: toSession(session),
276
+ lean: true,
277
+ throwOnNotFound: false
278
+ });
279
+ }
280
+ async findBySerial(serialCode, skuRef, ctx, session) {
281
+ const query = {
282
+ serialCode,
283
+ organizationId: toOrgId(ctx)
284
+ };
285
+ if (skuRef) query.skuRef = skuRef;
286
+ return this.repo.getByQuery(query, {
287
+ session: toSession(session),
288
+ lean: true,
289
+ throwOnNotFound: false
290
+ });
291
+ }
292
+ async findMany(filter, ctx, session, options) {
293
+ return this.repo.findAll({
294
+ ...filter,
295
+ organizationId: toOrgId(ctx)
296
+ }, {
297
+ session: toSession(session),
298
+ lean: true,
299
+ organizationId: ctx.organizationId,
300
+ ...options
301
+ });
302
+ }
303
+ async create(input, session) {
304
+ return this.repo.create({ ...input }, {
305
+ session: toSession(session),
306
+ organizationId: input.organizationId
307
+ });
308
+ }
309
+ async update(id, updates, ctx, session) {
310
+ return this.repo.update(id, { ...updates }, {
311
+ session: toSession(session),
312
+ organizationId: ctx.organizationId,
313
+ lean: true
314
+ });
315
+ }
316
+ };
317
+ //#endregion
318
+ //#region src/repositories/move.repository.ts
319
+ var MoveRepository = class {
320
+ repo;
321
+ constructor(model, plugins = []) {
322
+ this.repo = createRepository(model, plugins);
323
+ }
324
+ async create(input, session) {
325
+ return this.repo.create({ ...input }, {
326
+ session: toSession(session),
327
+ organizationId: input.organizationId
328
+ });
329
+ }
330
+ async findById(id, ctx, session) {
331
+ if (!isValidId(id)) return null;
332
+ return this.repo.getByQuery({
333
+ _id: id,
334
+ organizationId: toOrgId(ctx)
335
+ }, {
336
+ session: toSession(session),
337
+ lean: true,
338
+ throwOnNotFound: false
339
+ });
340
+ }
341
+ async findByGroupId(groupId, ctx, session) {
342
+ return this.repo.findAll({
343
+ moveGroupId: groupId,
344
+ organizationId: toOrgId(ctx)
345
+ }, {
346
+ session: toSession(session),
347
+ lean: true,
348
+ organizationId: ctx.organizationId
349
+ });
350
+ }
351
+ async updateStatus(id, status, updates, session) {
352
+ return this.repo.update(id, {
353
+ ...updates,
354
+ status
355
+ }, {
356
+ session: toSession(session),
357
+ lean: true
358
+ });
359
+ }
360
+ async findMany(filter, ctx, session, options) {
361
+ return this.repo.findAll({
362
+ ...filter,
363
+ organizationId: toOrgId(ctx)
364
+ }, {
365
+ session: toSession(session),
366
+ lean: true,
367
+ organizationId: ctx.organizationId,
368
+ ...options
369
+ });
370
+ }
371
+ };
372
+ //#endregion
373
+ //#region src/repositories/move-group.repository.ts
374
+ var MoveGroupRepository = class {
375
+ repo;
376
+ constructor(model, plugins = []) {
377
+ this.repo = createRepository(model, plugins);
378
+ }
379
+ async create(input, session) {
380
+ return this.repo.create({ ...input }, {
381
+ session: toSession(session),
382
+ organizationId: input.organizationId
383
+ });
384
+ }
385
+ async findById(id, ctx, session) {
386
+ if (!isValidId(id)) return null;
387
+ return this.repo.getByQuery({
388
+ _id: id,
389
+ organizationId: toOrgId(ctx)
390
+ }, {
391
+ session: toSession(session),
392
+ lean: true,
393
+ throwOnNotFound: false
394
+ });
395
+ }
396
+ async updateStatus(id, status, updates, session) {
397
+ return this.repo.update(id, {
398
+ ...updates,
399
+ status
400
+ }, {
401
+ session: toSession(session),
402
+ lean: true
403
+ });
404
+ }
405
+ async list(query, ctx, session) {
406
+ const page = query.page ?? 1;
407
+ const pageSize = query.pageSize ?? 20;
408
+ const sort = query.sort ?? { createdAt: -1 };
409
+ const filters = {
410
+ ...query.filter,
411
+ organizationId: toOrgId(ctx)
412
+ };
413
+ const offsetResult = await this.repo.getAll({
414
+ filters,
415
+ sort,
416
+ page,
417
+ limit: pageSize
418
+ }, {
419
+ session: toSession(session),
420
+ lean: true,
421
+ organizationId: ctx.organizationId
422
+ });
423
+ return {
424
+ data: offsetResult.docs,
425
+ total: offsetResult.total,
426
+ page: offsetResult.page,
427
+ pageSize,
428
+ hasMore: offsetResult.hasNext
429
+ };
430
+ }
431
+ };
432
+ //#endregion
433
+ //#region src/repositories/node.repository.ts
434
+ var NodeRepository = class {
435
+ repo;
436
+ constructor(model, plugins = []) {
437
+ this.repo = createRepository(model, plugins);
438
+ }
439
+ async findById(id, ctx, session) {
440
+ if (!isValidId(id)) return null;
441
+ return this.repo.getByQuery({
442
+ _id: id,
443
+ organizationId: toOrgId(ctx)
444
+ }, {
445
+ session: toSession(session),
446
+ lean: true,
447
+ throwOnNotFound: false
448
+ });
449
+ }
450
+ async findDefault(ctx, session) {
451
+ return this.repo.getByQuery({
452
+ isDefault: true,
453
+ organizationId: toOrgId(ctx)
454
+ }, {
455
+ session: toSession(session),
456
+ lean: true,
457
+ throwOnNotFound: false
458
+ });
459
+ }
460
+ async list(ctx, session) {
461
+ return this.repo.findAll({ organizationId: toOrgId(ctx) }, {
462
+ session: toSession(session),
463
+ lean: true,
464
+ organizationId: ctx.organizationId
465
+ });
466
+ }
467
+ async create(input, session) {
468
+ return this.repo.create({ ...input }, {
469
+ session: toSession(session),
470
+ organizationId: input.organizationId
471
+ });
472
+ }
473
+ async update(id, updates, ctx, session) {
474
+ return this.repo.update(id, { ...updates }, {
475
+ session: toSession(session),
476
+ organizationId: ctx.organizationId,
477
+ lean: true
478
+ });
479
+ }
480
+ };
481
+ //#endregion
482
+ //#region src/repositories/procurement.repository.ts
483
+ var ProcurementRepository = class {
484
+ repo;
485
+ constructor(model, plugins = []) {
486
+ this.repo = createRepository(model, plugins);
487
+ }
488
+ async create(input, session) {
489
+ return this.repo.create({ ...input }, {
490
+ session: toSession(session),
491
+ organizationId: input.organizationId
492
+ });
493
+ }
494
+ async findById(id, ctx, session) {
495
+ if (!isValidId(id)) return null;
496
+ return this.repo.getByQuery({
497
+ _id: id,
498
+ organizationId: toOrgId(ctx)
499
+ }, {
500
+ session: toSession(session),
501
+ lean: true,
502
+ throwOnNotFound: false
503
+ });
504
+ }
505
+ async updateStatus(id, status, updates, session) {
506
+ return this.repo.update(id, {
507
+ ...updates,
508
+ status
509
+ }, {
510
+ session: toSession(session),
511
+ lean: true
512
+ });
513
+ }
514
+ async findMany(filter, ctx, session, options) {
515
+ return this.repo.findAll({
516
+ ...filter,
517
+ organizationId: toOrgId(ctx)
518
+ }, {
519
+ session: toSession(session),
520
+ lean: true,
521
+ organizationId: ctx.organizationId,
522
+ ...options
523
+ });
524
+ }
525
+ async list(query, ctx, session) {
526
+ const page = query.page ?? 1;
527
+ const pageSize = query.pageSize ?? 20;
528
+ const sort = query.sort ?? { createdAt: -1 };
529
+ const filters = {
530
+ ...query.filter,
531
+ organizationId: toOrgId(ctx)
532
+ };
533
+ const offsetResult = await this.repo.getAll({
534
+ filters,
535
+ sort,
536
+ page,
537
+ limit: pageSize
538
+ }, {
539
+ session: toSession(session),
540
+ lean: true,
541
+ organizationId: ctx.organizationId
542
+ });
543
+ return {
544
+ data: offsetResult.docs,
545
+ total: offsetResult.total,
546
+ page: offsetResult.page,
547
+ pageSize,
548
+ hasMore: offsetResult.hasNext
549
+ };
550
+ }
551
+ };
552
+ //#endregion
553
+ //#region src/repositories/quant.repository.ts
554
+ function toObjectId(value) {
555
+ return new Types.ObjectId(value);
556
+ }
557
+ var QuantRepository = class {
558
+ repo;
559
+ constructor(model, plugins = []) {
560
+ this.repo = createRepository(model, plugins);
561
+ }
562
+ async getAvailability(query, ctx, session) {
563
+ const match = { organizationId: toObjectId(ctx.organizationId) };
564
+ if (query.skuRef) match.skuRef = query.skuRef;
565
+ if (query.locationId) match.locationId = query.locationId;
566
+ if (query.lotId) match.lotId = typeof query.lotId === "string" ? toObjectId(query.lotId) : query.lotId;
567
+ if (query.stockStatus) match.stockStatus = query.stockStatus;
568
+ if (query.nodeId) {
569
+ const locationIds = (await this.repo.Model.db.collection("locations").find({
570
+ organizationId: toObjectId(ctx.organizationId),
571
+ nodeId: toObjectId(query.nodeId)
572
+ }, { projection: { _id: 1 } }).toArray()).map((l) => l._id.toString());
573
+ if (query.locationId) if (locationIds.includes(query.locationId)) match.locationId = query.locationId;
574
+ else match.locationId = { $in: [] };
575
+ else match.locationId = { $in: locationIds };
576
+ }
577
+ const pipeline = new AggregationBuilder().match(match).group({
578
+ _id: null,
579
+ quantityOnHand: { $sum: "$quantityOnHand" },
580
+ quantityReserved: { $sum: "$quantityReserved" },
581
+ quantityAvailable: { $sum: "$quantityAvailable" },
582
+ quantityIncoming: { $sum: { $ifNull: ["$quantityIncoming", 0] } },
583
+ quantityOutgoing: { $sum: { $ifNull: ["$quantityOutgoing", 0] } },
584
+ breakdowns: { $push: "$$ROOT" }
585
+ }).build();
586
+ const [result] = await this.repo.aggregate(pipeline, {
587
+ session: toSession(session),
588
+ organizationId: ctx.organizationId
589
+ });
590
+ if (!result) return {
591
+ quantityOnHand: 0,
592
+ quantityReserved: 0,
593
+ quantityAvailable: 0,
594
+ quantityIncoming: 0,
595
+ quantityOutgoing: 0,
596
+ breakdowns: []
597
+ };
598
+ return {
599
+ quantityOnHand: result.quantityOnHand,
600
+ quantityReserved: result.quantityReserved,
601
+ quantityAvailable: result.quantityAvailable,
602
+ quantityIncoming: result.quantityIncoming,
603
+ quantityOutgoing: result.quantityOutgoing,
604
+ breakdowns: result.breakdowns
605
+ };
606
+ }
607
+ async upsert(update, session) {
608
+ const filter = {
609
+ organizationId: update.organizationId,
610
+ skuRef: update.skuRef,
611
+ locationId: update.locationId
612
+ };
613
+ if (update.lotId != null) filter.lotId = update.lotId;
614
+ else filter.lotId = null;
615
+ if (update.ownerRef != null) filter.ownerRef = update.ownerRef;
616
+ else filter.ownerRef = null;
617
+ const setStage = {
618
+ quantityOnHand: { $add: [{ $ifNull: ["$quantityOnHand", 0] }, update.quantityDelta] },
619
+ quantityReserved: { $add: [{ $ifNull: ["$quantityReserved", 0] }, update.reservedDelta ?? 0] },
620
+ lastMovementAt: /* @__PURE__ */ new Date()
621
+ };
622
+ if (update.stockStatus) setStage.stockStatus = update.stockStatus;
623
+ if (update.unitCost !== void 0) setStage.unitCost = update.unitCost;
624
+ if (update.inDate) setStage.inDate = { $min: [{ $ifNull: ["$inDate", update.inDate] }, update.inDate] };
625
+ const pipeline = [{ $set: setStage }, { $set: { quantityAvailable: { $subtract: ["$quantityOnHand", "$quantityReserved"] } } }];
626
+ return await this.repo.Model.findOneAndUpdate(filter, pipeline, {
627
+ upsert: true,
628
+ returnDocument: "after",
629
+ session: toSession(session),
630
+ updatePipeline: true
631
+ }).lean().exec();
632
+ }
633
+ async batchUpsert(updates, session) {
634
+ if (updates.length === 0) return;
635
+ const ops = updates.map((update) => {
636
+ const filter = {
637
+ organizationId: update.organizationId,
638
+ skuRef: update.skuRef,
639
+ locationId: update.locationId,
640
+ lotId: update.lotId ?? null,
641
+ ownerRef: update.ownerRef ?? null
642
+ };
643
+ const setStage = {
644
+ quantityOnHand: { $add: [{ $ifNull: ["$quantityOnHand", 0] }, update.quantityDelta] },
645
+ quantityReserved: { $add: [{ $ifNull: ["$quantityReserved", 0] }, update.reservedDelta ?? 0] },
646
+ lastMovementAt: /* @__PURE__ */ new Date()
647
+ };
648
+ if (update.stockStatus) setStage.stockStatus = update.stockStatus;
649
+ if (update.unitCost !== void 0) setStage.unitCost = update.unitCost;
650
+ if (update.inDate) setStage.inDate = { $min: [{ $ifNull: ["$inDate", update.inDate] }, update.inDate] };
651
+ return { updateOne: {
652
+ filter,
653
+ update: [{ $set: setStage }, { $set: { quantityAvailable: { $subtract: ["$quantityOnHand", "$quantityReserved"] } } }],
654
+ upsert: true
655
+ } };
656
+ });
657
+ await this.repo.Model.bulkWrite(ops, {
658
+ session: toSession(session),
659
+ ordered: false
660
+ });
661
+ }
662
+ async findByIdentity(identity, session) {
663
+ return this.repo.getByQuery({
664
+ organizationId: new Types.ObjectId(identity.organizationId),
665
+ skuRef: identity.skuRef,
666
+ locationId: identity.locationId,
667
+ lotId: identity.lotId ?? null,
668
+ ownerRef: identity.ownerRef ?? null
669
+ }, {
670
+ session: toSession(session),
671
+ lean: true,
672
+ throwOnNotFound: false
673
+ });
674
+ }
675
+ async findMany(filter, ctx, session, options) {
676
+ return this.repo.findAll({
677
+ ...filter,
678
+ organizationId: toOrgId(ctx)
679
+ }, {
680
+ session: toSession(session),
681
+ lean: true,
682
+ organizationId: ctx.organizationId,
683
+ ...options
684
+ });
685
+ }
686
+ async deleteAll(ctx, session) {
687
+ await this.repo.Model.deleteMany({ organizationId: toObjectId(ctx.organizationId) }).session(toSession(session) ?? null).exec();
688
+ }
689
+ };
690
+ //#endregion
691
+ //#region src/repositories/replenishment-rule.repository.ts
692
+ var ReplenishmentRuleRepository = class {
693
+ repo;
694
+ constructor(model, plugins = []) {
695
+ this.repo = createRepository(model, plugins);
696
+ }
697
+ async findByNode(nodeId, ctx, session) {
698
+ return this.repo.findAll({
699
+ scopeRef: nodeId,
700
+ scopeType: "node",
701
+ organizationId: toOrgId(ctx)
702
+ }, {
703
+ session: toSession(session),
704
+ lean: true,
705
+ organizationId: ctx.organizationId
706
+ });
707
+ }
708
+ async findBySkuRef(skuRef, ctx, session) {
709
+ return this.repo.findAll({
710
+ skuRef,
711
+ organizationId: toOrgId(ctx)
712
+ }, {
713
+ session: toSession(session),
714
+ lean: true,
715
+ organizationId: ctx.organizationId
716
+ });
717
+ }
718
+ async list(ctx, session) {
719
+ return this.repo.findAll({ organizationId: toOrgId(ctx) }, {
720
+ session: toSession(session),
721
+ lean: true,
722
+ organizationId: ctx.organizationId
723
+ });
724
+ }
725
+ async findById(id, ctx, session) {
726
+ if (!isValidId(id)) return null;
727
+ return this.repo.getByQuery({
728
+ _id: id,
729
+ organizationId: toOrgId(ctx)
730
+ }, {
731
+ session: toSession(session),
732
+ lean: true,
733
+ throwOnNotFound: false
734
+ });
735
+ }
736
+ async delete(id, ctx, session) {
737
+ await this.repo.delete(id, {
738
+ session: toSession(session),
739
+ organizationId: ctx.organizationId
740
+ });
741
+ }
742
+ async findMany(filter, ctx, session, options) {
743
+ return this.repo.findAll({
744
+ ...filter,
745
+ organizationId: toOrgId(ctx)
746
+ }, {
747
+ session: toSession(session),
748
+ lean: true,
749
+ organizationId: ctx.organizationId,
750
+ ...options
751
+ });
752
+ }
753
+ async create(input, session) {
754
+ return this.repo.create({ ...input }, {
755
+ session: toSession(session),
756
+ organizationId: input.organizationId
757
+ });
758
+ }
759
+ async update(id, updates, ctx, session) {
760
+ return this.repo.update(id, { ...updates }, {
761
+ session: toSession(session),
762
+ organizationId: ctx.organizationId,
763
+ lean: true
764
+ });
765
+ }
766
+ };
767
+ //#endregion
768
+ //#region src/repositories/reservation.repository.ts
769
+ var ReservationRepository = class {
770
+ repo;
771
+ constructor(model, plugins = []) {
772
+ this.repo = createRepository(model, plugins);
773
+ }
774
+ async create(input, session) {
775
+ return this.repo.create({ ...input }, {
776
+ session: toSession(session),
777
+ organizationId: input.organizationId
778
+ });
779
+ }
780
+ async findById(id, ctx, session) {
781
+ if (!isValidId(id)) return null;
782
+ return this.repo.getByQuery({
783
+ _id: id,
784
+ organizationId: toOrgId(ctx)
785
+ }, {
786
+ session: toSession(session),
787
+ lean: true,
788
+ throwOnNotFound: false
789
+ });
790
+ }
791
+ async findByOwner(ownerType, ownerId, ctx, session) {
792
+ return this.repo.findAll({
793
+ ownerType,
794
+ ownerId,
795
+ organizationId: toOrgId(ctx)
796
+ }, {
797
+ session: toSession(session),
798
+ lean: true,
799
+ organizationId: ctx.organizationId
800
+ });
801
+ }
802
+ async updateStatus(id, status, updates, session) {
803
+ return this.repo.update(id, {
804
+ ...updates,
805
+ status
806
+ }, {
807
+ session: toSession(session),
808
+ lean: true
809
+ });
810
+ }
811
+ async findExpired(asOf, ctx, session) {
812
+ return this.repo.findAll({
813
+ status: "active",
814
+ expiresAt: { $lt: asOf },
815
+ organizationId: toOrgId(ctx)
816
+ }, {
817
+ session: toSession(session),
818
+ lean: true,
819
+ organizationId: ctx.organizationId
820
+ });
821
+ }
822
+ };
823
+ //#endregion
824
+ //#region src/repositories/index.ts
825
+ function createRepositories(models, plugins) {
826
+ const p = plugins ?? {};
827
+ return {
828
+ quant: new QuantRepository(models.StockQuant, p.quant),
829
+ move: new MoveRepository(models.StockMove, p.move),
830
+ moveGroup: new MoveGroupRepository(models.StockMoveGroup, p.moveGroup),
831
+ reservation: new ReservationRepository(models.Reservation, p.reservation),
832
+ location: new LocationRepository(models.Location, p.location),
833
+ node: new NodeRepository(models.InventoryNode, p.node),
834
+ lot: new LotRepository(models.StockLot, p.lot),
835
+ costLayer: new CostLayerRepository(models.CostLayer, p.costLayer),
836
+ procurement: new ProcurementRepository(models.ProcurementOrder, p.procurement),
837
+ count: new CountRepository(models.InventoryCount, models.CountLine, p.count),
838
+ replenishmentRule: new ReplenishmentRuleRepository(models.ReplenishmentRule, p.replenishmentRule)
839
+ };
840
+ }
841
+ //#endregion
842
+ export { ProcurementRepository as a, MoveRepository as c, CountRepository as d, CostLayerRepository as f, toSession as g, toOrgId as h, QuantRepository as i, LotRepository as l, extractDocs as m, ReservationRepository as n, NodeRepository as o, asOffsetResult as p, ReplenishmentRuleRepository as r, MoveGroupRepository as s, createRepositories as t, LocationRepository as u };