@iservice365/module-hygiene 1.0.2 → 1.1.0
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 +13 -0
- package/dist/index.d.ts +118 -8
- package/dist/index.js +665 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +674 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -644,7 +644,7 @@ function useAreaController() {
|
|
|
644
644
|
return;
|
|
645
645
|
}
|
|
646
646
|
const page = parseInt(req.query.page) ?? 1;
|
|
647
|
-
const limit = parseInt(req.query.limit) ??
|
|
647
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
648
648
|
const search = req.query.search ?? "";
|
|
649
649
|
const site = req.params.site ?? "";
|
|
650
650
|
try {
|
|
@@ -1193,7 +1193,7 @@ function useUnitController() {
|
|
|
1193
1193
|
return;
|
|
1194
1194
|
}
|
|
1195
1195
|
const page = parseInt(req.query.page) ?? 1;
|
|
1196
|
-
const limit = parseInt(req.query.limit) ??
|
|
1196
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
1197
1197
|
const search = req.query.search ?? "";
|
|
1198
1198
|
const site = req.params.site ?? "";
|
|
1199
1199
|
try {
|
|
@@ -1607,7 +1607,7 @@ function useParentChecklistController() {
|
|
|
1607
1607
|
return;
|
|
1608
1608
|
}
|
|
1609
1609
|
const page = parseInt(req.query.page) ?? 1;
|
|
1610
|
-
const limit = parseInt(req.query.limit) ??
|
|
1610
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
1611
1611
|
const search = req.query.search ?? "";
|
|
1612
1612
|
const site = req.params.site ?? "";
|
|
1613
1613
|
const startDate = req.query.startDate ?? "";
|
|
@@ -2626,7 +2626,7 @@ function useAreaChecklistController() {
|
|
|
2626
2626
|
return;
|
|
2627
2627
|
}
|
|
2628
2628
|
const page = parseInt(req.query.page) ?? 1;
|
|
2629
|
-
const limit = parseInt(req.query.limit) ??
|
|
2629
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
2630
2630
|
const search = req.query.search ?? "";
|
|
2631
2631
|
const type = req.query.type ?? "";
|
|
2632
2632
|
const schedule = req.params.schedule ?? "";
|
|
@@ -2664,7 +2664,7 @@ function useAreaChecklistController() {
|
|
|
2664
2664
|
return;
|
|
2665
2665
|
}
|
|
2666
2666
|
const page = parseInt(req.query.page) ?? 1;
|
|
2667
|
-
const limit = parseInt(req.query.limit) ??
|
|
2667
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
2668
2668
|
const search = req.query.search ?? "";
|
|
2669
2669
|
const type = req.query.type ?? "";
|
|
2670
2670
|
const schedule = req.params.schedule ?? "";
|
|
@@ -2722,7 +2722,7 @@ function useAreaChecklistController() {
|
|
|
2722
2722
|
return;
|
|
2723
2723
|
}
|
|
2724
2724
|
const page = parseInt(req.query.page) ?? 1;
|
|
2725
|
-
const limit = parseInt(req.query.limit) ??
|
|
2725
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
2726
2726
|
const search = req.query.search ?? "";
|
|
2727
2727
|
const _id = req.params.id ?? "";
|
|
2728
2728
|
try {
|
|
@@ -2797,8 +2797,7 @@ import { ObjectId as ObjectId9 } from "mongodb";
|
|
|
2797
2797
|
var supplySchema = Joi9.object({
|
|
2798
2798
|
site: Joi9.string().hex().required(),
|
|
2799
2799
|
name: Joi9.string().required(),
|
|
2800
|
-
unitOfMeasurement: Joi9.string().required()
|
|
2801
|
-
qty: Joi9.number().min(0).required()
|
|
2800
|
+
unitOfMeasurement: Joi9.string().required()
|
|
2802
2801
|
});
|
|
2803
2802
|
function MSupply(value) {
|
|
2804
2803
|
const { error } = supplySchema.validate(value);
|
|
@@ -2817,9 +2816,9 @@ function MSupply(value) {
|
|
|
2817
2816
|
site: value.site,
|
|
2818
2817
|
name: value.name,
|
|
2819
2818
|
unitOfMeasurement: value.unitOfMeasurement,
|
|
2820
|
-
qty:
|
|
2821
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
2819
|
+
qty: 0,
|
|
2822
2820
|
status: "active",
|
|
2821
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2823
2822
|
updatedAt: "",
|
|
2824
2823
|
deletedAt: ""
|
|
2825
2824
|
};
|
|
@@ -2952,7 +2951,7 @@ function useSupplyRepository() {
|
|
|
2952
2951
|
throw error;
|
|
2953
2952
|
}
|
|
2954
2953
|
}
|
|
2955
|
-
async function getSupplyById(_id) {
|
|
2954
|
+
async function getSupplyById(_id, session) {
|
|
2956
2955
|
try {
|
|
2957
2956
|
_id = new ObjectId10(_id);
|
|
2958
2957
|
} catch (error) {
|
|
@@ -2965,10 +2964,14 @@ function useSupplyRepository() {
|
|
|
2965
2964
|
const cacheKey = makeCacheKey5(namespace_collection, {
|
|
2966
2965
|
_id: _id.toString()
|
|
2967
2966
|
});
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2967
|
+
if (!session) {
|
|
2968
|
+
const cachedData = await getCache(cacheKey);
|
|
2969
|
+
if (cachedData) {
|
|
2970
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2971
|
+
return cachedData;
|
|
2972
|
+
}
|
|
2973
|
+
} else {
|
|
2974
|
+
logger17.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
2972
2975
|
}
|
|
2973
2976
|
try {
|
|
2974
2977
|
const data = await collection.aggregate([
|
|
@@ -3116,7 +3119,7 @@ function useSupplyController() {
|
|
|
3116
3119
|
return;
|
|
3117
3120
|
}
|
|
3118
3121
|
const page = parseInt(req.query.page) ?? 1;
|
|
3119
|
-
const limit = parseInt(req.query.limit) ??
|
|
3122
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
3120
3123
|
const search = req.query.search ?? "";
|
|
3121
3124
|
const site = req.params.site ?? "";
|
|
3122
3125
|
try {
|
|
@@ -3207,18 +3210,667 @@ function useSupplyController() {
|
|
|
3207
3210
|
deleteSupply
|
|
3208
3211
|
};
|
|
3209
3212
|
}
|
|
3213
|
+
|
|
3214
|
+
// src/models/hygiene-stock.model.ts
|
|
3215
|
+
import { BadRequestError as BadRequestError18, logger as logger19 } from "@iservice365/node-server-utils";
|
|
3216
|
+
import Joi11 from "joi";
|
|
3217
|
+
import { ObjectId as ObjectId11 } from "mongodb";
|
|
3218
|
+
var stockSchema = Joi11.object({
|
|
3219
|
+
site: Joi11.string().hex().required(),
|
|
3220
|
+
supply: Joi11.string().hex().required(),
|
|
3221
|
+
in: Joi11.number().min(0).optional(),
|
|
3222
|
+
out: Joi11.number().min(0).optional(),
|
|
3223
|
+
balance: Joi11.number().min(0).required(),
|
|
3224
|
+
remarks: Joi11.string().optional().allow("", null)
|
|
3225
|
+
});
|
|
3226
|
+
function MStock(value) {
|
|
3227
|
+
const { error } = stockSchema.validate(value);
|
|
3228
|
+
if (error) {
|
|
3229
|
+
logger19.info(`Hygiene Stock Model: ${error.message}`);
|
|
3230
|
+
throw new BadRequestError18(error.message);
|
|
3231
|
+
}
|
|
3232
|
+
if (value.site) {
|
|
3233
|
+
try {
|
|
3234
|
+
value.site = new ObjectId11(value.site);
|
|
3235
|
+
} catch (error2) {
|
|
3236
|
+
throw new BadRequestError18("Invalid site ID format.");
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
if (value.supply) {
|
|
3240
|
+
try {
|
|
3241
|
+
value.supply = new ObjectId11(value.supply);
|
|
3242
|
+
} catch (error2) {
|
|
3243
|
+
throw new BadRequestError18("Invalid supply ID format.");
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
return {
|
|
3247
|
+
site: value.site,
|
|
3248
|
+
supply: value.supply,
|
|
3249
|
+
in: value.in ?? 0,
|
|
3250
|
+
out: value.out ?? 0,
|
|
3251
|
+
balance: value.balance,
|
|
3252
|
+
remarks: value.remarks ?? "",
|
|
3253
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3254
|
+
status: "active",
|
|
3255
|
+
updatedAt: "",
|
|
3256
|
+
deletedAt: ""
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// src/repositories/hygiene-stock.repository.ts
|
|
3261
|
+
import { ObjectId as ObjectId12 } from "mongodb";
|
|
3262
|
+
import {
|
|
3263
|
+
useAtlas as useAtlas8,
|
|
3264
|
+
InternalServerError as InternalServerError6,
|
|
3265
|
+
BadRequestError as BadRequestError19,
|
|
3266
|
+
useCache as useCache6,
|
|
3267
|
+
logger as logger20,
|
|
3268
|
+
makeCacheKey as makeCacheKey6,
|
|
3269
|
+
paginate as paginate6
|
|
3270
|
+
} from "@iservice365/node-server-utils";
|
|
3271
|
+
function useStockRepository() {
|
|
3272
|
+
const db = useAtlas8.getDb();
|
|
3273
|
+
if (!db) {
|
|
3274
|
+
throw new InternalServerError6("Unable to connect to server.");
|
|
3275
|
+
}
|
|
3276
|
+
const namespace_collection = "site.supply.stocks";
|
|
3277
|
+
const supply_collection = "site.supplies";
|
|
3278
|
+
const collection = db.collection(namespace_collection);
|
|
3279
|
+
const { delNamespace, setCache, getCache } = useCache6(namespace_collection);
|
|
3280
|
+
const { delNamespace: delSupplyNamespace } = useCache6(supply_collection);
|
|
3281
|
+
async function createIndex() {
|
|
3282
|
+
try {
|
|
3283
|
+
await collection.createIndexes([
|
|
3284
|
+
{ key: { site: 1 } },
|
|
3285
|
+
{ key: { supply: 1 } },
|
|
3286
|
+
{ key: { balance: 1 } },
|
|
3287
|
+
{ key: { status: 1 } }
|
|
3288
|
+
]);
|
|
3289
|
+
} catch (error) {
|
|
3290
|
+
throw new InternalServerError6("Failed to create index on hygiene stock.");
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
async function createStock(value, session) {
|
|
3294
|
+
try {
|
|
3295
|
+
value = MStock(value);
|
|
3296
|
+
const res = await collection.insertOne(value, { session });
|
|
3297
|
+
delNamespace().then(() => {
|
|
3298
|
+
logger20.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3299
|
+
}).catch((err) => {
|
|
3300
|
+
logger20.error(
|
|
3301
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3302
|
+
err
|
|
3303
|
+
);
|
|
3304
|
+
});
|
|
3305
|
+
delSupplyNamespace().then(() => {
|
|
3306
|
+
logger20.info(`Cache cleared for namespace: ${supply_collection}`);
|
|
3307
|
+
}).catch((err) => {
|
|
3308
|
+
logger20.error(
|
|
3309
|
+
`Failed to clear cache for namespace: ${supply_collection}`,
|
|
3310
|
+
err
|
|
3311
|
+
);
|
|
3312
|
+
});
|
|
3313
|
+
return res.insertedId;
|
|
3314
|
+
} catch (error) {
|
|
3315
|
+
throw error;
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
async function getStocksBySupplyId({
|
|
3319
|
+
page = 1,
|
|
3320
|
+
limit = 10,
|
|
3321
|
+
search = "",
|
|
3322
|
+
site,
|
|
3323
|
+
supply
|
|
3324
|
+
}) {
|
|
3325
|
+
page = page > 0 ? page - 1 : 0;
|
|
3326
|
+
const query = {
|
|
3327
|
+
status: { $ne: "deleted" }
|
|
3328
|
+
};
|
|
3329
|
+
const cacheOptions = {
|
|
3330
|
+
page,
|
|
3331
|
+
limit
|
|
3332
|
+
};
|
|
3333
|
+
try {
|
|
3334
|
+
site = new ObjectId12(site);
|
|
3335
|
+
query.site = site;
|
|
3336
|
+
cacheOptions.site = site.toString();
|
|
3337
|
+
} catch (error) {
|
|
3338
|
+
throw new BadRequestError19("Invalid site ID format.");
|
|
3339
|
+
}
|
|
3340
|
+
try {
|
|
3341
|
+
supply = new ObjectId12(supply);
|
|
3342
|
+
query.supply = supply;
|
|
3343
|
+
cacheOptions.supply = supply.toString();
|
|
3344
|
+
} catch (error) {
|
|
3345
|
+
throw new BadRequestError19("Invalid supply ID format.");
|
|
3346
|
+
}
|
|
3347
|
+
if (search) {
|
|
3348
|
+
query.$text = { $search: search };
|
|
3349
|
+
cacheOptions.search = search;
|
|
3350
|
+
}
|
|
3351
|
+
const cacheKey = makeCacheKey6(namespace_collection, cacheOptions);
|
|
3352
|
+
const cachedData = await getCache(cacheKey);
|
|
3353
|
+
if (cachedData) {
|
|
3354
|
+
logger20.info(`Cache hit for key: ${cacheKey}`);
|
|
3355
|
+
return cachedData;
|
|
3356
|
+
}
|
|
3357
|
+
try {
|
|
3358
|
+
const items = await collection.aggregate([
|
|
3359
|
+
{ $match: query },
|
|
3360
|
+
{
|
|
3361
|
+
$project: {
|
|
3362
|
+
createdAt: 1,
|
|
3363
|
+
in: 1,
|
|
3364
|
+
out: 1,
|
|
3365
|
+
balance: 1
|
|
3366
|
+
}
|
|
3367
|
+
},
|
|
3368
|
+
{ $sort: { _id: 1 } },
|
|
3369
|
+
{ $skip: page * limit },
|
|
3370
|
+
{ $limit: limit }
|
|
3371
|
+
]).toArray();
|
|
3372
|
+
const length = await collection.countDocuments(query);
|
|
3373
|
+
const data = paginate6(items, page, limit, length);
|
|
3374
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3375
|
+
logger20.info(`Cache set for key: ${cacheKey}`);
|
|
3376
|
+
}).catch((err) => {
|
|
3377
|
+
logger20.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3378
|
+
});
|
|
3379
|
+
return data;
|
|
3380
|
+
} catch (error) {
|
|
3381
|
+
throw error;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return {
|
|
3385
|
+
createIndex,
|
|
3386
|
+
createStock,
|
|
3387
|
+
getStocksBySupplyId
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
// src/services/hygiene-stock.service.ts
|
|
3392
|
+
import {
|
|
3393
|
+
NotFoundError as NotFoundError5,
|
|
3394
|
+
useAtlas as useAtlas9,
|
|
3395
|
+
BadRequestError as BadRequestError20
|
|
3396
|
+
} from "@iservice365/node-server-utils";
|
|
3397
|
+
function useStockService() {
|
|
3398
|
+
const { createStock: _createStock } = useStockRepository();
|
|
3399
|
+
const { getSupplyById, updateSupply } = useSupplyRepository();
|
|
3400
|
+
async function createStock(value, out = false) {
|
|
3401
|
+
const session = useAtlas9.getClient()?.startSession();
|
|
3402
|
+
try {
|
|
3403
|
+
session?.startTransaction();
|
|
3404
|
+
const { qty, ...stockData } = value;
|
|
3405
|
+
const supply = await getSupplyById(value.supply, session);
|
|
3406
|
+
if (!supply || supply.qty === void 0) {
|
|
3407
|
+
throw new NotFoundError5("Supply not found.");
|
|
3408
|
+
}
|
|
3409
|
+
const newSupplyQty = out ? supply.qty - qty : supply.qty + qty;
|
|
3410
|
+
if (out && newSupplyQty < 0) {
|
|
3411
|
+
throw new BadRequestError20(
|
|
3412
|
+
`Insufficient stock. Available: ${supply.qty}, Requested: ${qty}`
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
await updateSupply(value.supply, { qty: newSupplyQty }, session);
|
|
3416
|
+
const createdStock = await _createStock(
|
|
3417
|
+
{
|
|
3418
|
+
...stockData,
|
|
3419
|
+
in: out ? 0 : qty,
|
|
3420
|
+
out: out ? qty : 0,
|
|
3421
|
+
balance: newSupplyQty
|
|
3422
|
+
},
|
|
3423
|
+
session
|
|
3424
|
+
);
|
|
3425
|
+
await session?.commitTransaction();
|
|
3426
|
+
return createdStock;
|
|
3427
|
+
} catch (error) {
|
|
3428
|
+
await session?.abortTransaction();
|
|
3429
|
+
throw error;
|
|
3430
|
+
} finally {
|
|
3431
|
+
await session?.endSession();
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
return { createStock };
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
// src/controllers/hygiene-stock.controller.ts
|
|
3438
|
+
import { BadRequestError as BadRequestError21, logger as logger21 } from "@iservice365/node-server-utils";
|
|
3439
|
+
import Joi12 from "joi";
|
|
3440
|
+
function useStockController() {
|
|
3441
|
+
const { getStocksBySupplyId: _getStocksBySupplyId } = useStockRepository();
|
|
3442
|
+
const { createStock: _createStock } = useStockService();
|
|
3443
|
+
async function createStock(req, res, next) {
|
|
3444
|
+
const payload = { ...req.body, ...req.params };
|
|
3445
|
+
const validation = Joi12.object({
|
|
3446
|
+
site: Joi12.string().hex().required(),
|
|
3447
|
+
supply: Joi12.string().hex().required(),
|
|
3448
|
+
qty: Joi12.number().min(0).required(),
|
|
3449
|
+
remarks: Joi12.string().optional().allow("", null)
|
|
3450
|
+
});
|
|
3451
|
+
const { error } = validation.validate(payload);
|
|
3452
|
+
if (error) {
|
|
3453
|
+
logger21.log({ level: "error", message: error.message });
|
|
3454
|
+
next(new BadRequestError21(error.message));
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
try {
|
|
3458
|
+
const id = await _createStock(payload);
|
|
3459
|
+
res.status(201).json({ message: "Stock created successfully.", id });
|
|
3460
|
+
return;
|
|
3461
|
+
} catch (error2) {
|
|
3462
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3463
|
+
next(error2);
|
|
3464
|
+
return;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
async function getStocksBySupplyId(req, res, next) {
|
|
3468
|
+
const query = { ...req.query, ...req.params };
|
|
3469
|
+
const validation = Joi12.object({
|
|
3470
|
+
page: Joi12.number().min(1).optional().allow("", null),
|
|
3471
|
+
limit: Joi12.number().min(1).optional().allow("", null),
|
|
3472
|
+
search: Joi12.string().optional().allow("", null),
|
|
3473
|
+
site: Joi12.string().hex().required(),
|
|
3474
|
+
supply: Joi12.string().hex().required()
|
|
3475
|
+
});
|
|
3476
|
+
const { error } = validation.validate(query);
|
|
3477
|
+
if (error) {
|
|
3478
|
+
logger21.log({ level: "error", message: error.message });
|
|
3479
|
+
next(new BadRequestError21(error.message));
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3482
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
3483
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
3484
|
+
const search = req.query.search ?? "";
|
|
3485
|
+
const site = req.params.site ?? "";
|
|
3486
|
+
const supply = req.params.supply ?? "";
|
|
3487
|
+
try {
|
|
3488
|
+
const data = await _getStocksBySupplyId({
|
|
3489
|
+
page,
|
|
3490
|
+
limit,
|
|
3491
|
+
search,
|
|
3492
|
+
site,
|
|
3493
|
+
supply
|
|
3494
|
+
});
|
|
3495
|
+
res.json(data);
|
|
3496
|
+
return;
|
|
3497
|
+
} catch (error2) {
|
|
3498
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3499
|
+
next(error2);
|
|
3500
|
+
return;
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
return {
|
|
3504
|
+
createStock,
|
|
3505
|
+
getStocksBySupplyId
|
|
3506
|
+
};
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
// src/models/hygiene-request-item.model.ts
|
|
3510
|
+
import { BadRequestError as BadRequestError22, logger as logger22 } from "@iservice365/node-server-utils";
|
|
3511
|
+
import Joi13 from "joi";
|
|
3512
|
+
import { ObjectId as ObjectId13 } from "mongodb";
|
|
3513
|
+
var allowedRequestItemStatus = [
|
|
3514
|
+
"pending",
|
|
3515
|
+
"approved",
|
|
3516
|
+
"disapproved"
|
|
3517
|
+
];
|
|
3518
|
+
var requestItemSchema = Joi13.object({
|
|
3519
|
+
site: Joi13.string().hex().required(),
|
|
3520
|
+
supply: Joi13.string().hex().required(),
|
|
3521
|
+
supplyName: Joi13.string().required(),
|
|
3522
|
+
qty: Joi13.number().min(0).required(),
|
|
3523
|
+
createdBy: Joi13.string().hex().required(),
|
|
3524
|
+
createdByName: Joi13.string().required()
|
|
3525
|
+
});
|
|
3526
|
+
function MRequestItem(value) {
|
|
3527
|
+
const { error } = requestItemSchema.validate(value);
|
|
3528
|
+
if (error) {
|
|
3529
|
+
logger22.info(`Hygiene Request Item Model: ${error.message}`);
|
|
3530
|
+
throw new BadRequestError22(error.message);
|
|
3531
|
+
}
|
|
3532
|
+
if (value.site) {
|
|
3533
|
+
try {
|
|
3534
|
+
value.site = new ObjectId13(value.site);
|
|
3535
|
+
} catch (error2) {
|
|
3536
|
+
throw new BadRequestError22("Invalid site ID format.");
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
if (value.supply) {
|
|
3540
|
+
try {
|
|
3541
|
+
value.supply = new ObjectId13(value.supply);
|
|
3542
|
+
} catch (error2) {
|
|
3543
|
+
throw new BadRequestError22("Invalid supply ID format.");
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
return {
|
|
3547
|
+
site: value.site,
|
|
3548
|
+
supply: value.supply,
|
|
3549
|
+
supplyName: value.supplyName,
|
|
3550
|
+
qty: value.qty,
|
|
3551
|
+
remarks: "",
|
|
3552
|
+
createdBy: value.createdBy,
|
|
3553
|
+
createdByName: value.createdByName,
|
|
3554
|
+
status: "pending",
|
|
3555
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3556
|
+
updatedAt: "",
|
|
3557
|
+
deletedAt: ""
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
// src/repositories/hygiene-request-item.repository.ts
|
|
3562
|
+
import { ObjectId as ObjectId14 } from "mongodb";
|
|
3563
|
+
import {
|
|
3564
|
+
useAtlas as useAtlas10,
|
|
3565
|
+
InternalServerError as InternalServerError7,
|
|
3566
|
+
useCache as useCache7,
|
|
3567
|
+
logger as logger23,
|
|
3568
|
+
makeCacheKey as makeCacheKey7,
|
|
3569
|
+
paginate as paginate7,
|
|
3570
|
+
BadRequestError as BadRequestError23
|
|
3571
|
+
} from "@iservice365/node-server-utils";
|
|
3572
|
+
function useRequestItemRepository() {
|
|
3573
|
+
const db = useAtlas10.getDb();
|
|
3574
|
+
if (!db) {
|
|
3575
|
+
throw new InternalServerError7("Unable to connect to server.");
|
|
3576
|
+
}
|
|
3577
|
+
const namespace_collection = "site.supply.requests";
|
|
3578
|
+
const collection = db.collection(namespace_collection);
|
|
3579
|
+
const { delNamespace, setCache, getCache } = useCache7(namespace_collection);
|
|
3580
|
+
async function createIndex() {
|
|
3581
|
+
try {
|
|
3582
|
+
await collection.createIndexes([
|
|
3583
|
+
{ key: { site: 1 } },
|
|
3584
|
+
{ key: { supply: 1 } },
|
|
3585
|
+
{ key: { status: 1 } }
|
|
3586
|
+
]);
|
|
3587
|
+
} catch (error) {
|
|
3588
|
+
throw new InternalServerError7(
|
|
3589
|
+
"Failed to create index on hygiene request item."
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
async function createTextIndex() {
|
|
3594
|
+
try {
|
|
3595
|
+
await collection.createIndex({ supplyName: "text" });
|
|
3596
|
+
} catch (error) {
|
|
3597
|
+
throw new InternalServerError7(
|
|
3598
|
+
"Failed to create text index on hygiene supply."
|
|
3599
|
+
);
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
async function createRequestItem(value, session) {
|
|
3603
|
+
try {
|
|
3604
|
+
value = MRequestItem(value);
|
|
3605
|
+
const res = await collection.insertOne(value, { session });
|
|
3606
|
+
delNamespace().then(() => {
|
|
3607
|
+
logger23.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3608
|
+
}).catch((err) => {
|
|
3609
|
+
logger23.error(
|
|
3610
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3611
|
+
err
|
|
3612
|
+
);
|
|
3613
|
+
});
|
|
3614
|
+
return res.insertedId;
|
|
3615
|
+
} catch (error) {
|
|
3616
|
+
throw error;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
async function getRequestItems({
|
|
3620
|
+
page = 1,
|
|
3621
|
+
limit = 10,
|
|
3622
|
+
search = "",
|
|
3623
|
+
site
|
|
3624
|
+
}) {
|
|
3625
|
+
page = page > 0 ? page - 1 : 0;
|
|
3626
|
+
const query = {
|
|
3627
|
+
status: { $ne: "deleted" }
|
|
3628
|
+
};
|
|
3629
|
+
const cacheOptions = {
|
|
3630
|
+
page,
|
|
3631
|
+
limit
|
|
3632
|
+
};
|
|
3633
|
+
try {
|
|
3634
|
+
site = new ObjectId14(site);
|
|
3635
|
+
query.site = site;
|
|
3636
|
+
cacheOptions.site = site.toString();
|
|
3637
|
+
} catch (error) {
|
|
3638
|
+
throw new BadRequestError23("Invalid site ID format.");
|
|
3639
|
+
}
|
|
3640
|
+
if (search) {
|
|
3641
|
+
query.$text = { $search: search };
|
|
3642
|
+
cacheOptions.search = search;
|
|
3643
|
+
}
|
|
3644
|
+
const cacheKey = makeCacheKey7(namespace_collection, cacheOptions);
|
|
3645
|
+
const cachedData = await getCache(cacheKey);
|
|
3646
|
+
if (cachedData) {
|
|
3647
|
+
logger23.info(`Cache hit for key: ${cacheKey}`);
|
|
3648
|
+
return cachedData;
|
|
3649
|
+
}
|
|
3650
|
+
try {
|
|
3651
|
+
const items = await collection.aggregate([
|
|
3652
|
+
{ $match: query },
|
|
3653
|
+
{
|
|
3654
|
+
$project: {
|
|
3655
|
+
createdAt: 1,
|
|
3656
|
+
status: 1
|
|
3657
|
+
}
|
|
3658
|
+
},
|
|
3659
|
+
{ $sort: { _id: 1 } },
|
|
3660
|
+
{ $skip: page * limit },
|
|
3661
|
+
{ $limit: limit }
|
|
3662
|
+
]).toArray();
|
|
3663
|
+
const length = await collection.countDocuments(query);
|
|
3664
|
+
const data = paginate7(items, page, limit, length);
|
|
3665
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3666
|
+
logger23.info(`Cache set for key: ${cacheKey}`);
|
|
3667
|
+
}).catch((err) => {
|
|
3668
|
+
logger23.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3669
|
+
});
|
|
3670
|
+
return data;
|
|
3671
|
+
} catch (error) {
|
|
3672
|
+
throw error;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
return {
|
|
3676
|
+
createIndex,
|
|
3677
|
+
createTextIndex,
|
|
3678
|
+
createRequestItem,
|
|
3679
|
+
getRequestItems
|
|
3680
|
+
};
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3683
|
+
// src/services/hygiene-request-item.service.ts
|
|
3684
|
+
import { useAtlas as useAtlas11 } from "@iservice365/node-server-utils";
|
|
3685
|
+
import { useUserRepo } from "@iservice365/core";
|
|
3686
|
+
function useRequestItemService() {
|
|
3687
|
+
const { createRequestItem: _createRequestItem } = useRequestItemRepository();
|
|
3688
|
+
const { getSupplyById } = useSupplyRepository();
|
|
3689
|
+
const { getUserById } = useUserRepo();
|
|
3690
|
+
async function createRequestItem(value) {
|
|
3691
|
+
try {
|
|
3692
|
+
const { supply, createdBy } = value;
|
|
3693
|
+
const supplyData = await getSupplyById(supply);
|
|
3694
|
+
const createdByData = await getUserById(createdBy);
|
|
3695
|
+
const createdRequestItem = await _createRequestItem({
|
|
3696
|
+
...value,
|
|
3697
|
+
supplyName: supplyData?.name || "",
|
|
3698
|
+
createdByName: createdByData?.name || ""
|
|
3699
|
+
});
|
|
3700
|
+
return createdRequestItem;
|
|
3701
|
+
} catch (error) {
|
|
3702
|
+
throw error;
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
async function createRequestItemByBatch(value) {
|
|
3706
|
+
const session = useAtlas11.getClient()?.startSession();
|
|
3707
|
+
try {
|
|
3708
|
+
session?.startTransaction();
|
|
3709
|
+
const { site, createdBy, items } = value;
|
|
3710
|
+
const createdByData = await getUserById(createdBy);
|
|
3711
|
+
const createdRequestItemIds = [];
|
|
3712
|
+
for (const item of items) {
|
|
3713
|
+
const supplyData = await getSupplyById(item.supply, session);
|
|
3714
|
+
const createdId = await _createRequestItem(
|
|
3715
|
+
{
|
|
3716
|
+
site,
|
|
3717
|
+
supply: item.supply,
|
|
3718
|
+
qty: item.qty,
|
|
3719
|
+
supplyName: supplyData?.name || "",
|
|
3720
|
+
createdBy,
|
|
3721
|
+
createdByName: createdByData?.name || ""
|
|
3722
|
+
},
|
|
3723
|
+
session
|
|
3724
|
+
);
|
|
3725
|
+
createdRequestItemIds.push(createdId);
|
|
3726
|
+
}
|
|
3727
|
+
await session?.commitTransaction();
|
|
3728
|
+
return createdRequestItemIds;
|
|
3729
|
+
} catch (error) {
|
|
3730
|
+
await session?.abortTransaction();
|
|
3731
|
+
throw error;
|
|
3732
|
+
} finally {
|
|
3733
|
+
await session?.endSession();
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
return { createRequestItem, createRequestItemByBatch };
|
|
3737
|
+
}
|
|
3738
|
+
|
|
3739
|
+
// src/controllers/hygiene-request-item.controller.ts
|
|
3740
|
+
import { BadRequestError as BadRequestError24, logger as logger24 } from "@iservice365/node-server-utils";
|
|
3741
|
+
import Joi14 from "joi";
|
|
3742
|
+
function useRequestItemController() {
|
|
3743
|
+
const { getRequestItems: _getRequestItems } = useRequestItemRepository();
|
|
3744
|
+
const {
|
|
3745
|
+
createRequestItem: _createRequestItem,
|
|
3746
|
+
createRequestItemByBatch: _createRequestItemByBatch
|
|
3747
|
+
} = useRequestItemService();
|
|
3748
|
+
async function createRequestItem(req, res, next) {
|
|
3749
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3750
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3751
|
+
{}
|
|
3752
|
+
) : {};
|
|
3753
|
+
const createdBy = cookies["user"] || "";
|
|
3754
|
+
const payload = {
|
|
3755
|
+
...req.body,
|
|
3756
|
+
...req.params,
|
|
3757
|
+
createdBy
|
|
3758
|
+
};
|
|
3759
|
+
const validation = Joi14.object({
|
|
3760
|
+
site: Joi14.string().hex().required(),
|
|
3761
|
+
supply: Joi14.string().hex().required(),
|
|
3762
|
+
qty: Joi14.number().min(0).required(),
|
|
3763
|
+
createdBy: Joi14.string().hex().required()
|
|
3764
|
+
});
|
|
3765
|
+
const { error } = validation.validate(payload);
|
|
3766
|
+
if (error) {
|
|
3767
|
+
logger24.log({ level: "error", message: error.message });
|
|
3768
|
+
next(new BadRequestError24(error.message));
|
|
3769
|
+
return;
|
|
3770
|
+
}
|
|
3771
|
+
try {
|
|
3772
|
+
const id = await _createRequestItem(payload);
|
|
3773
|
+
res.status(201).json({ message: "Request item created successfully.", id });
|
|
3774
|
+
return;
|
|
3775
|
+
} catch (error2) {
|
|
3776
|
+
logger24.log({ level: "error", message: error2.message });
|
|
3777
|
+
next(error2);
|
|
3778
|
+
return;
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
async function createRequestItemByBatch(req, res, next) {
|
|
3782
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3783
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3784
|
+
{}
|
|
3785
|
+
) : {};
|
|
3786
|
+
const createdBy = cookies["user"] || "";
|
|
3787
|
+
const payload = {
|
|
3788
|
+
...req.body,
|
|
3789
|
+
...req.params,
|
|
3790
|
+
createdBy
|
|
3791
|
+
};
|
|
3792
|
+
const validation = Joi14.object({
|
|
3793
|
+
site: Joi14.string().hex().required(),
|
|
3794
|
+
createdBy: Joi14.string().hex().required(),
|
|
3795
|
+
items: Joi14.array().items(
|
|
3796
|
+
Joi14.object({
|
|
3797
|
+
supply: Joi14.string().hex().required(),
|
|
3798
|
+
qty: Joi14.number().min(0).required()
|
|
3799
|
+
})
|
|
3800
|
+
).min(1).required()
|
|
3801
|
+
});
|
|
3802
|
+
const { error } = validation.validate(payload);
|
|
3803
|
+
if (error) {
|
|
3804
|
+
logger24.log({ level: "error", message: error.message });
|
|
3805
|
+
next(new BadRequestError24(error.message));
|
|
3806
|
+
return;
|
|
3807
|
+
}
|
|
3808
|
+
try {
|
|
3809
|
+
await _createRequestItemByBatch(payload);
|
|
3810
|
+
res.status(201).json({ message: "Request items created successfully." });
|
|
3811
|
+
return;
|
|
3812
|
+
} catch (error2) {
|
|
3813
|
+
logger24.log({ level: "error", message: error2.message });
|
|
3814
|
+
next(error2);
|
|
3815
|
+
return;
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
async function getRequestItems(req, res, next) {
|
|
3819
|
+
const query = { ...req.query, ...req.params };
|
|
3820
|
+
const validation = Joi14.object({
|
|
3821
|
+
page: Joi14.number().min(1).optional().allow("", null),
|
|
3822
|
+
limit: Joi14.number().min(1).optional().allow("", null),
|
|
3823
|
+
search: Joi14.string().optional().allow("", null),
|
|
3824
|
+
site: Joi14.string().hex().required()
|
|
3825
|
+
});
|
|
3826
|
+
const { error } = validation.validate(query);
|
|
3827
|
+
if (error) {
|
|
3828
|
+
logger24.log({ level: "error", message: error.message });
|
|
3829
|
+
next(new BadRequestError24(error.message));
|
|
3830
|
+
return;
|
|
3831
|
+
}
|
|
3832
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
3833
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
3834
|
+
const search = req.query.search ?? "";
|
|
3835
|
+
const site = req.params.site ?? "";
|
|
3836
|
+
try {
|
|
3837
|
+
const data = await _getRequestItems({
|
|
3838
|
+
page,
|
|
3839
|
+
limit,
|
|
3840
|
+
search,
|
|
3841
|
+
site
|
|
3842
|
+
});
|
|
3843
|
+
res.json(data);
|
|
3844
|
+
return;
|
|
3845
|
+
} catch (error2) {
|
|
3846
|
+
logger24.log({ level: "error", message: error2.message });
|
|
3847
|
+
next(error2);
|
|
3848
|
+
return;
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
return {
|
|
3852
|
+
createRequestItem,
|
|
3853
|
+
createRequestItemByBatch,
|
|
3854
|
+
getRequestItems
|
|
3855
|
+
};
|
|
3856
|
+
}
|
|
3210
3857
|
export {
|
|
3211
3858
|
MArea,
|
|
3212
3859
|
MAreaChecklist,
|
|
3213
3860
|
MParentChecklist,
|
|
3861
|
+
MRequestItem,
|
|
3862
|
+
MStock,
|
|
3214
3863
|
MSupply,
|
|
3215
3864
|
MUnit,
|
|
3216
3865
|
allowedChecklistStatus,
|
|
3866
|
+
allowedRequestItemStatus,
|
|
3217
3867
|
allowedStatus,
|
|
3218
3868
|
allowedTypes,
|
|
3219
3869
|
areaChecklistSchema,
|
|
3220
3870
|
areaSchema,
|
|
3221
3871
|
parentChecklistSchema,
|
|
3872
|
+
requestItemSchema,
|
|
3873
|
+
stockSchema,
|
|
3222
3874
|
supplySchema,
|
|
3223
3875
|
unitSchema,
|
|
3224
3876
|
useAreaChecklistController,
|
|
@@ -3229,6 +3881,12 @@ export {
|
|
|
3229
3881
|
useAreaService,
|
|
3230
3882
|
useParentChecklistController,
|
|
3231
3883
|
useParentChecklistRepo,
|
|
3884
|
+
useRequestItemController,
|
|
3885
|
+
useRequestItemRepository,
|
|
3886
|
+
useRequestItemService,
|
|
3887
|
+
useStockController,
|
|
3888
|
+
useStockRepository,
|
|
3889
|
+
useStockService,
|
|
3232
3890
|
useSupplyController,
|
|
3233
3891
|
useSupplyRepository,
|
|
3234
3892
|
useUnitController,
|