@iservice365/module-hygiene 1.0.3 → 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/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) ?? 20;
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) ?? 20;
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) ?? 20;
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) ?? 20;
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) ?? 20;
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) ?? 20;
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,7 +2816,7 @@ function MSupply(value) {
2817
2816
  site: value.site,
2818
2817
  name: value.name,
2819
2818
  unitOfMeasurement: value.unitOfMeasurement,
2820
- qty: value.qty,
2819
+ qty: 0,
2821
2820
  status: "active",
2822
2821
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2823
2822
  updatedAt: "",
@@ -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
- const cachedData = await getCache(cacheKey);
2969
- if (cachedData) {
2970
- logger17.info(`Cache hit for key: ${cacheKey}`);
2971
- return cachedData;
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([
@@ -3072,39 +3075,172 @@ function useSupplyRepository() {
3072
3075
  };
3073
3076
  }
3074
3077
 
3075
- // src/services/hygiene-supply.service.ts
3076
- import { useAtlas as useAtlas9 } from "@iservice365/node-server-utils";
3077
-
3078
- // src/models/hygiene-stock.model.ts
3078
+ // src/controllers/hygiene-supply.controller.ts
3079
3079
  import { BadRequestError as BadRequestError17, logger as logger18 } from "@iservice365/node-server-utils";
3080
3080
  import Joi10 from "joi";
3081
+ function useSupplyController() {
3082
+ const {
3083
+ createSupply: _createSupply,
3084
+ getSupplies: _getSupplies,
3085
+ getSupplyById: _getSupplyById,
3086
+ updateSupply: _updateSupply,
3087
+ deleteSupply: _deleteSupply
3088
+ } = useSupplyRepository();
3089
+ async function createSupply(req, res, next) {
3090
+ const payload = { ...req.body, ...req.params };
3091
+ const { error } = supplySchema.validate(payload);
3092
+ if (error) {
3093
+ logger18.log({ level: "error", message: error.message });
3094
+ next(new BadRequestError17(error.message));
3095
+ return;
3096
+ }
3097
+ try {
3098
+ const id = await _createSupply(payload);
3099
+ res.status(201).json({ message: "Supply created successfully.", id });
3100
+ return;
3101
+ } catch (error2) {
3102
+ logger18.log({ level: "error", message: error2.message });
3103
+ next(error2);
3104
+ return;
3105
+ }
3106
+ }
3107
+ async function getSupplies(req, res, next) {
3108
+ const query = { ...req.query, ...req.params };
3109
+ const validation = Joi10.object({
3110
+ page: Joi10.number().min(1).optional().allow("", null),
3111
+ limit: Joi10.number().min(1).optional().allow("", null),
3112
+ search: Joi10.string().optional().allow("", null),
3113
+ site: Joi10.string().hex().required()
3114
+ });
3115
+ const { error } = validation.validate(query);
3116
+ if (error) {
3117
+ logger18.log({ level: "error", message: error.message });
3118
+ next(new BadRequestError17(error.message));
3119
+ return;
3120
+ }
3121
+ const page = parseInt(req.query.page) ?? 1;
3122
+ const limit = parseInt(req.query.limit) ?? 10;
3123
+ const search = req.query.search ?? "";
3124
+ const site = req.params.site ?? "";
3125
+ try {
3126
+ const data = await _getSupplies({
3127
+ page,
3128
+ limit,
3129
+ search,
3130
+ site
3131
+ });
3132
+ res.json(data);
3133
+ return;
3134
+ } catch (error2) {
3135
+ logger18.log({ level: "error", message: error2.message });
3136
+ next(error2);
3137
+ return;
3138
+ }
3139
+ }
3140
+ async function getSupplyById(req, res, next) {
3141
+ const validation = Joi10.string().hex().required();
3142
+ const _id = req.params.id;
3143
+ const { error } = validation.validate(_id);
3144
+ if (error) {
3145
+ logger18.log({ level: "error", message: error.message });
3146
+ next(new BadRequestError17(error.message));
3147
+ return;
3148
+ }
3149
+ try {
3150
+ const data = await _getSupplyById(_id);
3151
+ res.json(data);
3152
+ return;
3153
+ } catch (error2) {
3154
+ logger18.log({ level: "error", message: error2.message });
3155
+ next(error2);
3156
+ return;
3157
+ }
3158
+ }
3159
+ async function updateSupply(req, res, next) {
3160
+ const payload = { id: req.params.id, ...req.body };
3161
+ const validation = Joi10.object({
3162
+ id: Joi10.string().hex().required(),
3163
+ name: Joi10.string().optional().allow("", null),
3164
+ unitOfMeasurement: Joi10.string().optional().allow("", null),
3165
+ qty: Joi10.number().min(0).optional().allow("", null)
3166
+ });
3167
+ const { error } = validation.validate(payload);
3168
+ if (error) {
3169
+ logger18.log({ level: "error", message: error.message });
3170
+ next(new BadRequestError17(error.message));
3171
+ return;
3172
+ }
3173
+ try {
3174
+ const { id, ...value } = payload;
3175
+ await _updateSupply(id, value);
3176
+ res.json({ message: "Supply updated successfully." });
3177
+ return;
3178
+ } catch (error2) {
3179
+ logger18.log({ level: "error", message: error2.message });
3180
+ next(error2);
3181
+ return;
3182
+ }
3183
+ }
3184
+ async function deleteSupply(req, res, next) {
3185
+ const id = req.params.id;
3186
+ const validation = Joi10.object({
3187
+ id: Joi10.string().hex().required()
3188
+ });
3189
+ const { error } = validation.validate({ id });
3190
+ if (error) {
3191
+ logger18.log({ level: "error", message: error.message });
3192
+ next(new BadRequestError17(error.message));
3193
+ return;
3194
+ }
3195
+ try {
3196
+ await _deleteSupply(id);
3197
+ res.json({ message: "Supply deleted successfully." });
3198
+ return;
3199
+ } catch (error2) {
3200
+ logger18.log({ level: "error", message: error2.message });
3201
+ next(error2);
3202
+ return;
3203
+ }
3204
+ }
3205
+ return {
3206
+ createSupply,
3207
+ getSupplies,
3208
+ getSupplyById,
3209
+ updateSupply,
3210
+ deleteSupply
3211
+ };
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";
3081
3217
  import { ObjectId as ObjectId11 } from "mongodb";
3082
- var stockSchema = Joi10.object({
3083
- site: Joi10.string().hex().required(),
3084
- supply: Joi10.string().hex().required(),
3085
- in: Joi10.number().min(0).optional(),
3086
- out: Joi10.number().min(0).optional(),
3087
- balance: Joi10.number().min(0).required(),
3088
- remarks: Joi10.string().optional().allow("", null)
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)
3089
3225
  });
3090
3226
  function MStock(value) {
3091
3227
  const { error } = stockSchema.validate(value);
3092
3228
  if (error) {
3093
- logger18.info(`Hygiene Stock Model: ${error.message}`);
3094
- throw new BadRequestError17(error.message);
3229
+ logger19.info(`Hygiene Stock Model: ${error.message}`);
3230
+ throw new BadRequestError18(error.message);
3095
3231
  }
3096
3232
  if (value.site) {
3097
3233
  try {
3098
3234
  value.site = new ObjectId11(value.site);
3099
3235
  } catch (error2) {
3100
- throw new BadRequestError17("Invalid site ID format.");
3236
+ throw new BadRequestError18("Invalid site ID format.");
3101
3237
  }
3102
3238
  }
3103
3239
  if (value.supply) {
3104
3240
  try {
3105
3241
  value.supply = new ObjectId11(value.supply);
3106
3242
  } catch (error2) {
3107
- throw new BadRequestError17("Invalid supply ID format.");
3243
+ throw new BadRequestError18("Invalid supply ID format.");
3108
3244
  }
3109
3245
  }
3110
3246
  return {
@@ -3122,12 +3258,15 @@ function MStock(value) {
3122
3258
  }
3123
3259
 
3124
3260
  // src/repositories/hygiene-stock.repository.ts
3261
+ import { ObjectId as ObjectId12 } from "mongodb";
3125
3262
  import {
3126
3263
  useAtlas as useAtlas8,
3127
3264
  InternalServerError as InternalServerError6,
3128
- BadRequestError as BadRequestError18,
3265
+ BadRequestError as BadRequestError19,
3129
3266
  useCache as useCache6,
3130
- logger as logger19
3267
+ logger as logger20,
3268
+ makeCacheKey as makeCacheKey6,
3269
+ paginate as paginate6
3131
3270
  } from "@iservice365/node-server-utils";
3132
3271
  function useStockRepository() {
3133
3272
  const db = useAtlas8.getDb();
@@ -3135,8 +3274,10 @@ function useStockRepository() {
3135
3274
  throw new InternalServerError6("Unable to connect to server.");
3136
3275
  }
3137
3276
  const namespace_collection = "site.supply.stocks";
3277
+ const supply_collection = "site.supplies";
3138
3278
  const collection = db.collection(namespace_collection);
3139
- const { delNamespace } = useCache6(namespace_collection);
3279
+ const { delNamespace, setCache, getCache } = useCache6(namespace_collection);
3280
+ const { delNamespace: delSupplyNamespace } = useCache6(supply_collection);
3140
3281
  async function createIndex() {
3141
3282
  try {
3142
3283
  await collection.createIndexes([
@@ -3154,49 +3295,135 @@ function useStockRepository() {
3154
3295
  value = MStock(value);
3155
3296
  const res = await collection.insertOne(value, { session });
3156
3297
  delNamespace().then(() => {
3157
- logger19.info(`Cache cleared for namespace: ${namespace_collection}`);
3298
+ logger20.info(`Cache cleared for namespace: ${namespace_collection}`);
3158
3299
  }).catch((err) => {
3159
- logger19.error(
3300
+ logger20.error(
3160
3301
  `Failed to clear cache for namespace: ${namespace_collection}`,
3161
3302
  err
3162
3303
  );
3163
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
+ });
3164
3313
  return res.insertedId;
3165
3314
  } catch (error) {
3166
- const isDuplicated = error.message.includes("duplicate");
3167
- if (isDuplicated) {
3168
- throw new BadRequestError18("Stock already exists.");
3169
- }
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) {
3170
3381
  throw error;
3171
3382
  }
3172
3383
  }
3173
3384
  return {
3174
3385
  createIndex,
3175
- createStock
3386
+ createStock,
3387
+ getStocksBySupplyId
3176
3388
  };
3177
3389
  }
3178
3390
 
3179
- // src/services/hygiene-supply.service.ts
3180
- function useSupplyService() {
3181
- const { createSupply: _createSupply } = useSupplyRepository();
3182
- const { createStock } = useStockRepository();
3183
- async function createSupply(value) {
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) {
3184
3401
  const session = useAtlas9.getClient()?.startSession();
3185
3402
  try {
3186
3403
  session?.startTransaction();
3187
- const { qty, site } = value;
3188
- const supply = await _createSupply(value, session);
3189
- const createdSupply = await createStock(
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(
3190
3417
  {
3191
- site,
3192
- supply: supply.toString(),
3193
- in: qty,
3194
- balance: qty
3418
+ ...stockData,
3419
+ in: out ? 0 : qty,
3420
+ out: out ? qty : 0,
3421
+ balance: newSupplyQty
3195
3422
  },
3196
3423
  session
3197
3424
  );
3198
3425
  await session?.commitTransaction();
3199
- return createdSupply;
3426
+ return createdStock;
3200
3427
  } catch (error) {
3201
3428
  await session?.abortTransaction();
3202
3429
  throw error;
@@ -3204,167 +3431,301 @@ function useSupplyService() {
3204
3431
  await session?.endSession();
3205
3432
  }
3206
3433
  }
3207
- return { createSupply };
3434
+ return { createStock };
3208
3435
  }
3209
3436
 
3210
- // src/controllers/hygiene-supply.controller.ts
3211
- import { BadRequestError as BadRequestError19, logger as logger20 } from "@iservice365/node-server-utils";
3212
- import Joi11 from "joi";
3213
- function useSupplyController() {
3214
- const {
3215
- getSupplies: _getSupplies,
3216
- getSupplyById: _getSupplyById,
3217
- updateSupply: _updateSupply,
3218
- deleteSupply: _deleteSupply
3219
- } = useSupplyRepository();
3220
- const { createSupply: _createSupply } = useSupplyService();
3221
- async function createSupply(req, res, next) {
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) {
3222
3444
  const payload = { ...req.body, ...req.params };
3223
- const { error } = supplySchema.validate(payload);
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);
3224
3452
  if (error) {
3225
- logger20.log({ level: "error", message: error.message });
3226
- next(new BadRequestError19(error.message));
3453
+ logger21.log({ level: "error", message: error.message });
3454
+ next(new BadRequestError21(error.message));
3227
3455
  return;
3228
3456
  }
3229
3457
  try {
3230
- const id = await _createSupply(payload);
3231
- res.status(201).json({ message: "Supply created successfully.", id });
3458
+ const id = await _createStock(payload);
3459
+ res.status(201).json({ message: "Stock created successfully.", id });
3232
3460
  return;
3233
3461
  } catch (error2) {
3234
- logger20.log({ level: "error", message: error2.message });
3462
+ logger21.log({ level: "error", message: error2.message });
3235
3463
  next(error2);
3236
3464
  return;
3237
3465
  }
3238
3466
  }
3239
- async function getSupplies(req, res, next) {
3467
+ async function getStocksBySupplyId(req, res, next) {
3240
3468
  const query = { ...req.query, ...req.params };
3241
- const validation = Joi11.object({
3242
- page: Joi11.number().min(1).optional().allow("", null),
3243
- limit: Joi11.number().min(1).optional().allow("", null),
3244
- search: Joi11.string().optional().allow("", null),
3245
- site: Joi11.string().hex().required()
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()
3246
3475
  });
3247
3476
  const { error } = validation.validate(query);
3248
3477
  if (error) {
3249
- logger20.log({ level: "error", message: error.message });
3250
- next(new BadRequestError19(error.message));
3478
+ logger21.log({ level: "error", message: error.message });
3479
+ next(new BadRequestError21(error.message));
3251
3480
  return;
3252
3481
  }
3253
3482
  const page = parseInt(req.query.page) ?? 1;
3254
- const limit = parseInt(req.query.limit) ?? 20;
3483
+ const limit = parseInt(req.query.limit) ?? 10;
3255
3484
  const search = req.query.search ?? "";
3256
3485
  const site = req.params.site ?? "";
3486
+ const supply = req.params.supply ?? "";
3257
3487
  try {
3258
- const data = await _getSupplies({
3488
+ const data = await _getStocksBySupplyId({
3259
3489
  page,
3260
3490
  limit,
3261
3491
  search,
3262
- site
3492
+ site,
3493
+ supply
3263
3494
  });
3264
3495
  res.json(data);
3265
3496
  return;
3266
3497
  } catch (error2) {
3267
- logger20.log({ level: "error", message: error2.message });
3498
+ logger21.log({ level: "error", message: error2.message });
3268
3499
  next(error2);
3269
3500
  return;
3270
3501
  }
3271
3502
  }
3272
- async function getSupplyById(req, res, next) {
3273
- const validation = Joi11.string().hex().required();
3274
- const _id = req.params.id;
3275
- const { error } = validation.validate(_id);
3276
- if (error) {
3277
- logger20.log({ level: "error", message: error.message });
3278
- next(new BadRequestError19(error.message));
3279
- return;
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.");
3280
3537
  }
3538
+ }
3539
+ if (value.supply) {
3281
3540
  try {
3282
- const data = await _getSupplyById(_id);
3283
- res.json(data);
3284
- return;
3541
+ value.supply = new ObjectId13(value.supply);
3285
3542
  } catch (error2) {
3286
- logger20.log({ level: "error", message: error2.message });
3287
- next(error2);
3288
- return;
3543
+ throw new BadRequestError22("Invalid supply ID format.");
3289
3544
  }
3290
3545
  }
3291
- async function updateSupply(req, res, next) {
3292
- const payload = { id: req.params.id, ...req.body };
3293
- const validation = Joi11.object({
3294
- id: Joi11.string().hex().required(),
3295
- name: Joi11.string().optional().allow("", null),
3296
- unitOfMeasurement: Joi11.string().optional().allow("", null),
3297
- qty: Joi11.number().min(0).optional().allow("", null)
3298
- });
3299
- const { error } = validation.validate(payload);
3300
- if (error) {
3301
- logger20.log({ level: "error", message: error.message });
3302
- next(new BadRequestError19(error.message));
3303
- return;
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
+ );
3304
3591
  }
3592
+ }
3593
+ async function createTextIndex() {
3305
3594
  try {
3306
- const { id, ...value } = payload;
3307
- await _updateSupply(id, value);
3308
- res.json({ message: "Supply updated successfully." });
3309
- return;
3310
- } catch (error2) {
3311
- logger20.log({ level: "error", message: error2.message });
3312
- next(error2);
3313
- return;
3595
+ await collection.createIndex({ supplyName: "text" });
3596
+ } catch (error) {
3597
+ throw new InternalServerError7(
3598
+ "Failed to create text index on hygiene supply."
3599
+ );
3314
3600
  }
3315
3601
  }
3316
- async function deleteSupply(req, res, next) {
3317
- const id = req.params.id;
3318
- const validation = Joi11.object({
3319
- id: Joi11.string().hex().required()
3320
- });
3321
- const { error } = validation.validate({ id });
3322
- if (error) {
3323
- logger20.log({ level: "error", message: error.message });
3324
- next(new BadRequestError19(error.message));
3325
- return;
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;
3326
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
+ };
3327
3633
  try {
3328
- await _deleteSupply(id);
3329
- res.json({ message: "Supply deleted successfully." });
3330
- return;
3331
- } catch (error2) {
3332
- logger20.log({ level: "error", message: error2.message });
3333
- next(error2);
3334
- return;
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;
3335
3673
  }
3336
3674
  }
3337
3675
  return {
3338
- createSupply,
3339
- getSupplies,
3340
- getSupplyById,
3341
- updateSupply,
3342
- deleteSupply
3676
+ createIndex,
3677
+ createTextIndex,
3678
+ createRequestItem,
3679
+ getRequestItems
3343
3680
  };
3344
3681
  }
3345
3682
 
3346
- // src/services/hygiene-stock.service.ts
3347
- import { NotFoundError as NotFoundError5, useAtlas as useAtlas10 } from "@iservice365/node-server-utils";
3348
- function useStockService() {
3349
- const { createStock: _createStock } = useStockRepository();
3350
- const { getSupplyById, updateSupply } = useSupplyRepository();
3351
- async function createStock(value) {
3352
- const session = useAtlas10.getClient()?.startSession();
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();
3353
3707
  try {
3354
3708
  session?.startTransaction();
3355
- const { qty, ...stockData } = value;
3356
- const supply = await getSupplyById(value.supply);
3357
- if (!supply || supply.qty === void 0) {
3358
- throw new NotFoundError5("Supply not found.");
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);
3359
3726
  }
3360
- const newSupplyQty = supply.qty + qty;
3361
- await updateSupply(value.supply, { qty: newSupplyQty }, session);
3362
- const createdStock = await _createStock(
3363
- { ...stockData, in: qty, balance: newSupplyQty },
3364
- session
3365
- );
3366
3727
  await session?.commitTransaction();
3367
- return createdStock;
3728
+ return createdRequestItemIds;
3368
3729
  } catch (error) {
3369
3730
  await session?.abortTransaction();
3370
3731
  throw error;
@@ -3372,55 +3733,143 @@ function useStockService() {
3372
3733
  await session?.endSession();
3373
3734
  }
3374
3735
  }
3375
- return { createStock };
3736
+ return { createRequestItem, createRequestItemByBatch };
3376
3737
  }
3377
3738
 
3378
- // src/controllers/hygiene-stock.controller.ts
3379
- import { BadRequestError as BadRequestError20, logger as logger21 } from "@iservice365/node-server-utils";
3380
- import Joi12 from "joi";
3381
- function useStockController() {
3382
- const { createStock: _createStock } = useStockService();
3383
- async function createStock(req, res, next) {
3384
- const payload = { ...req.body, ...req.params };
3385
- const validation = Joi12.object({
3386
- site: Joi12.string().hex().required(),
3387
- supply: Joi12.string().hex().required(),
3388
- qty: Joi12.number().min(0).optional(),
3389
- remarks: Joi12.string().optional().allow("", null)
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()
3390
3764
  });
3391
3765
  const { error } = validation.validate(payload);
3392
3766
  if (error) {
3393
- logger21.log({ level: "error", message: error.message });
3394
- next(new BadRequestError20(error.message));
3767
+ logger24.log({ level: "error", message: error.message });
3768
+ next(new BadRequestError24(error.message));
3395
3769
  return;
3396
3770
  }
3397
3771
  try {
3398
- const id = await _createStock(payload);
3399
- res.status(201).json({ message: "Stock created successfully.", id });
3772
+ const id = await _createRequestItem(payload);
3773
+ res.status(201).json({ message: "Request item created successfully.", id });
3400
3774
  return;
3401
3775
  } catch (error2) {
3402
- logger21.log({ level: "error", message: error2.message });
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 });
3403
3847
  next(error2);
3404
3848
  return;
3405
3849
  }
3406
3850
  }
3407
3851
  return {
3408
- createStock
3852
+ createRequestItem,
3853
+ createRequestItemByBatch,
3854
+ getRequestItems
3409
3855
  };
3410
3856
  }
3411
3857
  export {
3412
3858
  MArea,
3413
3859
  MAreaChecklist,
3414
3860
  MParentChecklist,
3861
+ MRequestItem,
3415
3862
  MStock,
3416
3863
  MSupply,
3417
3864
  MUnit,
3418
3865
  allowedChecklistStatus,
3866
+ allowedRequestItemStatus,
3419
3867
  allowedStatus,
3420
3868
  allowedTypes,
3421
3869
  areaChecklistSchema,
3422
3870
  areaSchema,
3423
3871
  parentChecklistSchema,
3872
+ requestItemSchema,
3424
3873
  stockSchema,
3425
3874
  supplySchema,
3426
3875
  unitSchema,
@@ -3432,12 +3881,14 @@ export {
3432
3881
  useAreaService,
3433
3882
  useParentChecklistController,
3434
3883
  useParentChecklistRepo,
3884
+ useRequestItemController,
3885
+ useRequestItemRepository,
3886
+ useRequestItemService,
3435
3887
  useStockController,
3436
3888
  useStockRepository,
3437
3889
  useStockService,
3438
3890
  useSupplyController,
3439
3891
  useSupplyRepository,
3440
- useSupplyService,
3441
3892
  useUnitController,
3442
3893
  useUnitRepository,
3443
3894
  useUnitService