@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.
- package/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/README.md +258 -0
- package/dist/allocation-policy-my_HfzdV.d.mts +23 -0
- package/dist/base-MWBqRFM2.mjs +16 -0
- package/dist/catalog-bridge-K8bdkncJ.d.mts +29 -0
- package/dist/cost-layer.port-iH9pvZqB.d.mts +30 -0
- package/dist/cost-layer.service-BQ1bs-XN.mjs +86 -0
- package/dist/cost-layer.service-DWmo9dQz.d.mts +53 -0
- package/dist/count.port-BRqwGbi3.d.mts +57 -0
- package/dist/counting/index.d.mts +2 -0
- package/dist/counting/index.mjs +2 -0
- package/dist/counting.service-BiQXqorv.mjs +232 -0
- package/dist/counting.service-CpAxU2G0.d.mts +74 -0
- package/dist/domain/contracts/index.d.mts +3 -0
- package/dist/domain/contracts/index.mjs +1 -0
- package/dist/domain/enums/index.d.mts +2 -0
- package/dist/domain/enums/index.mjs +4 -0
- package/dist/domain/index.d.mts +24 -0
- package/dist/domain/index.mjs +10 -0
- package/dist/domain/policies/index.d.mts +4 -0
- package/dist/domain/policies/index.mjs +1 -0
- package/dist/domain-D5cpMpR0.mjs +96 -0
- package/dist/domain-errors-D7S9ydNF.mjs +133 -0
- package/dist/enums-C3_z6aHC.mjs +82 -0
- package/dist/event-bus-BNmyoJb4.mjs +37 -0
- package/dist/event-bus-Um_xrcMY.d.mts +21 -0
- package/dist/event-emitter.port-BFh2pasY.d.mts +183 -0
- package/dist/event-types-BSqQOvXv.mjs +29 -0
- package/dist/events/index.d.mts +3 -0
- package/dist/events/index.mjs +3 -0
- package/dist/idempotency.port-CTC70JON.d.mts +55 -0
- package/dist/index-Bia4m8d2.d.mts +67 -0
- package/dist/index-BmNm3oNU2.d.mts +107 -0
- package/dist/index-C5PciI9P.d.mts +203 -0
- package/dist/index-CMTUKEK_.d.mts +308 -0
- package/dist/index-C_aEnozN.d.mts +220 -0
- package/dist/index-CulWO137.d.mts +107 -0
- package/dist/index-DFF0GJ4J.d.mts +36 -0
- package/dist/index-DsE7lZdO.d.mts +11 -0
- package/dist/index-DwO9IdNa.d.mts +1 -0
- package/dist/index-dtWUZr2a2.d.mts +350 -0
- package/dist/index.d.mts +128 -0
- package/dist/index.mjs +102 -0
- package/dist/insufficient-stock.error-Dyr4BYaV.mjs +15 -0
- package/dist/location.port-CValXIpb.d.mts +52 -0
- package/dist/lot.port-ChsmvZqs.d.mts +32 -0
- package/dist/models/index.d.mts +2 -0
- package/dist/models/index.mjs +2 -0
- package/dist/models-CHTMbp-G.mjs +1020 -0
- package/dist/move-group.port-DHGoQA3d.d.mts +56 -0
- package/dist/move-status-DkaFp2GD.mjs +38 -0
- package/dist/move.port-Qg1CYp7h.d.mts +89 -0
- package/dist/package.service-4tcAwBbr.mjs +95 -0
- package/dist/package.service-C605NaBQ.d.mts +42 -0
- package/dist/packaging/index.d.mts +2 -0
- package/dist/packaging/index.mjs +2 -0
- package/dist/procurement/index.d.mts +2 -0
- package/dist/procurement/index.mjs +2 -0
- package/dist/quant.port-BBa66PBT.d.mts +42 -0
- package/dist/removal-policy-BItBB8FD.d.mts +29 -0
- package/dist/replenishment-rule.port-DnEYtbyD.d.mts +78 -0
- package/dist/replenishment.service-BT9P-HKM.mjs +284 -0
- package/dist/replenishment.service-HO0sDhB_.d.mts +89 -0
- package/dist/reporting/index.d.mts +2 -0
- package/dist/reporting/index.mjs +2 -0
- package/dist/reporting-CL5ffrKM.mjs +243 -0
- package/dist/repositories/index.d.mts +2 -0
- package/dist/repositories/index.mjs +2 -0
- package/dist/repositories-nZXJKvLW.mjs +842 -0
- package/dist/reservation-status-ZfuTaWG0.mjs +22 -0
- package/dist/reservation.port-l9NFQ0si.d.mts +85 -0
- package/dist/reservations/index.d.mts +2 -0
- package/dist/reservations/index.mjs +2 -0
- package/dist/reservations-Cg4wN0QB.mjs +112 -0
- package/dist/routing/index.d.mts +362 -0
- package/dist/routing/index.mjs +582 -0
- package/dist/runtime-config-C0ggPkiK.mjs +40 -0
- package/dist/runtime-config-CQLtPPqY.d.mts +38 -0
- package/dist/scan-token-CNM9QVLY.d.mts +26 -0
- package/dist/scanning/index.d.mts +45 -0
- package/dist/scanning/index.mjs +228 -0
- package/dist/services/index.d.mts +8 -0
- package/dist/services/index.mjs +8 -0
- package/dist/services-_lLO4Xbl.mjs +1009 -0
- package/dist/stock-move-group-C0DqUfPY.mjs +88 -0
- package/dist/stock-package-BIarxbDS.d.mts +19 -0
- package/dist/stock-quant-CZhgvTu7.d.mts +41 -0
- package/dist/tenant-guard-6Ne-BILP.mjs +12 -0
- package/dist/tenant-isolation.error-D3OcKUdx.mjs +11 -0
- package/dist/trace.service-B9vAh-l-.d.mts +55 -0
- package/dist/trace.service-DE6Eh8_8.mjs +71 -0
- package/dist/traceability/index.d.mts +2 -0
- package/dist/traceability/index.mjs +2 -0
- package/dist/types/index.d.mts +2 -0
- package/dist/types/index.mjs +1 -0
- package/dist/unit-of-work.port-CWEkrDKu.d.mts +17 -0
- package/dist/valuation/index.d.mts +78 -0
- package/dist/valuation/index.mjs +103 -0
- package/dist/valuation-policy-Dco8c9Vw.d.mts +14 -0
- package/dist/virtual-locations-B9zXqPdi.d.mts +38 -0
- 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 };
|