@iservice365/module-hygiene 0.2.0 → 1.0.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
@@ -1,13 +1,11 @@
1
- // src/models/hygiene-area.model.ts
2
- import { BadRequestError, logger } from "@iservice365/node-server-utils";
3
- import Joi from "joi";
4
- import { ObjectId } from "mongodb";
5
-
6
1
  // src/models/hygiene-base.model.ts
7
2
  var allowedTypes = ["common", "toilet"];
8
3
  var allowedStatus = ["ready", "ongoing", "completed"];
9
4
 
10
5
  // src/models/hygiene-area.model.ts
6
+ import { BadRequestError, logger } from "@iservice365/node-server-utils";
7
+ import Joi from "joi";
8
+ import { ObjectId } from "mongodb";
11
9
  var areaSchema = Joi.object({
12
10
  site: Joi.string().hex().required(),
13
11
  name: Joi.string().required(),
@@ -266,6 +264,7 @@ function useAreaRepo() {
266
264
  { $match: query },
267
265
  {
268
266
  $project: {
267
+ type: 1,
269
268
  name: 1,
270
269
  set: 1,
271
270
  units: 1
@@ -285,7 +284,38 @@ function useAreaRepo() {
285
284
  throw error;
286
285
  }
287
286
  }
288
- async function updateArea(_id, value) {
287
+ async function verifyAreaByUnitId(unitId) {
288
+ try {
289
+ unitId = new ObjectId2(unitId);
290
+ } catch (error) {
291
+ throw new BadRequestError2("Invalid unit ID format.");
292
+ }
293
+ const query = {
294
+ "units.unit": unitId,
295
+ status: { $ne: "deleted" }
296
+ };
297
+ const cacheKey = makeCacheKey(namespace_collection, {
298
+ verify_unitId: unitId.toString()
299
+ });
300
+ const cachedData = await getCache(cacheKey);
301
+ if (cachedData) {
302
+ logger2.info(`Cache hit for key: ${cacheKey}`);
303
+ return cachedData;
304
+ }
305
+ try {
306
+ const count = await collection.countDocuments(query);
307
+ const result = count > 0;
308
+ setCache(cacheKey, result, 15 * 60).then(() => {
309
+ logger2.info(`Cache set for key: ${cacheKey}`);
310
+ }).catch((err) => {
311
+ logger2.error(`Failed to set cache for key: ${cacheKey}`, err);
312
+ });
313
+ return result;
314
+ } catch (error) {
315
+ throw error;
316
+ }
317
+ }
318
+ async function updateArea(_id, value, session) {
289
319
  try {
290
320
  _id = new ObjectId2(_id);
291
321
  } catch (error) {
@@ -316,7 +346,11 @@ function useAreaRepo() {
316
346
  }
317
347
  });
318
348
  }
319
- const res = await collection.updateOne({ _id }, { $set: updateValue });
349
+ const res = await collection.updateOne(
350
+ { _id },
351
+ { $set: updateValue },
352
+ { session }
353
+ );
320
354
  if (res.modifiedCount === 0) {
321
355
  throw new InternalServerError("Unable to update cleaning area.");
322
356
  }
@@ -337,29 +371,25 @@ function useAreaRepo() {
337
371
  throw error;
338
372
  }
339
373
  }
340
- async function updateAreaChecklist(_id, value) {
374
+ async function updateAreaChecklist(unitId, name, session) {
341
375
  try {
342
- _id = new ObjectId2(_id);
376
+ unitId = new ObjectId2(unitId);
343
377
  } catch (error) {
344
- throw new BadRequestError2("Invalid area ID format.");
345
- }
346
- if (value.units && Array.isArray(value.units)) {
347
- value.units = value.units.map((item) => {
348
- try {
349
- return {
350
- ...item,
351
- unit: new ObjectId2(item.unit)
352
- };
353
- } catch (error) {
354
- throw new BadRequestError2(`Invalid unit ID format: ${item.unit}`);
355
- }
356
- });
378
+ throw new BadRequestError2("Invalid unit ID format.");
357
379
  }
358
380
  try {
359
- const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
360
- const res = await collection.updateOne({ _id }, { $set: updateValue });
381
+ const res = await collection.updateMany(
382
+ { "units.unit": unitId },
383
+ {
384
+ $set: {
385
+ "units.$.name": name,
386
+ updatedAt: /* @__PURE__ */ new Date()
387
+ }
388
+ },
389
+ { session }
390
+ );
361
391
  if (res.modifiedCount === 0) {
362
- throw new InternalServerError("Unable to update cleaning area.");
392
+ throw new InternalServerError("Unable to update unit name in areas.");
363
393
  }
364
394
  delNamespace().then(() => {
365
395
  logger2.info(`Cache cleared for namespace: ${namespace_collection}`);
@@ -415,6 +445,7 @@ function useAreaRepo() {
415
445
  getAreas,
416
446
  getAreasForChecklist,
417
447
  getAreaById,
448
+ verifyAreaByUnitId,
418
449
  updateArea,
419
450
  updateAreaChecklist,
420
451
  deleteArea
@@ -454,7 +485,7 @@ function useAreaService() {
454
485
  skippedRows.push(i + 1);
455
486
  continue;
456
487
  }
457
- const areaType = String(row.TYPE).trim();
488
+ const areaType = String(row.TYPE).trim().toLowerCase();
458
489
  if (!areaType) {
459
490
  logger3.warn(`Skipping row ${i + 1} with empty area type`);
460
491
  skippedRows.push(i + 1);
@@ -502,18 +533,22 @@ function useAreaService() {
502
533
  }
503
534
  logger3.info(message);
504
535
  if (insertedAreaIds.length === 0) {
505
- if (duplicateAreas.length > 0 && failedAreas.length === 0) {
506
- throw new BadRequestError3(
507
- `No new areas were created. All ${duplicateAreas.length} areas already exist in the system: ${duplicateAreas.join(", ")}`
508
- );
536
+ if (duplicateAreas.length > 0 && failedAreas.length === 0 && skippedRows.length === 0) {
537
+ return {
538
+ message: `No new areas were created. All ${duplicateAreas.length} areas already exist in the system: ${duplicateAreas.join(", ")}`
539
+ };
509
540
  } else if (failedAreas.length > 0) {
510
541
  throw new BadRequestError3(
511
- `No areas were created. Please check your data format and ensure area names are valid.`
542
+ `No areas were created. ${failedAreas.length} areas failed due to errors. Please check your data format and ensure area names are valid.`
512
543
  );
513
- } else if (skippedRows.length > 0) {
544
+ } else if (skippedRows.length > 0 && duplicateAreas.length === 0) {
514
545
  throw new BadRequestError3(
515
- `No areas were created. All rows contained invalid or missing area names.`
546
+ `No areas were created. All ${skippedRows.length} rows contained invalid or missing data.`
516
547
  );
548
+ } else {
549
+ return {
550
+ message: `No new areas were created. ${duplicateAreas.length} areas already exist, ${skippedRows.length} rows had invalid data.`
551
+ };
517
552
  }
518
553
  }
519
554
  return { message };
@@ -521,10 +556,6 @@ function useAreaService() {
521
556
  logger3.error("Error while uploading area information", error);
522
557
  if (error instanceof BadRequestError3) {
523
558
  throw error;
524
- } else if (error.message.includes("duplicate")) {
525
- throw new BadRequestError3(
526
- `Upload failed due to duplicate area names. Please ensure all area names are unique.`
527
- );
528
559
  } else if (error.message.includes("validation")) {
529
560
  throw new BadRequestError3(
530
561
  "Upload failed due to invalid data format. Please check that all required fields are properly filled."
@@ -577,7 +608,6 @@ function useAreaController() {
577
608
  getAreas: _getAreas,
578
609
  getAreaById: _getAreaById,
579
610
  updateArea: _updateArea,
580
- updateAreaChecklist: _updateAreaChecklist,
581
611
  deleteArea: _deleteById
582
612
  } = useAreaRepo();
583
613
  const { importArea: _importArea } = useAreaService();
@@ -683,36 +713,6 @@ function useAreaController() {
683
713
  return;
684
714
  }
685
715
  }
686
- async function updateAreaChecklist(req, res, next) {
687
- const payload = { id: req.params.id, ...req.body };
688
- const schema = Joi2.object({
689
- id: Joi2.string().hex().required(),
690
- units: Joi2.array().items(
691
- Joi2.object({
692
- unit: Joi2.string().hex().required(),
693
- name: Joi2.string().required()
694
- }).required()
695
- ).min(1).unique("unit", { ignoreUndefined: true }).messages({
696
- "array.unique": "Duplicate area units are not allowed"
697
- })
698
- });
699
- const { error } = schema.validate(payload);
700
- if (error) {
701
- logger4.log({ level: "error", message: error.message });
702
- next(new BadRequestError4(error.message));
703
- return;
704
- }
705
- try {
706
- const { id, ...value } = payload;
707
- await _updateAreaChecklist(id, value);
708
- res.json({ message: "Area updated successfully." });
709
- return;
710
- } catch (error2) {
711
- logger4.log({ level: "error", message: error2.message });
712
- next(error2);
713
- return;
714
- }
715
- }
716
716
  async function deleteArea(req, res, next) {
717
717
  const id = req.params.id;
718
718
  const validation = Joi2.string().hex().required();
@@ -761,7 +761,6 @@ function useAreaController() {
761
761
  getAreas,
762
762
  getAreaById,
763
763
  updateArea,
764
- updateAreaChecklist,
765
764
  deleteArea,
766
765
  importArea
767
766
  };
@@ -802,7 +801,8 @@ function MUnit(value) {
802
801
  import {
803
802
  BadRequestError as BadRequestError7,
804
803
  logger as logger7,
805
- NotFoundError as NotFoundError3
804
+ NotFoundError as NotFoundError3,
805
+ useAtlas as useAtlas3
806
806
  } from "@iservice365/node-server-utils";
807
807
 
808
808
  // src/repositories/hygiene-unit.repository.ts
@@ -927,7 +927,7 @@ function useUnitRepository() {
927
927
  throw error;
928
928
  }
929
929
  }
930
- async function updateUnit(_id, value) {
930
+ async function updateUnit(_id, value, session) {
931
931
  try {
932
932
  _id = new ObjectId4(_id);
933
933
  } catch (error) {
@@ -935,7 +935,11 @@ function useUnitRepository() {
935
935
  }
936
936
  try {
937
937
  const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
938
- const res = await collection.updateOne({ _id }, { $set: updateValue });
938
+ const res = await collection.updateOne(
939
+ { _id },
940
+ { $set: updateValue },
941
+ { session }
942
+ );
939
943
  if (res.modifiedCount === 0) {
940
944
  throw new InternalServerError2("Unable to update cleaning unit.");
941
945
  }
@@ -1002,7 +1006,12 @@ function useUnitRepository() {
1002
1006
 
1003
1007
  // src/services/hygiene-unit.service.ts
1004
1008
  function useUnitService() {
1005
- const { createUnit: _createUnit } = useUnitRepository();
1009
+ const {
1010
+ createUnit: _createUnit,
1011
+ updateUnit: _updateUnit,
1012
+ deleteUnit: _deleteUnit
1013
+ } = useUnitRepository();
1014
+ const { verifyAreaByUnitId, updateAreaChecklist } = useAreaRepo();
1006
1015
  async function importUnit({
1007
1016
  dataJson,
1008
1017
  site
@@ -1096,20 +1105,61 @@ function useUnitService() {
1096
1105
  }
1097
1106
  }
1098
1107
  }
1099
- return { importUnit };
1108
+ async function updateUnit(_id, value) {
1109
+ const session = useAtlas3.getClient()?.startSession();
1110
+ try {
1111
+ session?.startTransaction();
1112
+ const result = await _updateUnit(_id, value, session);
1113
+ const isExistingArea = await verifyAreaByUnitId(_id);
1114
+ if (isExistingArea) {
1115
+ await updateAreaChecklist(_id, value.name, session);
1116
+ }
1117
+ await session?.commitTransaction();
1118
+ return result;
1119
+ } catch (error) {
1120
+ logger7.error(`Error updating unit and associated areas:`, error);
1121
+ if (session?.inTransaction()) {
1122
+ await session?.abortTransaction();
1123
+ }
1124
+ throw error;
1125
+ } finally {
1126
+ session?.endSession();
1127
+ }
1128
+ }
1129
+ async function deleteUnit(_id) {
1130
+ const session = useAtlas3.getClient()?.startSession();
1131
+ try {
1132
+ session?.startTransaction();
1133
+ const isExistingArea = await verifyAreaByUnitId(_id);
1134
+ if (isExistingArea) {
1135
+ throw new BadRequestError7("Failed to delete unit, unit is in use.");
1136
+ }
1137
+ const result = await _deleteUnit(_id, session);
1138
+ await session?.commitTransaction();
1139
+ return result;
1140
+ } catch (error) {
1141
+ logger7.error(`Error deleting unit:`, error);
1142
+ if (session?.inTransaction()) {
1143
+ await session?.abortTransaction();
1144
+ }
1145
+ throw error;
1146
+ } finally {
1147
+ session?.endSession();
1148
+ }
1149
+ }
1150
+ return { importUnit, updateUnit, deleteUnit };
1100
1151
  }
1101
1152
 
1102
1153
  // src/controllers/hygiene-unit.controller.ts
1103
1154
  import { BadRequestError as BadRequestError8, logger as logger8 } from "@iservice365/node-server-utils";
1104
1155
  import Joi4 from "joi";
1105
1156
  function useUnitController() {
1157
+ const { createUnit: _createUnit, getUnits: _getUnits } = useUnitRepository();
1106
1158
  const {
1107
- createUnit: _createUnit,
1108
- getUnits: _getUnits,
1109
1159
  updateUnit: _updateUnit,
1110
- deleteUnit: _deleteUnit
1111
- } = useUnitRepository();
1112
- const { importUnit: _importUnit } = useUnitService();
1160
+ deleteUnit: _deleteUnit,
1161
+ importUnit: _importUnit
1162
+ } = useUnitService();
1113
1163
  async function createUnit(req, res, next) {
1114
1164
  const payload = { ...req.body, ...req.params };
1115
1165
  const { error } = unitSchema.validate(payload);
@@ -1270,7 +1320,7 @@ function MParentChecklist(value) {
1270
1320
  // src/repositories/hygiene-parent-checklist.repository.ts
1271
1321
  import { ObjectId as ObjectId6 } from "mongodb";
1272
1322
  import {
1273
- useAtlas as useAtlas3,
1323
+ useAtlas as useAtlas4,
1274
1324
  InternalServerError as InternalServerError3,
1275
1325
  paginate as paginate3,
1276
1326
  useCache as useCache3,
@@ -1279,7 +1329,7 @@ import {
1279
1329
  BadRequestError as BadRequestError10
1280
1330
  } from "@iservice365/node-server-utils";
1281
1331
  function useParentChecklistRepo() {
1282
- const db = useAtlas3.getDb();
1332
+ const db = useAtlas4.getDb();
1283
1333
  if (!db) {
1284
1334
  throw new InternalServerError3("Unable to connect to server.");
1285
1335
  }
@@ -1442,164 +1492,37 @@ function useParentChecklistRepo() {
1442
1492
  throw error;
1443
1493
  }
1444
1494
  }
1445
- async function updateParentChecklistStatuses(createdAt) {
1495
+ async function updateParentChecklistStatuses(_id, status, session) {
1446
1496
  try {
1447
- const currentDate = /* @__PURE__ */ new Date();
1448
- const dateToUpdate = createdAt || new Date(currentDate.getTime() - 24 * 60 * 60 * 1e3);
1449
- const startOfDay = new Date(dateToUpdate);
1450
- startOfDay.setUTCHours(0, 0, 0, 0);
1451
- const endOfDay = new Date(dateToUpdate);
1452
- endOfDay.setUTCHours(23, 59, 59, 999);
1453
- logger10.info(
1454
- `Updating parent checklist statuses for date: ${dateToUpdate.toISOString().split("T")[0]}`
1455
- );
1456
- const statusUpdates = await collection.aggregate([
1457
- {
1458
- $match: {
1459
- createdAt: {
1460
- $gte: startOfDay,
1461
- $lte: endOfDay
1462
- }
1463
- }
1464
- },
1465
- {
1466
- $lookup: {
1467
- from: "site.cleaning.schedule.areas",
1468
- let: { parentId: "$_id" },
1469
- pipeline: [
1470
- {
1471
- $match: {
1472
- $expr: { $eq: ["$parentChecklist", "$$parentId"] }
1473
- }
1474
- },
1475
- {
1476
- $group: {
1477
- _id: {
1478
- parentChecklist: "$parentChecklist"
1479
- },
1480
- completedCount: {
1481
- $sum: {
1482
- $cond: [{ $eq: ["$status", "completed"] }, 1, 0]
1483
- }
1484
- },
1485
- ongoingCount: {
1486
- $sum: {
1487
- $cond: [{ $eq: ["$status", "ongoing"] }, 1, 0]
1488
- }
1489
- },
1490
- readyCount: {
1491
- $sum: {
1492
- $cond: [{ $eq: ["$status", "ready"] }, 1, 0]
1493
- }
1494
- },
1495
- totalCount: { $sum: 1 }
1496
- }
1497
- },
1498
- {
1499
- $addFields: {
1500
- finalStatus: {
1501
- $cond: {
1502
- if: {
1503
- $and: [
1504
- { $gt: ["$completedCount", 0] },
1505
- { $eq: ["$ongoingCount", 0] },
1506
- { $eq: ["$readyCount", 0] }
1507
- ]
1508
- },
1509
- then: "completed",
1510
- else: {
1511
- $cond: {
1512
- if: {
1513
- $and: [
1514
- { $eq: ["$completedCount", 0] },
1515
- { $eq: ["$ongoingCount", 0] }
1516
- ]
1517
- },
1518
- then: "expired",
1519
- else: "ongoing"
1520
- }
1521
- }
1522
- }
1523
- },
1524
- completedAt: {
1525
- $cond: {
1526
- if: {
1527
- $and: [
1528
- { $gt: ["$completedCount", 0] },
1529
- { $eq: ["$ongoingCount", 0] },
1530
- { $eq: ["$readyCount", 0] }
1531
- ]
1532
- },
1533
- then: /* @__PURE__ */ new Date(),
1534
- else: null
1535
- }
1536
- }
1537
- }
1538
- }
1539
- ],
1540
- as: "areaStats"
1541
- }
1542
- },
1543
- {
1544
- $match: {
1545
- "areaStats.0": { $exists: true }
1546
- }
1547
- },
1548
- {
1549
- $addFields: {
1550
- areaData: { $arrayElemAt: ["$areaStats", 0] }
1551
- }
1552
- },
1553
- {
1554
- $project: {
1555
- _id: 1,
1556
- newStatus: "$areaData.finalStatus",
1557
- completedAt: "$areaData.completedAt"
1558
- }
1559
- }
1560
- ]).toArray();
1561
- logger10.info(
1562
- `Found ${statusUpdates.length} parent checklists to potentially update`
1497
+ _id = new ObjectId6(_id);
1498
+ } catch (error) {
1499
+ throw new BadRequestError10("Invalid parent checklist ID format.");
1500
+ }
1501
+ try {
1502
+ const updateValue = {
1503
+ status,
1504
+ updatedAt: /* @__PURE__ */ new Date()
1505
+ };
1506
+ const res = await collection.updateOne(
1507
+ { _id },
1508
+ { $set: updateValue },
1509
+ { session }
1563
1510
  );
1564
- if (statusUpdates.length === 0) {
1565
- logger10.info(
1566
- `No parent checklists found for date range: ${startOfDay.toISOString()} to ${endOfDay.toISOString()}`
1511
+ if (res.modifiedCount === 0) {
1512
+ throw new InternalServerError3(
1513
+ "Unable to update parent checklist status."
1567
1514
  );
1568
- return null;
1569
1515
  }
1570
- const bulkOps = statusUpdates.map((update) => {
1571
- logger10.info(
1572
- `Updating parent checklist ${update._id} with status: ${update.newStatus}`
1516
+ delNamespace().then(() => {
1517
+ logger10.info(`Cache cleared for namespace: ${namespace_collection}`);
1518
+ }).catch((err) => {
1519
+ logger10.error(
1520
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1521
+ err
1573
1522
  );
1574
- return {
1575
- updateOne: {
1576
- filter: { _id: update._id },
1577
- update: {
1578
- $set: {
1579
- status: update.newStatus,
1580
- completedAt: update.completedAt,
1581
- updatedAt: /* @__PURE__ */ new Date()
1582
- }
1583
- }
1584
- }
1585
- };
1586
1523
  });
1587
- let result = null;
1588
- if (bulkOps.length > 0) {
1589
- result = await collection.bulkWrite(bulkOps);
1590
- delNamespace().then(() => {
1591
- logger10.info(`Cache cleared for namespace: ${namespace_collection}`);
1592
- }).catch((err) => {
1593
- logger10.error(
1594
- `Failed to clear cache for namespace: ${namespace_collection}`,
1595
- err
1596
- );
1597
- });
1598
- }
1599
- logger10.info(`Updated statuses for ${bulkOps.length} parent checklists.`);
1600
- return result;
1524
+ return res.modifiedCount;
1601
1525
  } catch (error) {
1602
- logger10.error("Failed to update parent checklist statuses", error);
1603
1526
  throw error;
1604
1527
  }
1605
1528
  }
@@ -1726,6 +1649,7 @@ var areaChecklistSchema = Joi7.object({
1726
1649
  set: Joi7.number().min(0).required(),
1727
1650
  units: Joi7.array().items(
1728
1651
  Joi7.object({
1652
+ unit: Joi7.string().hex().required(),
1729
1653
  name: Joi7.string().required()
1730
1654
  }).required()
1731
1655
  ).min(1).required()
@@ -1750,9 +1674,11 @@ function MAreaChecklist(value) {
1750
1674
  return {
1751
1675
  set: checklistItem.set,
1752
1676
  units: checklistItem.units.map((unit) => ({
1677
+ unit: new ObjectId7(unit.unit),
1753
1678
  name: unit.name,
1754
1679
  status: "ready",
1755
1680
  remarks: "",
1681
+ completedBy: "",
1756
1682
  timestamp: ""
1757
1683
  }))
1758
1684
  };
@@ -1765,10 +1691,14 @@ function MAreaChecklist(value) {
1765
1691
  checklist: value.checklist || [],
1766
1692
  status: "ready",
1767
1693
  createdAt: /* @__PURE__ */ new Date(),
1694
+ completedAt: "",
1768
1695
  updatedAt: ""
1769
1696
  };
1770
1697
  }
1771
1698
 
1699
+ // src/services/hygiene-area-checklist.service.ts
1700
+ import { logger as logger14, useAtlas as useAtlas6 } from "@iservice365/node-server-utils";
1701
+
1772
1702
  // src/repositories/hygiene-area-checklist.repository.ts
1773
1703
  import {
1774
1704
  BadRequestError as BadRequestError13,
@@ -1776,23 +1706,18 @@ import {
1776
1706
  logger as logger13,
1777
1707
  makeCacheKey as makeCacheKey4,
1778
1708
  paginate as paginate4,
1779
- useAtlas as useAtlas4,
1709
+ useAtlas as useAtlas5,
1780
1710
  useCache as useCache4
1781
1711
  } from "@iservice365/node-server-utils";
1782
1712
  import { ObjectId as ObjectId8 } from "mongodb";
1783
1713
  function useAreaChecklistRepo() {
1784
- const db = useAtlas4.getDb();
1714
+ const db = useAtlas5.getDb();
1785
1715
  if (!db) {
1786
1716
  throw new InternalServerError4("Unable to connect to server.");
1787
1717
  }
1788
1718
  const namespace_collection = "site.cleaning.schedule.areas";
1789
- const unit_checklist_collection = "site.cleaning.schedule.units";
1790
1719
  const collection = db.collection(namespace_collection);
1791
- const unitChecklistCollection = db.collection(unit_checklist_collection);
1792
1720
  const { delNamespace, setCache, getCache } = useCache4(namespace_collection);
1793
- const { delNamespace: delUnitNamespace } = useCache4(
1794
- unit_checklist_collection
1795
- );
1796
1721
  async function createIndex() {
1797
1722
  try {
1798
1723
  await collection.createIndexes([
@@ -1800,7 +1725,8 @@ function useAreaChecklistRepo() {
1800
1725
  { key: { type: 1 } },
1801
1726
  { key: { status: 1 } },
1802
1727
  { key: { createdAt: 1 } },
1803
- { key: { acceptedBy: 1 } }
1728
+ { key: { "checklist.units.unit": 1 } },
1729
+ { key: { "checklist.units.status": 1 } }
1804
1730
  ]);
1805
1731
  } catch (error) {
1806
1732
  throw new InternalServerError4(
@@ -1810,7 +1736,11 @@ function useAreaChecklistRepo() {
1810
1736
  }
1811
1737
  async function createTextIndex() {
1812
1738
  try {
1813
- await collection.createIndex({ name: "text" });
1739
+ await collection.createIndex({
1740
+ name: "text",
1741
+ "checklist.units.name": "text",
1742
+ "checklist.units.remarks": "text"
1743
+ });
1814
1744
  } catch (error) {
1815
1745
  throw new InternalServerError4(
1816
1746
  "Failed to create text index on hygiene checklist area."
@@ -1861,7 +1791,7 @@ function useAreaChecklistRepo() {
1861
1791
  search = "",
1862
1792
  type,
1863
1793
  schedule
1864
- }) {
1794
+ }, session) {
1865
1795
  page = page > 0 ? page - 1 : 0;
1866
1796
  const query = {};
1867
1797
  const cacheOptions = {
@@ -1883,10 +1813,12 @@ function useAreaChecklistRepo() {
1883
1813
  cacheOptions.search = search;
1884
1814
  }
1885
1815
  const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
1886
- const cachedData = await getCache(cacheKey);
1887
- if (cachedData) {
1888
- logger13.info(`Cache hit for key: ${cacheKey}`);
1889
- return cachedData;
1816
+ if (!session) {
1817
+ const cachedData = await getCache(cacheKey);
1818
+ if (cachedData) {
1819
+ logger13.info(`Cache hit for key: ${cacheKey}`);
1820
+ return cachedData;
1821
+ }
1890
1822
  }
1891
1823
  try {
1892
1824
  const pipeline = [
@@ -1911,7 +1843,15 @@ function useAreaChecklistRepo() {
1911
1843
  set: {
1912
1844
  $cond: {
1913
1845
  if: { $gt: [{ $size: { $ifNull: ["$checklist", []] } }, 0] },
1914
- then: { $max: "$checklist.set" },
1846
+ then: {
1847
+ $max: {
1848
+ $map: {
1849
+ input: "$checklist",
1850
+ as: "item",
1851
+ in: { $toInt: "$$item.set" }
1852
+ }
1853
+ }
1854
+ },
1915
1855
  else: 0
1916
1856
  }
1917
1857
  }
@@ -1921,8 +1861,8 @@ function useAreaChecklistRepo() {
1921
1861
  { $skip: page * limit },
1922
1862
  { $limit: limit }
1923
1863
  ];
1924
- const items = await collection.aggregate(pipeline).toArray();
1925
- const length = await collection.countDocuments(query);
1864
+ const items = await collection.aggregate(pipeline, { session }).toArray();
1865
+ const length = await collection.countDocuments(query, { session });
1926
1866
  const data = paginate4(items, page, limit, length);
1927
1867
  setCache(cacheKey, data, 15 * 60).then(() => {
1928
1868
  logger13.info(`Cache set for key: ${cacheKey}`);
@@ -1938,31 +1878,27 @@ function useAreaChecklistRepo() {
1938
1878
  page = 1,
1939
1879
  limit = 10,
1940
1880
  search = "",
1941
- site,
1942
1881
  type,
1943
1882
  schedule,
1944
1883
  status,
1945
- createdAt,
1946
- user
1884
+ createdAt
1947
1885
  }) {
1948
1886
  page = page > 0 ? page - 1 : 0;
1949
- const query = { type };
1887
+ const query = {};
1950
1888
  const cacheOptions = {
1951
1889
  page,
1952
1890
  limit
1953
1891
  };
1954
- try {
1955
- query.site = new ObjectId8(site);
1956
- cacheOptions.site = site.toString();
1957
- } catch (error) {
1958
- throw new BadRequestError13("Invalid site ID format.");
1959
- }
1960
1892
  try {
1961
1893
  query.schedule = new ObjectId8(schedule);
1962
1894
  cacheOptions.schedule = schedule.toString();
1963
1895
  } catch (error) {
1964
1896
  throw new BadRequestError13("Invalid parent checklist ID format.");
1965
1897
  }
1898
+ if (type) {
1899
+ query.type = type;
1900
+ cacheOptions.type = type;
1901
+ }
1966
1902
  if (search) {
1967
1903
  query.$text = { $search: search };
1968
1904
  cacheOptions.search = search;
@@ -1980,14 +1916,6 @@ function useAreaChecklistRepo() {
1980
1916
  } else {
1981
1917
  query.status = { $in: ["ongoing", "completed"] };
1982
1918
  }
1983
- if (user) {
1984
- try {
1985
- query.acceptedBy = new ObjectId8(user);
1986
- cacheOptions.user = user.toString();
1987
- } catch (error) {
1988
- throw new BadRequestError13("Invalid user ID format.");
1989
- }
1990
- }
1991
1919
  const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
1992
1920
  const cachedData = await getCache(cacheKey);
1993
1921
  if (cachedData) {
@@ -1997,29 +1925,12 @@ function useAreaChecklistRepo() {
1997
1925
  try {
1998
1926
  const pipeline = [
1999
1927
  { $match: query },
2000
- {
2001
- $lookup: {
2002
- from: "users",
2003
- localField: "acceptedBy",
2004
- foreignField: "_id",
2005
- pipeline: [
2006
- ...search ? [{ $match: { name: { $regex: search, $options: "i" } } }] : [],
2007
- { $project: { name: 1 } }
2008
- ],
2009
- as: "acceptedBy"
2010
- }
2011
- },
2012
- {
2013
- $unwind: {
2014
- path: "$acceptedBy",
2015
- preserveNullAndEmptyArrays: true
2016
- }
2017
- },
2018
1928
  {
2019
1929
  $project: {
2020
1930
  name: 1,
1931
+ type: 1,
2021
1932
  createdAt: 1,
2022
- acceptedByName: "$acceptedBy.name",
1933
+ completedAt: 1,
2023
1934
  status: {
2024
1935
  $switch: {
2025
1936
  branches: [
@@ -2033,8 +1944,21 @@ function useAreaChecklistRepo() {
2033
1944
  default: "$status"
2034
1945
  }
2035
1946
  },
2036
- startedAt: "$acceptedAt",
2037
- endedAt: "$completedAt"
1947
+ set: {
1948
+ $cond: {
1949
+ if: { $gt: [{ $size: { $ifNull: ["$checklist", []] } }, 0] },
1950
+ then: {
1951
+ $max: {
1952
+ $map: {
1953
+ input: "$checklist",
1954
+ as: "item",
1955
+ in: { $toInt: "$$item.set" }
1956
+ }
1957
+ }
1958
+ },
1959
+ else: 0
1960
+ }
1961
+ }
2038
1962
  }
2039
1963
  },
2040
1964
  { $sort: { status: 1 } },
@@ -2072,75 +1996,120 @@ function useAreaChecklistRepo() {
2072
1996
  const areaPipeline = [
2073
1997
  { $match: { _id } },
2074
1998
  {
2075
- $lookup: {
2076
- from: "users",
2077
- localField: "createdBy",
2078
- foreignField: "_id",
2079
- pipeline: [{ $project: { name: 1 } }],
2080
- as: "createdBy"
1999
+ $project: {
2000
+ name: 1,
2001
+ createdAt: 1,
2002
+ completedAt: 1,
2003
+ status: {
2004
+ $switch: {
2005
+ branches: [
2006
+ { case: { $eq: ["$status", "ready"] }, then: "Ready" },
2007
+ { case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
2008
+ {
2009
+ case: { $eq: ["$status", "completed"] },
2010
+ then: "Completed"
2011
+ }
2012
+ ],
2013
+ default: "$status"
2014
+ }
2015
+ }
2016
+ }
2017
+ }
2018
+ ];
2019
+ const unitPipeline = [
2020
+ { $match: { _id } },
2021
+ {
2022
+ $unwind: {
2023
+ path: "$checklist",
2024
+ preserveNullAndEmptyArrays: false
2081
2025
  }
2082
2026
  },
2083
2027
  {
2084
2028
  $unwind: {
2085
- path: "$createdBy",
2086
- preserveNullAndEmptyArrays: true
2029
+ path: "$checklist.units",
2030
+ preserveNullAndEmptyArrays: false
2087
2031
  }
2088
2032
  },
2089
2033
  {
2090
2034
  $lookup: {
2091
2035
  from: "users",
2092
- localField: "acceptedBy",
2093
- foreignField: "_id",
2094
- pipeline: [{ $project: { name: 1 } }],
2095
- as: "acceptedBy"
2036
+ let: { completedById: "$checklist.units.completedBy" },
2037
+ pipeline: [
2038
+ {
2039
+ $match: {
2040
+ $expr: {
2041
+ $and: [
2042
+ { $ne: ["$$completedById", ""] },
2043
+ { $eq: ["$_id", "$$completedById"] }
2044
+ ]
2045
+ }
2046
+ }
2047
+ },
2048
+ { $project: { name: 1 } }
2049
+ ],
2050
+ as: "completedBy"
2096
2051
  }
2097
2052
  },
2098
2053
  {
2099
2054
  $unwind: {
2100
- path: "$acceptedBy",
2055
+ path: "$completedBy",
2101
2056
  preserveNullAndEmptyArrays: true
2102
2057
  }
2103
2058
  },
2104
2059
  {
2105
2060
  $project: {
2106
- name: 1,
2107
- createdAt: 1,
2108
- createdByName: "$createdBy.name",
2109
- startedAt: "$acceptedAt",
2110
- endedAt: "$completedAt",
2061
+ _id: 0,
2062
+ set: "$checklist.set",
2063
+ unit: "$checklist.units.unit",
2064
+ name: "$checklist.units.name",
2065
+ remarks: "$checklist.units.remarks",
2111
2066
  status: {
2112
2067
  $switch: {
2113
2068
  branches: [
2114
- { case: { $eq: ["$status", "ready"] }, then: "Ready" },
2115
- { case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
2116
2069
  {
2117
- case: { $eq: ["$status", "completed"] },
2070
+ case: { $eq: ["$checklist.units.status", "ready"] },
2071
+ then: "Ready"
2072
+ },
2073
+ {
2074
+ case: { $eq: ["$checklist.units.status", "completed"] },
2118
2075
  then: "Completed"
2119
2076
  }
2120
2077
  ],
2121
- default: "$status"
2078
+ default: "$checklist.units.status"
2122
2079
  }
2123
2080
  },
2124
- signature: 1,
2125
- acceptedByName: "$acceptedBy.name"
2081
+ completedByName: "$completedBy.name",
2082
+ timestamp: "$checklist.units.timestamp"
2126
2083
  }
2127
- }
2128
- ];
2129
- const unitPipeline = [
2130
- { $match: { areaChecklist: _id } },
2084
+ },
2085
+ { $sort: { set: 1, name: 1 } },
2086
+ {
2087
+ $group: {
2088
+ _id: "$set",
2089
+ units: {
2090
+ $push: {
2091
+ unit: "$unit",
2092
+ name: "$name",
2093
+ status: "$status",
2094
+ remarks: "$remarks",
2095
+ completedByName: "$completedByName",
2096
+ timestamp: "$timestamp"
2097
+ }
2098
+ }
2099
+ }
2100
+ },
2131
2101
  {
2132
2102
  $project: {
2133
- name: 1,
2134
- remarks: "$metadata.remarks",
2135
- attachments: "$metadata.attachments",
2136
- approve: { $ifNull: ["$approve", null] },
2137
- reject: { $ifNull: ["$reject", null] }
2103
+ _id: 0,
2104
+ set: "$_id",
2105
+ units: 1
2138
2106
  }
2139
- }
2107
+ },
2108
+ { $sort: { set: 1 } }
2140
2109
  ];
2141
2110
  const [area, units] = await Promise.all([
2142
2111
  collection.aggregate(areaPipeline).toArray(),
2143
- unitChecklistCollection.aggregate(unitPipeline).toArray()
2112
+ collection.aggregate(unitPipeline).toArray()
2144
2113
  ]);
2145
2114
  if (!area.length) {
2146
2115
  throw new BadRequestError13("Area checklist not found.");
@@ -2159,64 +2128,166 @@ function useAreaChecklistRepo() {
2159
2128
  throw error;
2160
2129
  }
2161
2130
  }
2162
- async function acceptAreaChecklist(_id, acceptedBy) {
2131
+ async function getAreaChecklistUnits({
2132
+ page = 1,
2133
+ limit = 10,
2134
+ search = "",
2135
+ _id
2136
+ }, session) {
2137
+ page = page > 0 ? page - 1 : 0;
2138
+ const query = {};
2139
+ const cacheOptions = {
2140
+ page,
2141
+ limit
2142
+ };
2163
2143
  try {
2164
- _id = new ObjectId8(_id);
2144
+ query._id = new ObjectId8(_id);
2145
+ cacheOptions._id = _id.toString();
2165
2146
  } catch (error) {
2166
- throw new BadRequestError13("Invalid area ID format.");
2147
+ throw new BadRequestError13("Invalid area checklist ID format.");
2167
2148
  }
2168
- try {
2169
- acceptedBy = new ObjectId8(acceptedBy);
2170
- } catch (error) {
2171
- throw new BadRequestError13("Invalid acceptedBy ID format.");
2149
+ if (search) {
2150
+ query.$text = { $search: search };
2151
+ cacheOptions.search = search;
2152
+ }
2153
+ const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
2154
+ if (!session) {
2155
+ const cachedData = await getCache(cacheKey);
2156
+ if (cachedData) {
2157
+ logger13.info(`Cache hit for key: ${cacheKey}`);
2158
+ return cachedData;
2159
+ }
2172
2160
  }
2173
2161
  try {
2174
- const now = /* @__PURE__ */ new Date();
2175
- const updateValue = {
2176
- metadata: {
2177
- acceptedBy,
2178
- acceptedAt: now
2162
+ const pipeline = [
2163
+ { $match: query },
2164
+ {
2165
+ $unwind: {
2166
+ path: "$checklist",
2167
+ preserveNullAndEmptyArrays: false
2168
+ }
2179
2169
  },
2180
- updatedAt: now
2181
- };
2182
- const res = await collection.updateOne({ _id }, { $set: updateValue });
2183
- if (res.modifiedCount === 0) {
2184
- throw new InternalServerError4("Unable to update cleaning area.");
2185
- }
2186
- delNamespace().then(() => {
2187
- logger13.info(`Cache cleared for namespace: ${namespace_collection}`);
2170
+ {
2171
+ $unwind: {
2172
+ path: "$checklist.units",
2173
+ preserveNullAndEmptyArrays: false
2174
+ }
2175
+ },
2176
+ {
2177
+ $project: {
2178
+ _id: 0,
2179
+ set: "$checklist.set",
2180
+ unit: "$checklist.units.unit",
2181
+ name: "$checklist.units.name",
2182
+ status: {
2183
+ $switch: {
2184
+ branches: [
2185
+ {
2186
+ case: { $eq: ["$checklist.units.status", "ready"] },
2187
+ then: "Ready"
2188
+ },
2189
+ {
2190
+ case: { $eq: ["$checklist.units.status", "completed"] },
2191
+ then: "Completed"
2192
+ }
2193
+ ],
2194
+ default: "$checklist.units.status"
2195
+ }
2196
+ },
2197
+ remarks: "$checklist.units.remarks"
2198
+ }
2199
+ },
2200
+ { $sort: { set: 1, name: 1 } },
2201
+ {
2202
+ $group: {
2203
+ _id: "$set",
2204
+ units: {
2205
+ $push: {
2206
+ unit: "$unit",
2207
+ name: "$name",
2208
+ status: "$status",
2209
+ remarks: "$remarks"
2210
+ }
2211
+ }
2212
+ }
2213
+ },
2214
+ {
2215
+ $project: {
2216
+ _id: 0,
2217
+ set: "$_id",
2218
+ units: 1
2219
+ }
2220
+ },
2221
+ { $sort: { set: 1 } },
2222
+ { $skip: page * limit },
2223
+ { $limit: limit }
2224
+ ];
2225
+ const countPipeline = [
2226
+ { $match: query },
2227
+ {
2228
+ $unwind: {
2229
+ path: "$checklist",
2230
+ preserveNullAndEmptyArrays: false
2231
+ }
2232
+ },
2233
+ {
2234
+ $group: {
2235
+ _id: "$checklist.set"
2236
+ }
2237
+ },
2238
+ { $count: "total" }
2239
+ ];
2240
+ const [items, countResult] = await Promise.all([
2241
+ collection.aggregate(pipeline, { session }).toArray(),
2242
+ collection.aggregate(countPipeline, { session }).toArray()
2243
+ ]);
2244
+ const length = countResult.length > 0 ? countResult[0].total : 0;
2245
+ const data = paginate4(items, page, limit, length);
2246
+ setCache(cacheKey, data, 15 * 60).then(() => {
2247
+ logger13.info(`Cache set for key: ${cacheKey}`);
2188
2248
  }).catch((err) => {
2189
- logger13.error(
2190
- `Failed to clear cache for namespace: ${namespace_collection}`,
2191
- err
2192
- );
2249
+ logger13.error(`Failed to set cache for key: ${cacheKey}`, err);
2193
2250
  });
2194
- return res.modifiedCount;
2251
+ return data;
2195
2252
  } catch (error) {
2196
2253
  throw error;
2197
2254
  }
2198
2255
  }
2199
- async function attachImageAreaChecklist(_id, attachments, session) {
2256
+ async function completeAreaChecklistUnits(_id, set, unitId, value, session) {
2200
2257
  try {
2201
2258
  _id = new ObjectId8(_id);
2202
2259
  } catch (error) {
2203
2260
  throw new BadRequestError13("Invalid area checklist ID format.");
2204
2261
  }
2262
+ try {
2263
+ unitId = new ObjectId8(unitId);
2264
+ } catch (error) {
2265
+ throw new BadRequestError13("Invalid unit checklist ID format.");
2266
+ }
2205
2267
  try {
2206
2268
  const now = /* @__PURE__ */ new Date();
2207
2269
  const updateValue = {
2208
- metadata: { attachments },
2270
+ "checklist.$[checklist].units.$[unit].timestamp": now,
2271
+ "checklist.$[checklist].units.$[unit].status": "completed",
2209
2272
  updatedAt: now
2210
2273
  };
2274
+ if (value.remarks) {
2275
+ updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
2276
+ }
2277
+ if (value.completedBy) {
2278
+ updateValue["checklist.$[checklist].units.$[unit].completedBy"] = new ObjectId8(value.completedBy);
2279
+ }
2280
+ const arrayFilters = [
2281
+ { "checklist.set": set, "checklist.units.unit": unitId },
2282
+ { "unit.unit": unitId }
2283
+ ];
2211
2284
  const res = await collection.updateOne(
2212
- { _id },
2285
+ { _id, "checklist.set": set, "checklist.units.unit": unitId },
2213
2286
  { $set: updateValue },
2214
- { session }
2287
+ { arrayFilters, session }
2215
2288
  );
2216
2289
  if (res.modifiedCount === 0) {
2217
- throw new InternalServerError4(
2218
- "Unable to update cleaning area checklist."
2219
- );
2290
+ throw new InternalServerError4("Unable to update area checklist unit.");
2220
2291
  }
2221
2292
  delNamespace().then(() => {
2222
2293
  logger13.info(`Cache cleared for namespace: ${namespace_collection}`);
@@ -2226,40 +2297,35 @@ function useAreaChecklistRepo() {
2226
2297
  err
2227
2298
  );
2228
2299
  });
2229
- delUnitNamespace().then(() => {
2230
- logger13.info(
2231
- `Cache cleared for namespace: ${unit_checklist_collection}`
2232
- );
2233
- }).catch((err) => {
2234
- logger13.error(
2235
- `Failed to clear cache for namespace: ${unit_checklist_collection}`,
2236
- err
2237
- );
2238
- });
2239
2300
  return res.modifiedCount;
2240
2301
  } catch (error) {
2302
+ logger13.error("Error updating area checklist unit:", error.message);
2241
2303
  throw error;
2242
2304
  }
2243
2305
  }
2244
- async function submitAreaChecklist(_id, signature) {
2306
+ async function updateAreaChecklistStatus(_id, status, session) {
2245
2307
  try {
2246
2308
  _id = new ObjectId8(_id);
2247
2309
  } catch (error) {
2248
- throw new BadRequestError13("Invalid area ID format.");
2310
+ throw new BadRequestError13("Invalid area checklist ID format.");
2249
2311
  }
2250
2312
  try {
2251
- const now = /* @__PURE__ */ new Date();
2252
2313
  const updateValue = {
2253
- metadata: {
2254
- signature,
2255
- completedAt: now
2256
- },
2257
- status: "ongoing",
2258
- updatedAt: now
2314
+ status,
2315
+ updatedAt: /* @__PURE__ */ new Date()
2259
2316
  };
2260
- const res = await collection.updateOne({ _id }, { $set: updateValue });
2317
+ if (status === "completed") {
2318
+ updateValue.completedAt = /* @__PURE__ */ new Date();
2319
+ }
2320
+ const res = await collection.updateOne(
2321
+ { _id },
2322
+ { $set: updateValue },
2323
+ { session }
2324
+ );
2261
2325
  if (res.modifiedCount === 0) {
2262
- throw new InternalServerError4("Unable to update cleaning area.");
2326
+ throw new InternalServerError4(
2327
+ "Unable to update area checklist status."
2328
+ );
2263
2329
  }
2264
2330
  delNamespace().then(() => {
2265
2331
  logger13.info(`Cache cleared for namespace: ${namespace_collection}`);
@@ -2274,35 +2340,21 @@ function useAreaChecklistRepo() {
2274
2340
  throw error;
2275
2341
  }
2276
2342
  }
2277
- async function completeAreaChecklist(_id, signature) {
2343
+ async function getAreaChecklistById(_id, session) {
2278
2344
  try {
2279
2345
  _id = new ObjectId8(_id);
2280
2346
  } catch (error) {
2281
- throw new BadRequestError13("Invalid area ID format.");
2347
+ throw new BadRequestError13("Invalid area checklist ID format.");
2282
2348
  }
2283
2349
  try {
2284
- const now = /* @__PURE__ */ new Date();
2285
- const updateValue = {
2286
- metadata: {
2287
- signature,
2288
- completedAt: now
2289
- },
2290
- status: "completed",
2291
- updatedAt: now
2292
- };
2293
- const res = await collection.updateOne({ _id }, { $set: updateValue });
2294
- if (res.modifiedCount === 0) {
2295
- throw new InternalServerError4("Unable to update cleaning area.");
2350
+ const area = await collection.findOne(
2351
+ { _id },
2352
+ { projection: { schedule: 1, name: 1, type: 1, status: 1 }, session }
2353
+ );
2354
+ if (!area) {
2355
+ throw new BadRequestError13("Area checklist not found.");
2296
2356
  }
2297
- delNamespace().then(() => {
2298
- logger13.info(`Cache cleared for namespace: ${namespace_collection}`);
2299
- }).catch((err) => {
2300
- logger13.error(
2301
- `Failed to clear cache for namespace: ${namespace_collection}`,
2302
- err
2303
- );
2304
- });
2305
- return res.modifiedCount;
2357
+ return area;
2306
2358
  } catch (error) {
2307
2359
  throw error;
2308
2360
  }
@@ -2314,24 +2366,27 @@ function useAreaChecklistRepo() {
2314
2366
  getAllAreaChecklist,
2315
2367
  getAreaChecklistHistory,
2316
2368
  getAreaChecklistHistoryDetails,
2317
- acceptAreaChecklist,
2318
- attachImageAreaChecklist,
2319
- submitAreaChecklist,
2320
- completeAreaChecklist
2369
+ getAreaChecklistUnits,
2370
+ getAreaChecklistById,
2371
+ updateAreaChecklistStatus,
2372
+ completeAreaChecklistUnits
2321
2373
  };
2322
2374
  }
2323
2375
 
2324
- // src/controllers/hygiene-area-checklist.controller.ts
2325
- import { BadRequestError as BadRequestError14, logger as logger15 } from "@iservice365/node-server-utils";
2326
- import Joi8 from "joi";
2327
-
2328
2376
  // src/services/hygiene-area-checklist.service.ts
2329
- import { logger as logger14, useAtlas as useAtlas5 } from "@iservice365/node-server-utils";
2330
2377
  function useAreaChecklistService() {
2331
- const { createAreaChecklist: _createAreaChecklist } = useAreaChecklistRepo();
2378
+ const {
2379
+ createAreaChecklist: _createAreaChecklist,
2380
+ getAllAreaChecklist,
2381
+ getAreaChecklistUnits,
2382
+ getAreaChecklistById,
2383
+ completeAreaChecklistUnits: _completeAreaChecklistUnits,
2384
+ updateAreaChecklistStatus
2385
+ } = useAreaChecklistRepo();
2332
2386
  const { getAreasForChecklist } = useAreaRepo();
2387
+ const { updateParentChecklistStatuses } = useParentChecklistRepo();
2333
2388
  async function createAreaChecklist(value) {
2334
- const session = useAtlas5.getClient()?.startSession();
2389
+ const session = useAtlas6.getClient()?.startSession();
2335
2390
  try {
2336
2391
  session?.startTransaction();
2337
2392
  const results = [];
@@ -2350,14 +2405,13 @@ function useAreaChecklistService() {
2350
2405
  schedule: value.schedule,
2351
2406
  name: area.name,
2352
2407
  type: "common",
2353
- checklist: area.units && area.units.length > 0 ? [
2354
- {
2355
- set: area.set || 0,
2356
- units: area.units.map((unit) => ({
2357
- name: unit.name
2358
- }))
2359
- }
2360
- ] : []
2408
+ checklist: area.units && area.units.length > 0 ? Array.from({ length: area.set || 1 }, (_, index) => ({
2409
+ set: index + 1,
2410
+ units: area.units.map((unit) => ({
2411
+ unit: unit.unit.toString(),
2412
+ name: unit.name
2413
+ }))
2414
+ })) : []
2361
2415
  };
2362
2416
  const insertedId = await _createAreaChecklist(
2363
2417
  checklistData,
@@ -2388,14 +2442,16 @@ function useAreaChecklistService() {
2388
2442
  schedule: value.schedule,
2389
2443
  name: toiletLocation.name,
2390
2444
  type: "toilet",
2391
- checklist: toiletLocation.units && toiletLocation.units.length > 0 ? [
2392
- {
2393
- set: toiletLocation.set || 0,
2445
+ checklist: toiletLocation.units && toiletLocation.units.length > 0 ? Array.from(
2446
+ { length: toiletLocation.set || 1 },
2447
+ (_, index) => ({
2448
+ set: index + 1,
2394
2449
  units: toiletLocation.units.map((unit) => ({
2450
+ unit: unit.unit.toString(),
2395
2451
  name: unit.name
2396
2452
  }))
2397
- }
2398
- ] : []
2453
+ })
2454
+ ) : []
2399
2455
  };
2400
2456
  const insertedId = await _createAreaChecklist(
2401
2457
  checklistData,
@@ -2428,21 +2484,107 @@ function useAreaChecklistService() {
2428
2484
  session?.endSession();
2429
2485
  }
2430
2486
  }
2431
- return { createAreaChecklist };
2487
+ async function completeAreaChecklistUnits(_id, set, unitId, value) {
2488
+ const session = useAtlas6.getClient()?.startSession();
2489
+ try {
2490
+ session?.startTransaction();
2491
+ await _completeAreaChecklistUnits(_id, set, unitId, value, session);
2492
+ const allUnitsResult = await getAreaChecklistUnits(
2493
+ {
2494
+ page: 1,
2495
+ limit: 1e3,
2496
+ _id: _id.toString()
2497
+ },
2498
+ session
2499
+ );
2500
+ let areaStatus = "ready";
2501
+ if (allUnitsResult && allUnitsResult.items && allUnitsResult.items.length > 0) {
2502
+ const sets = allUnitsResult.items;
2503
+ const allUnits = sets.flatMap((set2) => set2.units || []);
2504
+ const readyCount = allUnits.filter(
2505
+ (unit) => unit.status === "Ready"
2506
+ ).length;
2507
+ const completedCount = allUnits.filter(
2508
+ (unit) => unit.status === "Completed"
2509
+ ).length;
2510
+ const totalCount = allUnits.length;
2511
+ if (completedCount === totalCount) {
2512
+ areaStatus = "completed";
2513
+ } else if (readyCount === totalCount) {
2514
+ areaStatus = "ready";
2515
+ } else {
2516
+ areaStatus = "ongoing";
2517
+ }
2518
+ }
2519
+ await updateAreaChecklistStatus(_id, areaStatus, session);
2520
+ const currentArea = await getAreaChecklistById(_id, session);
2521
+ const scheduleId = currentArea.schedule;
2522
+ if (scheduleId) {
2523
+ const allAreasResult = await getAllAreaChecklist(
2524
+ {
2525
+ page: 1,
2526
+ limit: 1e3,
2527
+ schedule: scheduleId.toString()
2528
+ },
2529
+ session
2530
+ );
2531
+ if (allAreasResult && allAreasResult.items && allAreasResult.items.length > 0) {
2532
+ const areas = allAreasResult.items;
2533
+ const readyAreasCount = areas.filter(
2534
+ (area) => area.status === "Ready"
2535
+ ).length;
2536
+ const completedAreasCount = areas.filter(
2537
+ (area) => area.status === "Completed"
2538
+ ).length;
2539
+ const totalAreasCount = areas.length;
2540
+ let parentStatus = "ready";
2541
+ if (completedAreasCount === totalAreasCount) {
2542
+ parentStatus = "completed";
2543
+ } else if (readyAreasCount === totalAreasCount) {
2544
+ parentStatus = "ready";
2545
+ } else {
2546
+ parentStatus = "ongoing";
2547
+ }
2548
+ await updateParentChecklistStatuses(
2549
+ scheduleId,
2550
+ parentStatus,
2551
+ session
2552
+ );
2553
+ } else {
2554
+ logger14.info(
2555
+ "No area checklists found, keeping parent status as ready"
2556
+ );
2557
+ }
2558
+ }
2559
+ await session?.commitTransaction();
2560
+ return;
2561
+ } catch (error) {
2562
+ logger14.error(`Error updating area checklist unit and statuses:`, error);
2563
+ if (session?.inTransaction()) {
2564
+ await session?.abortTransaction();
2565
+ }
2566
+ throw error;
2567
+ } finally {
2568
+ session?.endSession();
2569
+ }
2570
+ }
2571
+ return { createAreaChecklist, completeAreaChecklistUnits };
2432
2572
  }
2433
2573
 
2434
2574
  // src/controllers/hygiene-area-checklist.controller.ts
2575
+ import { BadRequestError as BadRequestError14, logger as logger15 } from "@iservice365/node-server-utils";
2576
+ import Joi8 from "joi";
2435
2577
  function useAreaChecklistController() {
2436
2578
  const {
2437
2579
  getAllAreaChecklist: _getAllAreaChecklist,
2438
2580
  getAreaChecklistHistory: _getAreaChecklistHistory,
2439
2581
  getAreaChecklistHistoryDetails: _getAreaChecklistHistoryDetails,
2440
- acceptAreaChecklist: _acceptAreaChecklist,
2441
- attachImageAreaChecklist: _attachImageAreaChecklist,
2442
- submitAreaChecklist: _submitAreaChecklist,
2443
- completeAreaChecklist: _completeAreaChecklist
2582
+ getAreaChecklistUnits: _getAreaChecklistUnits
2444
2583
  } = useAreaChecklistRepo();
2445
- const { createAreaChecklist: _createAreaChecklist } = useAreaChecklistService();
2584
+ const {
2585
+ createAreaChecklist: _createAreaChecklist,
2586
+ completeAreaChecklistUnits: _completeAreaChecklistUnits
2587
+ } = useAreaChecklistService();
2446
2588
  async function createAreaChecklist(req, res, next) {
2447
2589
  const payload = {
2448
2590
  site: req.params.site,
@@ -2505,22 +2647,15 @@ function useAreaChecklistController() {
2505
2647
  }
2506
2648
  }
2507
2649
  async function getAreaChecklistHistory(req, res, next) {
2508
- const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2509
- (acc, [key, value]) => ({ ...acc, [key]: value }),
2510
- {}
2511
- ) : {};
2512
- const user = cookies["user"] || "";
2513
- const query = { ...req.query, ...req.params, user };
2650
+ const query = { ...req.query, ...req.params };
2514
2651
  const validation = Joi8.object({
2515
2652
  page: Joi8.number().min(1).optional().allow("", null),
2516
2653
  limit: Joi8.number().min(1).optional().allow("", null),
2517
2654
  search: Joi8.string().optional().allow("", null),
2518
- site: Joi8.string().hex().required(),
2519
- type: Joi8.string().valid(...allowedTypes).required(),
2655
+ type: Joi8.string().optional().allow("", ...allowedTypes),
2520
2656
  schedule: Joi8.string().hex().required(),
2521
2657
  status: Joi8.string().allow("", null, ...allowedStatus),
2522
- createdAt: Joi8.alternatives().try(Joi8.date(), Joi8.string()).optional().allow("", null),
2523
- user: Joi8.string().hex().optional().allow("", null)
2658
+ createdAt: Joi8.alternatives().try(Joi8.date(), Joi8.string()).optional().allow("", null)
2524
2659
  });
2525
2660
  const { error } = validation.validate(query);
2526
2661
  if (error) {
@@ -2531,7 +2666,6 @@ function useAreaChecklistController() {
2531
2666
  const page = parseInt(req.query.page) ?? 1;
2532
2667
  const limit = parseInt(req.query.limit) ?? 20;
2533
2668
  const search = req.query.search ?? "";
2534
- const site = req.params.site ?? "";
2535
2669
  const type = req.query.type ?? "";
2536
2670
  const schedule = req.params.schedule ?? "";
2537
2671
  const status = req.query.status ?? "";
@@ -2541,12 +2675,10 @@ function useAreaChecklistController() {
2541
2675
  page,
2542
2676
  limit,
2543
2677
  search,
2544
- site,
2545
2678
  type,
2546
2679
  schedule,
2547
2680
  status,
2548
- createdAt,
2549
- user
2681
+ createdAt
2550
2682
  });
2551
2683
  res.json(data);
2552
2684
  return;
@@ -2575,70 +2707,32 @@ function useAreaChecklistController() {
2575
2707
  return;
2576
2708
  }
2577
2709
  }
2578
- async function acceptAreaChecklist(req, res, next) {
2579
- const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2580
- (acc, [key, value]) => ({ ...acc, [key]: value }),
2581
- {}
2582
- ) : {};
2583
- const acceptedBy = cookies["user"] || "";
2584
- const payload = { id: req.params.id, acceptedBy };
2585
- const validation = Joi8.object({
2586
- id: Joi8.string().hex().required(),
2587
- acceptedBy: Joi8.string().hex().required()
2588
- });
2589
- const { error } = validation.validate(payload);
2590
- if (error) {
2591
- logger15.log({ level: "error", message: error.message });
2592
- next(new BadRequestError14(error.message));
2593
- return;
2594
- }
2595
- try {
2596
- await _acceptAreaChecklist(payload.id, payload.acceptedBy);
2597
- res.json({ message: "Area checklist updated successfully." });
2598
- return;
2599
- } catch (error2) {
2600
- logger15.log({ level: "error", message: error2.message });
2601
- next(error2);
2602
- return;
2603
- }
2604
- }
2605
- async function attachImageAreaChecklist(req, res, next) {
2606
- const payload = { id: req.params.id, attachments: req.body.attachments };
2607
- const validation = Joi8.object({
2608
- id: Joi8.string().hex().required(),
2609
- attachments: Joi8.array().items(Joi8.string()).optional()
2610
- });
2611
- const { error } = validation.validate(payload);
2612
- if (error) {
2613
- logger15.log({ level: "error", message: error.message });
2614
- next(new BadRequestError14(error.message));
2615
- return;
2616
- }
2617
- try {
2618
- await _attachImageAreaChecklist(payload.id, payload.attachments);
2619
- res.json({ message: "Area checklist attachments updated successfully." });
2620
- return;
2621
- } catch (error2) {
2622
- logger15.log({ level: "error", message: error2.message });
2623
- next(error2);
2624
- return;
2625
- }
2626
- }
2627
- async function submitAreaChecklist(req, res, next) {
2628
- const payload = { id: req.params.id, signature: req.body.signature };
2710
+ async function getAreaChecklistUnits(req, res, next) {
2711
+ const query = { ...req.query, ...req.params };
2629
2712
  const validation = Joi8.object({
2630
- id: Joi8.string().hex().required(),
2631
- signature: Joi8.string().required()
2713
+ page: Joi8.number().min(1).optional().allow("", null),
2714
+ limit: Joi8.number().min(1).optional().allow("", null),
2715
+ search: Joi8.string().optional().allow("", null),
2716
+ id: Joi8.string().hex().required()
2632
2717
  });
2633
- const { error } = validation.validate(payload);
2718
+ const { error } = validation.validate(query);
2634
2719
  if (error) {
2635
2720
  logger15.log({ level: "error", message: error.message });
2636
2721
  next(new BadRequestError14(error.message));
2637
2722
  return;
2638
2723
  }
2724
+ const page = parseInt(req.query.page) ?? 1;
2725
+ const limit = parseInt(req.query.limit) ?? 20;
2726
+ const search = req.query.search ?? "";
2727
+ const _id = req.params.id ?? "";
2639
2728
  try {
2640
- await _submitAreaChecklist(payload.id, payload.signature);
2641
- res.json({ message: "Area checklist submitted successfully." });
2729
+ const data = await _getAreaChecklistUnits({
2730
+ page,
2731
+ limit,
2732
+ search,
2733
+ _id
2734
+ });
2735
+ res.json(data);
2642
2736
  return;
2643
2737
  } catch (error2) {
2644
2738
  logger15.log({ level: "error", message: error2.message });
@@ -2646,11 +2740,23 @@ function useAreaChecklistController() {
2646
2740
  return;
2647
2741
  }
2648
2742
  }
2649
- async function completeAreaChecklist(req, res, next) {
2650
- const payload = { id: req.params.id, signature: req.body.signature };
2743
+ async function completeAreaChecklistUnits(req, res, next) {
2744
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2745
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
2746
+ {}
2747
+ ) : {};
2748
+ const completedBy = cookies["user"] || "";
2749
+ const payload = {
2750
+ ...req.params,
2751
+ ...req.body,
2752
+ completedBy
2753
+ };
2651
2754
  const validation = Joi8.object({
2652
2755
  id: Joi8.string().hex().required(),
2653
- signature: Joi8.string().required()
2756
+ set: Joi8.number().integer().min(1).required(),
2757
+ unit: Joi8.string().hex().required(),
2758
+ remarks: Joi8.string().optional().allow("", null),
2759
+ completedBy: Joi8.string().hex().required()
2654
2760
  });
2655
2761
  const { error } = validation.validate(payload);
2656
2762
  if (error) {
@@ -2659,8 +2765,14 @@ function useAreaChecklistController() {
2659
2765
  return;
2660
2766
  }
2661
2767
  try {
2662
- await _completeAreaChecklist(payload.id, payload.signature);
2663
- res.json({ message: "Area checklist completed successfully." });
2768
+ const { id, set, unit, ...value } = payload;
2769
+ await _completeAreaChecklistUnits(
2770
+ id,
2771
+ parseInt(set),
2772
+ unit,
2773
+ value
2774
+ );
2775
+ res.json({ message: "Area checklist updated successfully." });
2664
2776
  return;
2665
2777
  } catch (error2) {
2666
2778
  logger15.log({ level: "error", message: error2.message });
@@ -2673,30 +2785,25 @@ function useAreaChecklistController() {
2673
2785
  getAllAreaChecklist,
2674
2786
  getAreaChecklistHistory,
2675
2787
  getAreaChecklistHistoryDetails,
2676
- acceptAreaChecklist,
2677
- attachImageAreaChecklist,
2678
- submitAreaChecklist,
2679
- completeAreaChecklist
2788
+ getAreaChecklistUnits,
2789
+ completeAreaChecklistUnits
2680
2790
  };
2681
2791
  }
2682
2792
 
2683
- // src/models/hygiene-unit-checklist.model.ts
2793
+ // src/models/hygiene-supply.model.ts
2684
2794
  import { BadRequestError as BadRequestError15, logger as logger16 } from "@iservice365/node-server-utils";
2685
2795
  import Joi9 from "joi";
2686
2796
  import { ObjectId as ObjectId9 } from "mongodb";
2687
- var unitChecklistSchema = Joi9.object({
2797
+ var supplySchema = Joi9.object({
2688
2798
  site: Joi9.string().hex().required(),
2689
- type: Joi9.string().valid(...allowedTypes).required(),
2690
- parentChecklist: Joi9.string().hex().required(),
2691
- areaChecklist: Joi9.string().hex().required(),
2692
- unit: Joi9.string().hex().required(),
2693
- name: Joi9.string().optional().allow("", null),
2694
- createdBy: Joi9.string().hex().optional().allow("", null)
2799
+ name: Joi9.string().required(),
2800
+ unitOfMeasurement: Joi9.string().required(),
2801
+ qty: Joi9.number().min(0).required()
2695
2802
  });
2696
- function MUnitChecklist(value) {
2697
- const { error } = unitChecklistSchema.validate(value);
2803
+ function MSupply(value) {
2804
+ const { error } = supplySchema.validate(value);
2698
2805
  if (error) {
2699
- logger16.info(`Hygiene Checklist Unit Model: ${error.message}`);
2806
+ logger16.info(`Hygiene Supply Model: ${error.message}`);
2700
2807
  throw new BadRequestError15(error.message);
2701
2808
  }
2702
2809
  if (value.site) {
@@ -2706,84 +2813,43 @@ function MUnitChecklist(value) {
2706
2813
  throw new BadRequestError15("Invalid site ID format.");
2707
2814
  }
2708
2815
  }
2709
- if (value.parentChecklist) {
2710
- try {
2711
- value.parentChecklist = new ObjectId9(value.parentChecklist);
2712
- } catch (error2) {
2713
- throw new BadRequestError15("Invalid parent checklist ID format.");
2714
- }
2715
- }
2716
- if (value.areaChecklist) {
2717
- try {
2718
- value.areaChecklist = new ObjectId9(value.areaChecklist);
2719
- } catch (error2) {
2720
- throw new BadRequestError15("Invalid area checklist ID format.");
2721
- }
2722
- }
2723
- if (value.unit) {
2724
- try {
2725
- value.unit = new ObjectId9(value.unit);
2726
- } catch (error2) {
2727
- throw new BadRequestError15("Invalid unit ID format.");
2728
- }
2729
- }
2730
- if (value.createdBy) {
2731
- try {
2732
- value.createdBy = new ObjectId9(value.createdBy);
2733
- } catch (error2) {
2734
- throw new BadRequestError15("Invalid createdBy ID format.");
2735
- }
2736
- }
2737
2816
  return {
2738
2817
  site: value.site,
2739
- type: value.type,
2740
- parentChecklist: value.parentChecklist,
2741
- areaChecklist: value.areaChecklist,
2742
- unit: value.unit,
2743
- name: value.name ?? "",
2744
- approve: false,
2745
- reject: false,
2746
- createdBy: value.createdBy ?? "",
2818
+ name: value.name,
2819
+ unitOfMeasurement: value.unitOfMeasurement,
2820
+ qty: value.qty,
2747
2821
  createdAt: /* @__PURE__ */ new Date(),
2748
- updatedAt: value.updatedAt ?? ""
2822
+ status: "active",
2823
+ updatedAt: "",
2824
+ deletedAt: ""
2749
2825
  };
2750
2826
  }
2751
2827
 
2752
- // src/repositories/hygiene-unit-checklist.repository.ts
2828
+ // src/repositories/hygiene-supply.repository.ts
2829
+ import { ObjectId as ObjectId10 } from "mongodb";
2753
2830
  import {
2754
- BadRequestError as BadRequestError16,
2831
+ useAtlas as useAtlas7,
2755
2832
  InternalServerError as InternalServerError5,
2756
- logger as logger17,
2757
- makeCacheKey as makeCacheKey5,
2758
2833
  paginate as paginate5,
2759
- useAtlas as useAtlas6,
2760
- useCache as useCache5
2834
+ BadRequestError as BadRequestError16,
2835
+ useCache as useCache5,
2836
+ logger as logger17,
2837
+ makeCacheKey as makeCacheKey5
2761
2838
  } from "@iservice365/node-server-utils";
2762
- import { ObjectId as ObjectId10 } from "mongodb";
2763
- function useUnitChecklistRepo() {
2764
- const db = useAtlas6.getDb();
2839
+ function useSupplyRepository() {
2840
+ const db = useAtlas7.getDb();
2765
2841
  if (!db) {
2766
2842
  throw new InternalServerError5("Unable to connect to server.");
2767
2843
  }
2768
- const namespace_collection = "hygiene-checklist.units";
2844
+ const namespace_collection = "site.supplies";
2769
2845
  const collection = db.collection(namespace_collection);
2770
2846
  const { delNamespace, setCache, getCache } = useCache5(namespace_collection);
2771
2847
  async function createIndex() {
2772
2848
  try {
2773
- await collection.createIndexes([
2774
- { key: { site: 1 } },
2775
- { key: { type: 1 } },
2776
- { key: { parentChecklist: 1 } },
2777
- { key: { areaChecklist: 1 } },
2778
- { key: { "metadata.workOrder.category": 1 } },
2779
- { key: { "metadata.workOrder.createdBy": 1 } },
2780
- { key: { "metadata.workOrder.serviceProvider": 1 } },
2781
- { key: { "metadata.workOrder.organization": 1 } },
2782
- { key: { "metadata.workOrder.site": 1 } }
2783
- ]);
2849
+ await collection.createIndexes([{ key: { site: 1 } }]);
2784
2850
  } catch (error) {
2785
2851
  throw new InternalServerError5(
2786
- "Failed to create index on hygiene unit checklist."
2852
+ "Failed to create index on hygiene supply."
2787
2853
  );
2788
2854
  }
2789
2855
  }
@@ -2792,13 +2858,25 @@ function useUnitChecklistRepo() {
2792
2858
  await collection.createIndex({ name: "text" });
2793
2859
  } catch (error) {
2794
2860
  throw new InternalServerError5(
2795
- "Failed to create text index on hygiene unit checklist."
2861
+ "Failed to create text index on hygiene supply."
2862
+ );
2863
+ }
2864
+ }
2865
+ async function createUniqueIndex() {
2866
+ try {
2867
+ await collection.createIndex(
2868
+ { site: 1, name: 1, deletedAt: 1 },
2869
+ { unique: true }
2870
+ );
2871
+ } catch (error) {
2872
+ throw new InternalServerError5(
2873
+ "Failed to create unique index on hygiene supply."
2796
2874
  );
2797
2875
  }
2798
2876
  }
2799
- async function createUnitChecklist(value, session) {
2877
+ async function createSupply(value, session) {
2800
2878
  try {
2801
- value = MUnitChecklist(value);
2879
+ value = MSupply(value);
2802
2880
  const res = await collection.insertOne(value, { session });
2803
2881
  delNamespace().then(() => {
2804
2882
  logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
@@ -2810,42 +2888,34 @@ function useUnitChecklistRepo() {
2810
2888
  });
2811
2889
  return res.insertedId;
2812
2890
  } catch (error) {
2891
+ const isDuplicated = error.message.includes("duplicate");
2892
+ if (isDuplicated) {
2893
+ throw new BadRequestError16("Supply already exists.");
2894
+ }
2813
2895
  throw error;
2814
2896
  }
2815
2897
  }
2816
- async function getAllUnitChecklist({
2898
+ async function getSupplies({
2817
2899
  page = 1,
2818
2900
  limit = 10,
2819
2901
  search = "",
2820
- site,
2821
- type,
2822
- parentChecklist,
2823
- areaChecklist
2902
+ site
2824
2903
  }) {
2825
2904
  page = page > 0 ? page - 1 : 0;
2826
- const query = { type };
2905
+ const query = {
2906
+ status: { $ne: "deleted" }
2907
+ };
2827
2908
  const cacheOptions = {
2828
2909
  page,
2829
2910
  limit
2830
2911
  };
2831
2912
  try {
2832
- query.site = new ObjectId10(site);
2913
+ site = new ObjectId10(site);
2914
+ query.site = site;
2833
2915
  cacheOptions.site = site.toString();
2834
2916
  } catch (error) {
2835
2917
  throw new BadRequestError16("Invalid site ID format.");
2836
2918
  }
2837
- try {
2838
- query.parentChecklist = new ObjectId10(parentChecklist);
2839
- cacheOptions.parentChecklist = parentChecklist.toString();
2840
- } catch (error) {
2841
- throw new BadRequestError16("Invalid parent checklist ID format.");
2842
- }
2843
- try {
2844
- query.areaChecklist = new ObjectId10(areaChecklist);
2845
- cacheOptions.areaChecklist = areaChecklist.toString();
2846
- } catch (error) {
2847
- throw new BadRequestError16("Invalid area checklist ID format.");
2848
- }
2849
2919
  if (search) {
2850
2920
  query.$text = { $search: search };
2851
2921
  cacheOptions.search = search;
@@ -2857,120 +2927,20 @@ function useUnitChecklistRepo() {
2857
2927
  return cachedData;
2858
2928
  }
2859
2929
  try {
2860
- const areaAttachmentsResult = await collection.aggregate([
2861
- { $match: query },
2862
- {
2863
- $lookup: {
2864
- from: "hygiene-checklist.areas",
2865
- localField: "areaChecklist",
2866
- foreignField: "_id",
2867
- pipeline: [
2868
- { $project: { attachments: "$metadata.attachments" } }
2869
- ],
2870
- as: "areaChecklistData"
2871
- }
2872
- },
2873
- {
2874
- $unwind: {
2875
- path: "$areaChecklistData",
2876
- preserveNullAndEmptyArrays: true
2877
- }
2878
- },
2879
- {
2880
- $group: {
2881
- _id: null,
2882
- attachments: { $first: "$areaChecklistData.attachments" }
2883
- }
2884
- }
2885
- ]).toArray();
2886
- const areaAttachments = areaAttachmentsResult.length > 0 ? areaAttachmentsResult[0].attachments || [] : [];
2887
- const pipeline = [
2930
+ const items = await collection.aggregate([
2888
2931
  { $match: query },
2889
- {
2890
- $lookup: {
2891
- from: "organizations",
2892
- localField: "metadata.workOrder.category",
2893
- foreignField: "_id",
2894
- pipeline: [{ $project: { nature: 1 } }],
2895
- as: "categoryData"
2896
- }
2897
- },
2898
- {
2899
- $lookup: {
2900
- from: "users",
2901
- localField: "metadata.workOrder.createdBy",
2902
- foreignField: "_id",
2903
- pipeline: [{ $project: { name: 1 } }],
2904
- as: "createdByData"
2905
- }
2906
- },
2907
- {
2908
- $lookup: {
2909
- from: "service-providers",
2910
- localField: "metadata.workOrder.serviceProvider",
2911
- foreignField: "_id",
2912
- pipeline: [{ $project: { name: 1 } }],
2913
- as: "serviceProviderData"
2914
- }
2915
- },
2916
- {
2917
- $lookup: {
2918
- from: "organizations",
2919
- localField: "metadata.workOrder.organization",
2920
- foreignField: "_id",
2921
- pipeline: [{ $project: { name: 1 } }],
2922
- as: "organizationData"
2923
- }
2924
- },
2925
- {
2926
- $lookup: {
2927
- from: "sites",
2928
- localField: "metadata.workOrder.site",
2929
- foreignField: "_id",
2930
- pipeline: [{ $project: { name: 1 } }],
2931
- as: "siteData"
2932
- }
2933
- },
2934
- {
2935
- $addFields: {
2936
- "metadata.workOrder.categoryName": {
2937
- $arrayElemAt: ["$categoryData.nature", 0]
2938
- },
2939
- "metadata.workOrder.createdByName": {
2940
- $arrayElemAt: ["$createdByData.name", 0]
2941
- },
2942
- "metadata.workOrder.serviceProviderName": {
2943
- $arrayElemAt: ["$serviceProviderData.name", 0]
2944
- },
2945
- "metadata.workOrder.organizationName": {
2946
- $arrayElemAt: ["$organizationData.name", 0]
2947
- },
2948
- "metadata.workOrder.siteName": {
2949
- $arrayElemAt: ["$siteData.name", 0]
2950
- }
2951
- }
2952
- },
2953
2932
  {
2954
2933
  $project: {
2955
2934
  name: 1,
2956
- remarks: "$metadata.remarks",
2957
- attachments: "$metadata.attachments",
2958
- workOrder: "$metadata.workOrder",
2959
- approve: { $ifNull: ["$approve", null] },
2960
- reject: { $ifNull: ["$reject", null] }
2935
+ qty: 1
2961
2936
  }
2962
2937
  },
2963
2938
  { $sort: { _id: -1 } },
2964
2939
  { $skip: page * limit },
2965
2940
  { $limit: limit }
2966
- ];
2967
- const items = await collection.aggregate(pipeline).toArray();
2941
+ ]).toArray();
2968
2942
  const length = await collection.countDocuments(query);
2969
- const paginatedData = paginate5(items, page, limit, length);
2970
- const data = {
2971
- attachments: areaAttachments,
2972
- ...paginatedData
2973
- };
2943
+ const data = paginate5(items, page, limit, length);
2974
2944
  setCache(cacheKey, data, 15 * 60).then(() => {
2975
2945
  logger17.info(`Cache set for key: ${cacheKey}`);
2976
2946
  }).catch((err) => {
@@ -2981,78 +2951,58 @@ function useUnitChecklistRepo() {
2981
2951
  throw error;
2982
2952
  }
2983
2953
  }
2984
- async function getUnitChecklistById(_id) {
2954
+ async function updateSupply(_id, value, session) {
2985
2955
  try {
2986
2956
  _id = new ObjectId10(_id);
2987
2957
  } catch (error) {
2988
- throw new BadRequestError16("Invalid unit checklist ID format.");
2958
+ throw new BadRequestError16("Invalid supply ID format.");
2989
2959
  }
2990
2960
  try {
2991
- return await collection.findOne({ _id });
2961
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
2962
+ const res = await collection.updateOne(
2963
+ { _id },
2964
+ { $set: updateValue },
2965
+ { session }
2966
+ );
2967
+ if (res.modifiedCount === 0) {
2968
+ throw new InternalServerError5("Unable to update cleaning supply.");
2969
+ }
2970
+ delNamespace().then(() => {
2971
+ logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
2972
+ }).catch((err) => {
2973
+ logger17.error(
2974
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2975
+ err
2976
+ );
2977
+ });
2978
+ return res.modifiedCount;
2992
2979
  } catch (error) {
2980
+ const isDuplicated = error.message.includes("duplicate");
2981
+ if (isDuplicated) {
2982
+ throw new BadRequestError16("Area already exists.");
2983
+ }
2993
2984
  throw error;
2994
2985
  }
2995
2986
  }
2996
- async function updateUnitChecklist(_id, value, session) {
2987
+ async function deleteSupply(_id, session) {
2997
2988
  try {
2998
2989
  _id = new ObjectId10(_id);
2999
2990
  } catch (error) {
3000
- throw new BadRequestError16("Invalid unit checklist ID format.");
3001
- }
3002
- if (value.checkedBy && typeof value.checkedBy === "string") {
3003
- try {
3004
- value.checkedBy = new ObjectId10(value.checkedBy);
3005
- } catch (error) {
3006
- throw new BadRequestError16("Invalid checkedBy ID format.");
3007
- }
3008
- }
3009
- if ("metadata" in value && value.metadata?.workOrder) {
3010
- const workOrder = value.metadata.workOrder;
3011
- if (workOrder.category && typeof workOrder.category === "string") {
3012
- try {
3013
- workOrder.category = new ObjectId10(workOrder.category);
3014
- } catch (error) {
3015
- throw new BadRequestError16("Invalid category ID format.");
3016
- }
3017
- }
3018
- if (workOrder.createdBy && typeof workOrder.createdBy === "string") {
3019
- try {
3020
- workOrder.createdBy = new ObjectId10(workOrder.createdBy);
3021
- } catch (error) {
3022
- throw new BadRequestError16("Invalid createdBy ID format.");
3023
- }
3024
- }
3025
- if (workOrder.serviceProvider && typeof workOrder.serviceProvider === "string") {
3026
- try {
3027
- workOrder.serviceProvider = new ObjectId10(workOrder.serviceProvider);
3028
- } catch (error) {
3029
- throw new BadRequestError16("Invalid serviceProvider ID format.");
3030
- }
3031
- }
3032
- if (workOrder.organization && typeof workOrder.organization === "string") {
3033
- try {
3034
- workOrder.organization = new ObjectId10(workOrder.organization);
3035
- } catch (error) {
3036
- throw new BadRequestError16("Invalid organization ID format.");
3037
- }
3038
- }
3039
- if (workOrder.site && typeof workOrder.site === "string") {
3040
- try {
3041
- workOrder.site = new ObjectId10(workOrder.site);
3042
- } catch (error) {
3043
- throw new BadRequestError16("Invalid site ID format.");
3044
- }
3045
- }
2991
+ throw new BadRequestError16("Invalid supply ID format.");
3046
2992
  }
3047
2993
  try {
3048
- const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
2994
+ const updateValue = {
2995
+ status: "deleted",
2996
+ updatedAt: /* @__PURE__ */ new Date(),
2997
+ deletedAt: /* @__PURE__ */ new Date()
2998
+ };
3049
2999
  const res = await collection.updateOne(
3050
3000
  { _id },
3051
3001
  { $set: updateValue },
3052
3002
  { session }
3053
3003
  );
3054
3004
  if (res.modifiedCount === 0) {
3055
- throw new InternalServerError5("Unable to update unit checklist.");
3005
+ throw new InternalServerError5("Unable to delete supply.");
3056
3006
  }
3057
3007
  delNamespace().then(() => {
3058
3008
  logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
@@ -3070,129 +3020,53 @@ function useUnitChecklistRepo() {
3070
3020
  return {
3071
3021
  createIndex,
3072
3022
  createTextIndex,
3073
- createUnitChecklist,
3074
- getAllUnitChecklist,
3075
- getUnitChecklistById,
3076
- updateUnitChecklist
3023
+ createUniqueIndex,
3024
+ createSupply,
3025
+ getSupplies,
3026
+ updateSupply,
3027
+ deleteSupply
3077
3028
  };
3078
3029
  }
3079
3030
 
3080
- // src/controllers/hygiene-unit-checklist.controller.ts
3081
- import { BadRequestError as BadRequestError17, logger as logger19 } from "@iservice365/node-server-utils";
3031
+ // src/controllers/hygiene-supply.controller.ts
3032
+ import { BadRequestError as BadRequestError17, logger as logger18 } from "@iservice365/node-server-utils";
3082
3033
  import Joi10 from "joi";
3083
-
3084
- // src/services/hygiene-unit-checklist.service.ts
3085
- import { logger as logger18, NotFoundError as NotFoundError4 } from "@iservice365/node-server-utils";
3086
- import {
3087
- useSiteRepo,
3088
- useWorkOrderService
3089
- } from "@iservice365/core";
3090
- function useUnitChecklistService() {
3091
- const {
3092
- getUnitChecklistById: _getUnitChecklistById,
3093
- updateUnitChecklist: _updateUnitChecklist
3094
- } = useUnitChecklistRepo();
3095
- const { getSiteById } = useSiteRepo();
3096
- const { createWorkOrder } = useWorkOrderService();
3097
- async function approveUnitChecklist(id, value) {
3098
- try {
3099
- value.approve = true;
3100
- value.reject = false;
3101
- const result = await _updateUnitChecklist(id, value);
3102
- return result;
3103
- } catch (error) {
3104
- logger18.error(`Error updating unit checklist with id ${id}:`, error);
3105
- throw error;
3106
- }
3107
- }
3108
- async function rejectUnitChecklist(id, value, fullHost) {
3109
- try {
3110
- value.reject = true;
3111
- value.approve = false;
3112
- if (value.metadata?.workOrder) {
3113
- const existingChecklist = await _getUnitChecklistById(id);
3114
- if (!existingChecklist)
3115
- throw new NotFoundError4("Unit checklist not found.");
3116
- const site = await getSiteById(
3117
- existingChecklist.site
3118
- );
3119
- if (!site)
3120
- throw new NotFoundError4("Site not found.");
3121
- const workOrderData = {
3122
- ...value.metadata.workOrder,
3123
- attachments: value.metadata.attachments,
3124
- createdBy: value.checkedBy,
3125
- organization: site.orgId,
3126
- site: site._id
3127
- };
3128
- const workOrder = await createWorkOrder(
3129
- workOrderData,
3130
- fullHost
3131
- );
3132
- if (!workOrder)
3133
- throw new NotFoundError4("Failed to create work order.");
3134
- }
3135
- const result = await _updateUnitChecklist(id, value);
3136
- return result;
3137
- } catch (error) {
3138
- logger18.error(`Error updating unit checklist with id ${id}:`, error);
3139
- throw error;
3140
- }
3141
- }
3142
- return {
3143
- approveUnitChecklist,
3144
- rejectUnitChecklist
3145
- };
3146
- }
3147
-
3148
- // src/controllers/hygiene-unit-checklist.controller.ts
3149
- import { workOrderSchema } from "@iservice365/core";
3150
- function useUnitChecklistController() {
3034
+ function useSupplyController() {
3151
3035
  const {
3152
- createUnitChecklist: _createUnitChecklist,
3153
- getAllUnitChecklist: _getAllUnitChecklist
3154
- } = useUnitChecklistRepo();
3155
- const {
3156
- approveUnitChecklist: _approveUnitChecklist,
3157
- rejectUnitChecklist: _rejectUnitChecklist
3158
- } = useUnitChecklistService();
3159
- async function createUnitChecklist(req, res, next) {
3160
- const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3161
- (acc, [key, value]) => ({ ...acc, [key]: value }),
3162
- {}
3163
- ) : {};
3164
- const createdBy = cookies["user"] || "";
3165
- const payload = { ...req.body, ...req.params, createdBy };
3166
- const { error } = unitChecklistSchema.validate(payload);
3036
+ createSupply: _createSupply,
3037
+ getSupplies: _getSupplies,
3038
+ updateSupply: _updateSupply,
3039
+ deleteSupply: _deleteSupply
3040
+ } = useSupplyRepository();
3041
+ async function createSupply(req, res, next) {
3042
+ const payload = { ...req.body, ...req.params };
3043
+ const { error } = supplySchema.validate(payload);
3167
3044
  if (error) {
3168
- logger19.log({ level: "error", message: error.message });
3045
+ logger18.log({ level: "error", message: error.message });
3169
3046
  next(new BadRequestError17(error.message));
3170
3047
  return;
3171
3048
  }
3172
3049
  try {
3173
- const id = await _createUnitChecklist(payload);
3174
- res.status(201).json({ message: "Unit checklist created successfully.", id });
3050
+ const id = await _createSupply(payload);
3051
+ res.status(201).json({ message: "Supply created successfully.", id });
3175
3052
  return;
3176
3053
  } catch (error2) {
3177
- logger19.log({ level: "error", message: error2.message });
3054
+ logger18.log({ level: "error", message: error2.message });
3178
3055
  next(error2);
3179
3056
  return;
3180
3057
  }
3181
3058
  }
3182
- async function getAllUnitChecklist(req, res, next) {
3059
+ async function getSupplies(req, res, next) {
3183
3060
  const query = { ...req.query, ...req.params };
3184
3061
  const validation = Joi10.object({
3185
3062
  page: Joi10.number().min(1).optional().allow("", null),
3186
3063
  limit: Joi10.number().min(1).optional().allow("", null),
3187
3064
  search: Joi10.string().optional().allow("", null),
3188
- site: Joi10.string().hex().required(),
3189
- type: Joi10.string().valid(...allowedTypes).required(),
3190
- parentChecklist: Joi10.string().hex().required(),
3191
- areaChecklist: Joi10.string().hex().required()
3065
+ site: Joi10.string().hex().required()
3192
3066
  });
3193
3067
  const { error } = validation.validate(query);
3194
3068
  if (error) {
3195
- logger19.log({ level: "error", message: error.message });
3069
+ logger18.log({ level: "error", message: error.message });
3196
3070
  next(new BadRequestError17(error.message));
3197
3071
  return;
3198
3072
  }
@@ -3200,132 +3074,98 @@ function useUnitChecklistController() {
3200
3074
  const limit = parseInt(req.query.limit) ?? 20;
3201
3075
  const search = req.query.search ?? "";
3202
3076
  const site = req.params.site ?? "";
3203
- const type = req.params.type ?? "";
3204
- const parentChecklist = req.params.parentChecklist ?? "";
3205
- const areaChecklist = req.params.areaChecklist ?? "";
3206
3077
  try {
3207
- const data = await _getAllUnitChecklist({
3078
+ const data = await _getSupplies({
3208
3079
  page,
3209
3080
  limit,
3210
3081
  search,
3211
- site,
3212
- type,
3213
- parentChecklist,
3214
- areaChecklist
3082
+ site
3215
3083
  });
3216
3084
  res.json(data);
3217
3085
  return;
3218
3086
  } catch (error2) {
3219
- logger19.log({ level: "error", message: error2.message });
3087
+ logger18.log({ level: "error", message: error2.message });
3220
3088
  next(error2);
3221
3089
  return;
3222
3090
  }
3223
3091
  }
3224
- async function approveUnitChecklist(req, res, next) {
3225
- const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3226
- (acc, [key, value]) => ({ ...acc, [key]: value }),
3227
- {}
3228
- ) : {};
3229
- const checkedBy = cookies["user"] || "";
3230
- const payload = { id: req.params.id, checkedBy };
3092
+ async function updateSupply(req, res, next) {
3093
+ const payload = { id: req.params.id, ...req.body };
3231
3094
  const validation = Joi10.object({
3232
3095
  id: Joi10.string().hex().required(),
3233
- checkedBy: Joi10.string().hex().required()
3096
+ name: Joi10.string().optional().allow("", null),
3097
+ unitOfMeasurement: Joi10.string().optional().allow("", null),
3098
+ qty: Joi10.number().min(0).optional().allow("", null)
3234
3099
  });
3235
3100
  const { error } = validation.validate(payload);
3236
3101
  if (error) {
3237
- logger19.log({ level: "error", message: error.message });
3102
+ logger18.log({ level: "error", message: error.message });
3238
3103
  next(new BadRequestError17(error.message));
3239
3104
  return;
3240
3105
  }
3241
3106
  try {
3242
3107
  const { id, ...value } = payload;
3243
- await _approveUnitChecklist(id, value);
3244
- res.json({ message: "Unit checklist approved successfully." });
3108
+ await _updateSupply(id, value);
3109
+ res.json({ message: "Supply updated successfully." });
3245
3110
  return;
3246
3111
  } catch (error2) {
3247
- logger19.log({ level: "error", message: error2.message });
3112
+ logger18.log({ level: "error", message: error2.message });
3248
3113
  next(error2);
3249
3114
  return;
3250
3115
  }
3251
3116
  }
3252
- async function rejectUnitChecklist(req, res, next) {
3253
- const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3254
- (acc, [key, value]) => ({ ...acc, [key]: value }),
3255
- {}
3256
- ) : {};
3257
- const checkedBy = cookies["user"] || "";
3258
- if (req.body.workOrder) {
3259
- req.body.workOrder.createdBy = checkedBy;
3260
- }
3261
- const payload = { id: req.params.id, checkedBy, ...req.body };
3117
+ async function deleteSupply(req, res, next) {
3118
+ const id = req.params.id;
3262
3119
  const validation = Joi10.object({
3263
- id: Joi10.string().hex().required(),
3264
- attachments: Joi10.array().items(Joi10.string()).optional(),
3265
- remarks: Joi10.string().required(),
3266
- workOrder: workOrderSchema.optional(),
3267
- checkedBy: Joi10.string().hex().required()
3120
+ id: Joi10.string().hex().required()
3268
3121
  });
3269
- const { error } = validation.validate(payload);
3122
+ const { error } = validation.validate({ id });
3270
3123
  if (error) {
3271
- logger19.log({ level: "error", message: error.message });
3124
+ logger18.log({ level: "error", message: error.message });
3272
3125
  next(new BadRequestError17(error.message));
3273
3126
  return;
3274
3127
  }
3275
3128
  try {
3276
- const { id, attachments, remarks, workOrder, ...value } = payload;
3277
- value.metadata = {
3278
- attachments: attachments || [],
3279
- remarks: remarks || "",
3280
- workOrder
3281
- };
3282
- if (value.metadata.workOrder) {
3283
- if (value.metadata.workOrder.category) {
3284
- value.metadata.workOrder.category = value.metadata.workOrder.category;
3285
- }
3286
- if (value.metadata.workOrder.serviceProvider) {
3287
- value.metadata.workOrder.serviceProvider = value.metadata.workOrder.serviceProvider;
3288
- }
3289
- value.metadata.workOrder.createdBy = checkedBy;
3290
- }
3291
- const fullHost = req.headers.origin;
3292
- await _rejectUnitChecklist(id, value, fullHost);
3293
- res.json({ message: "Unit checklist rejected successfully." });
3129
+ await _deleteSupply(id);
3130
+ res.json({ message: "Supply deleted successfully." });
3294
3131
  return;
3295
3132
  } catch (error2) {
3296
- logger19.log({ level: "error", message: error2.message });
3133
+ logger18.log({ level: "error", message: error2.message });
3297
3134
  next(error2);
3298
3135
  return;
3299
3136
  }
3300
3137
  }
3301
3138
  return {
3302
- createUnitChecklist,
3303
- getAllUnitChecklist,
3304
- approveUnitChecklist,
3305
- rejectUnitChecklist
3139
+ createSupply,
3140
+ getSupplies,
3141
+ updateSupply,
3142
+ deleteSupply
3306
3143
  };
3307
3144
  }
3308
3145
  export {
3309
3146
  MArea,
3310
3147
  MAreaChecklist,
3311
3148
  MParentChecklist,
3149
+ MSupply,
3312
3150
  MUnit,
3313
- MUnitChecklist,
3314
3151
  allowedChecklistStatus,
3152
+ allowedStatus,
3153
+ allowedTypes,
3315
3154
  areaChecklistSchema,
3316
3155
  areaSchema,
3317
3156
  parentChecklistSchema,
3318
- unitChecklistSchema,
3157
+ supplySchema,
3319
3158
  unitSchema,
3320
3159
  useAreaChecklistController,
3321
3160
  useAreaChecklistRepo,
3161
+ useAreaChecklistService,
3322
3162
  useAreaController,
3323
3163
  useAreaRepo,
3324
3164
  useAreaService,
3325
3165
  useParentChecklistController,
3326
3166
  useParentChecklistRepo,
3327
- useUnitChecklistController,
3328
- useUnitChecklistRepo,
3167
+ useSupplyController,
3168
+ useSupplyRepository,
3329
3169
  useUnitController,
3330
3170
  useUnitRepository,
3331
3171
  useUnitService