@7365admin1/module-hygiene 4.11.0 → 4.13.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,4 +1,15 @@
1
1
  // src/models/hygiene-base.model.ts
2
+ var ServiceType = /* @__PURE__ */ ((ServiceType2) => {
3
+ ServiceType2["REAL_ESTATE_DEVELOPER"] = "real_estate_developer";
4
+ ServiceType2["PROPERTY_MANAGEMENT_AGENCY"] = "property_management_agency";
5
+ ServiceType2["SECURITY_AGENCY"] = "security_agency";
6
+ ServiceType2["CLEANING_SERVICES"] = "cleaning_services";
7
+ ServiceType2["MECHANICAL_ELECTRICAL_SERVICES"] = "mechanical_electrical_services";
8
+ ServiceType2["LANDSCAPING_SERVICES"] = "landscaping_services";
9
+ ServiceType2["PEST_CONTROL_SERVICES"] = "pest_control_services";
10
+ ServiceType2["POOL_MAINTENANCE_SERVICES"] = "pool_maintenance_services";
11
+ return ServiceType2;
12
+ })(ServiceType || {});
2
13
  var allowedTypes = ["common", "toilet"];
3
14
  var allowedStatus = [
4
15
  "open",
@@ -547,6 +558,7 @@ import { ObjectId as ObjectId2 } from "mongodb";
547
558
  import { BadRequestError as BadRequestError3, logger as logger3 } from "@7365admin1/node-server-utils";
548
559
  var areaSchema = Joi2.object({
549
560
  site: Joi2.string().hex().required(),
561
+ serviceType: Joi2.string().valid(...Object.values(ServiceType)).required(),
550
562
  name: Joi2.string().required(),
551
563
  type: Joi2.string().valid(...allowedTypes).required(),
552
564
  set: Joi2.number().min(0).optional(),
@@ -586,6 +598,7 @@ function MArea(value) {
586
598
  }
587
599
  return {
588
600
  site: value.site,
601
+ serviceType: value.serviceType,
589
602
  name: value.name,
590
603
  type: value.type,
591
604
  set: value.set ?? 0,
@@ -630,6 +643,7 @@ function useAreaRepo() {
630
643
  try {
631
644
  await collection.createIndexes([
632
645
  { key: { site: 1 } },
646
+ { key: { serviceType: 1 } },
633
647
  { key: { type: 1 } },
634
648
  { key: { status: 1 } },
635
649
  { key: { "units.unit": 1 } }
@@ -650,7 +664,7 @@ function useAreaRepo() {
650
664
  async function createUniqueIndex() {
651
665
  try {
652
666
  await collection.createIndex(
653
- { site: 1, name: 1, type: 1, deletedAt: 1 },
667
+ { site: 1, serviceType: 1, name: 1, type: 1, deletedAt: 1 },
654
668
  { unique: true }
655
669
  );
656
670
  } catch (error) {
@@ -687,15 +701,18 @@ function useAreaRepo() {
687
701
  limit = 10,
688
702
  search = "",
689
703
  type = "all",
690
- site
704
+ site,
705
+ serviceType
691
706
  }) {
692
707
  page = page > 0 ? page - 1 : 0;
693
708
  const query = {
694
- status: { $ne: "deleted" }
709
+ status: { $ne: "deleted" },
710
+ serviceType
695
711
  };
696
712
  const cacheOptions = {
697
713
  page,
698
- limit
714
+ limit,
715
+ serviceType
699
716
  };
700
717
  try {
701
718
  site = new ObjectId3(site);
@@ -744,9 +761,15 @@ function useAreaRepo() {
744
761
  throw error;
745
762
  }
746
763
  }
747
- async function getAreasForChecklist(site) {
748
- const query = { status: { $ne: "deleted" } };
764
+ async function getAreasForChecklist(site, serviceType) {
765
+ const query = {
766
+ status: { $ne: "deleted" }
767
+ };
749
768
  const cacheOptions = {};
769
+ if (serviceType) {
770
+ query.serviceType = serviceType;
771
+ cacheOptions.serviceType = serviceType;
772
+ }
750
773
  try {
751
774
  site = new ObjectId3(site);
752
775
  query.site = site;
@@ -826,28 +849,6 @@ function useAreaRepo() {
826
849
  throw error;
827
850
  }
828
851
  }
829
- async function getAreaByMultipleId(_id) {
830
- for (let i = 0; i < _id.length; i++) {
831
- try {
832
- _id[i] = new ObjectId3(_id[i]);
833
- } catch (error) {
834
- throw new BadRequestError4("Invalid area ID format.");
835
- }
836
- }
837
- const query = {
838
- _id: { $in: _id },
839
- status: { $ne: "deleted" }
840
- };
841
- try {
842
- const data = await collection.aggregate([{ $match: query }]).toArray();
843
- if (!data || data.length === 0) {
844
- throw new NotFoundError("Area not found.");
845
- }
846
- return data;
847
- } catch (error) {
848
- throw error;
849
- }
850
- }
851
852
  async function verifyAreaByUnitId(unitId) {
852
853
  try {
853
854
  unitId = new ObjectId3(unitId);
@@ -1009,7 +1010,6 @@ function useAreaRepo() {
1009
1010
  getAreas,
1010
1011
  getAreasForChecklist,
1011
1012
  getAreaById,
1012
- getAreaByMultipleId,
1013
1013
  verifyAreaByUnitId,
1014
1014
  updateArea,
1015
1015
  updateAreaChecklist,
@@ -1088,6 +1088,7 @@ import { ObjectId as ObjectId4 } from "mongodb";
1088
1088
  import { BadRequestError as BadRequestError5, logger as logger6 } from "@7365admin1/node-server-utils";
1089
1089
  var unitSchema = Joi3.object({
1090
1090
  site: Joi3.string().hex().required(),
1091
+ serviceType: Joi3.string().valid(...Object.values(ServiceType)).required(),
1091
1092
  name: Joi3.string().required()
1092
1093
  });
1093
1094
  function MUnit(value) {
@@ -1105,6 +1106,7 @@ function MUnit(value) {
1105
1106
  }
1106
1107
  return {
1107
1108
  site: value.site,
1109
+ serviceType: value.serviceType,
1108
1110
  name: value.name,
1109
1111
  status: "active",
1110
1112
  createdAt: /* @__PURE__ */ new Date(),
@@ -1126,6 +1128,7 @@ function useUnitRepository() {
1126
1128
  try {
1127
1129
  await collection.createIndexes([
1128
1130
  { key: { site: 1 } },
1131
+ { key: { serviceType: 1 } },
1129
1132
  { key: { status: 1 } }
1130
1133
  ]);
1131
1134
  } catch (error) {
@@ -1144,7 +1147,7 @@ function useUnitRepository() {
1144
1147
  async function createUniqueIndex() {
1145
1148
  try {
1146
1149
  await collection.createIndex(
1147
- { site: 1, name: 1, deletedAt: 1 },
1150
+ { site: 1, serviceType: 1, name: 1, deletedAt: 1 },
1148
1151
  { unique: true }
1149
1152
  );
1150
1153
  } catch (error) {
@@ -1178,7 +1181,8 @@ function useUnitRepository() {
1178
1181
  page = 1,
1179
1182
  limit = 10,
1180
1183
  search = "",
1181
- site
1184
+ site,
1185
+ serviceType
1182
1186
  }) {
1183
1187
  page = page > 0 ? page - 1 : 0;
1184
1188
  const query = {
@@ -1195,6 +1199,10 @@ function useUnitRepository() {
1195
1199
  } catch (error) {
1196
1200
  throw new BadRequestError6("Invalid site ID format.");
1197
1201
  }
1202
+ if (serviceType) {
1203
+ query.serviceType = serviceType;
1204
+ cacheOptions.serviceType = serviceType;
1205
+ }
1198
1206
  if (search) {
1199
1207
  query.$or = [{ name: { $regex: search, $options: "i" } }];
1200
1208
  cacheOptions.search = search;
@@ -1253,7 +1261,7 @@ function useUnitRepository() {
1253
1261
  } catch (error) {
1254
1262
  const isDuplicated = error.message.includes("duplicate");
1255
1263
  if (isDuplicated) {
1256
- throw new BadRequestError6("Area already exists.");
1264
+ throw new BadRequestError6("Unit already exists.");
1257
1265
  }
1258
1266
  throw error;
1259
1267
  }
@@ -1309,7 +1317,8 @@ function useAreaService() {
1309
1317
  const { getUnits: _getUnits } = useUnitRepository();
1310
1318
  async function importArea({
1311
1319
  dataJson,
1312
- site
1320
+ site,
1321
+ serviceType
1313
1322
  }) {
1314
1323
  let dataArray;
1315
1324
  try {
@@ -1330,7 +1339,8 @@ function useAreaService() {
1330
1339
  page: 1,
1331
1340
  limit: 999999,
1332
1341
  search: "",
1333
- site
1342
+ site,
1343
+ serviceType
1334
1344
  });
1335
1345
  if (unitsData && unitsData.items) {
1336
1346
  availableUnits = unitsData.items;
@@ -1377,7 +1387,8 @@ function useAreaService() {
1377
1387
  const areaData = {
1378
1388
  type: areaType,
1379
1389
  name: areaName,
1380
- site
1390
+ site,
1391
+ serviceType
1381
1392
  };
1382
1393
  if (row.SET !== void 0 && row.SET !== null && row.SET !== "") {
1383
1394
  const setNumber = parseInt(String(row.SET).trim());
@@ -1471,9 +1482,12 @@ function useAreaService() {
1471
1482
  }
1472
1483
  }
1473
1484
  }
1474
- async function exportAreas(site) {
1485
+ async function exportAreas({
1486
+ site,
1487
+ serviceType
1488
+ }) {
1475
1489
  try {
1476
- const areas = await getAreasForChecklist(site);
1490
+ const areas = await getAreasForChecklist(site, serviceType);
1477
1491
  if (!areas || !Array.isArray(areas) || areas.length === 0) {
1478
1492
  throw new BadRequestError7(
1479
1493
  "There are no areas to export yet. Please add some areas first, then try again."
@@ -1560,7 +1574,8 @@ function useAreaController() {
1560
1574
  limit: Joi4.number().min(1).optional().allow("", null),
1561
1575
  search: Joi4.string().optional().allow("", null),
1562
1576
  type: Joi4.string().valid("all", ...allowedTypes).optional().allow("", null),
1563
- site: Joi4.string().hex().required()
1577
+ site: Joi4.string().hex().required(),
1578
+ serviceType: Joi4.string().valid(...Object.values(ServiceType)).required()
1564
1579
  });
1565
1580
  const { error } = validation.validate(query);
1566
1581
  if (error) {
@@ -1573,13 +1588,15 @@ function useAreaController() {
1573
1588
  const search = req.query.search ?? "";
1574
1589
  const type = req.query.type ?? "all";
1575
1590
  const site = req.params.site ?? "";
1591
+ const serviceType = req.query.serviceType ?? "";
1576
1592
  try {
1577
1593
  const data = await _getAreas({
1578
1594
  page,
1579
1595
  limit,
1580
1596
  search,
1581
1597
  type,
1582
- site
1598
+ site,
1599
+ serviceType
1583
1600
  });
1584
1601
  res.json(data);
1585
1602
  return;
@@ -1664,9 +1681,12 @@ function useAreaController() {
1664
1681
  next(new BadRequestError8("File is required!"));
1665
1682
  return;
1666
1683
  }
1667
- const { site } = req.params;
1668
- const schema = Joi4.string().hex().required();
1669
- const { error, value } = schema.validate(site);
1684
+ const query = { ...req.query, ...req.params };
1685
+ const validation = Joi4.object({
1686
+ site: Joi4.string().hex().required(),
1687
+ serviceType: Joi4.string().valid(...Object.values(ServiceType)).required()
1688
+ });
1689
+ const { error, value } = validation.validate(query);
1670
1690
  if (error) {
1671
1691
  logger9.log({ level: "error", message: error.message });
1672
1692
  next(new BadRequestError8(error.message));
@@ -1675,7 +1695,7 @@ function useAreaController() {
1675
1695
  try {
1676
1696
  const xlsData = await convertBufferFile(req.file.buffer);
1677
1697
  const dataJson = JSON.stringify(xlsData);
1678
- const result = await _importArea({ dataJson, site: value });
1698
+ const result = await _importArea({ dataJson, ...value });
1679
1699
  return res.status(201).json(result);
1680
1700
  } catch (error2) {
1681
1701
  logger9.log({ level: "error", message: error2.message });
@@ -1684,9 +1704,12 @@ function useAreaController() {
1684
1704
  }
1685
1705
  }
1686
1706
  async function exportAreas(req, res, next) {
1687
- const { site } = req.params;
1688
- const validation = Joi4.string().hex().required();
1689
- const { error, value } = validation.validate(site);
1707
+ const query = { ...req.query, ...req.params };
1708
+ const validation = Joi4.object({
1709
+ site: Joi4.string().hex().required(),
1710
+ serviceType: Joi4.string().valid(...Object.values(ServiceType)).required()
1711
+ });
1712
+ const { error, value } = validation.validate(query);
1690
1713
  if (error) {
1691
1714
  logger9.log({ level: "error", message: error.message });
1692
1715
  next(new BadRequestError8(error.message));
@@ -1782,7 +1805,8 @@ function useUnitService() {
1782
1805
  } = useUnitRepository();
1783
1806
  async function importUnit({
1784
1807
  dataJson,
1785
- site
1808
+ site,
1809
+ serviceType
1786
1810
  }) {
1787
1811
  let dataArray;
1788
1812
  try {
@@ -1823,7 +1847,8 @@ function useUnitService() {
1823
1847
  try {
1824
1848
  const insertedId = await _createUnit({
1825
1849
  name: unitName,
1826
- site
1850
+ site,
1851
+ serviceType
1827
1852
  });
1828
1853
  insertedUnitIds.push(insertedId);
1829
1854
  logger11.info(`Successfully created unit: ${unitName}`);
@@ -1928,7 +1953,10 @@ function useUnitService() {
1928
1953
  session?.endSession();
1929
1954
  }
1930
1955
  }
1931
- async function exportUnits(site) {
1956
+ async function exportUnits({
1957
+ site,
1958
+ serviceType
1959
+ }) {
1932
1960
  const { generateUnitExcel: _generateUnitExcel } = useUnitExportService();
1933
1961
  const { getUnits: _getUnits } = useUnitRepository();
1934
1962
  try {
@@ -1936,7 +1964,8 @@ function useUnitService() {
1936
1964
  page: 1,
1937
1965
  limit: 999999,
1938
1966
  search: "",
1939
- site
1967
+ site,
1968
+ serviceType
1940
1969
  });
1941
1970
  if (!data || !data.items || data.items.length === 0) {
1942
1971
  throw new BadRequestError9(
@@ -1993,7 +2022,8 @@ function useUnitController() {
1993
2022
  page: Joi5.number().min(1).optional().allow("", null),
1994
2023
  limit: Joi5.number().min(1).optional().allow("", null),
1995
2024
  search: Joi5.string().optional().allow("", null),
1996
- site: Joi5.string().hex().required()
2025
+ site: Joi5.string().hex().required(),
2026
+ serviceType: Joi5.string().valid(...Object.values(ServiceType)).required()
1997
2027
  });
1998
2028
  const { error } = validation.validate(query);
1999
2029
  if (error) {
@@ -2005,12 +2035,14 @@ function useUnitController() {
2005
2035
  const limit = parseInt(req.query.limit) ?? 10;
2006
2036
  const search = req.query.search ?? "";
2007
2037
  const site = req.params.site ?? "";
2038
+ const serviceType = req.query.serviceType ?? "";
2008
2039
  try {
2009
2040
  const data = await _getUnits({
2010
2041
  page,
2011
2042
  limit,
2012
2043
  search,
2013
- site
2044
+ site,
2045
+ serviceType
2014
2046
  });
2015
2047
  res.json(data);
2016
2048
  return;
@@ -2069,9 +2101,12 @@ function useUnitController() {
2069
2101
  next(new BadRequestError10("File is required!"));
2070
2102
  return;
2071
2103
  }
2072
- const { site } = req.params;
2073
- const validation = Joi5.string().hex().required();
2074
- const { error, value } = validation.validate(site);
2104
+ const query = { ...req.query, ...req.params };
2105
+ const validation = Joi5.object({
2106
+ site: Joi5.string().hex().required(),
2107
+ serviceType: Joi5.string().valid(...Object.values(ServiceType)).required()
2108
+ });
2109
+ const { error, value } = validation.validate(query);
2075
2110
  if (error) {
2076
2111
  logger12.log({ level: "error", message: error.message });
2077
2112
  next(new BadRequestError10(error.message));
@@ -2080,7 +2115,7 @@ function useUnitController() {
2080
2115
  try {
2081
2116
  const xlsData = await convertBufferFile(req.file.buffer);
2082
2117
  const dataJson = JSON.stringify(xlsData);
2083
- const result = await _importUnit({ dataJson, site: value });
2118
+ const result = await _importUnit({ dataJson, ...value });
2084
2119
  return res.status(201).json(result);
2085
2120
  } catch (error2) {
2086
2121
  logger12.log({ level: "error", message: error2.message });
@@ -2089,9 +2124,12 @@ function useUnitController() {
2089
2124
  }
2090
2125
  }
2091
2126
  async function exportUnits(req, res, next) {
2092
- const { site } = req.params;
2093
- const validation = Joi5.string().hex().required();
2094
- const { error, value } = validation.validate(site);
2127
+ const query = { ...req.query, ...req.params };
2128
+ const validation = Joi5.object({
2129
+ site: Joi5.string().hex().required(),
2130
+ serviceType: Joi5.string().valid(...Object.values(ServiceType)).required()
2131
+ });
2132
+ const { error, value } = validation.validate(query);
2095
2133
  if (error) {
2096
2134
  logger12.log({ level: "error", message: error.message });
2097
2135
  next(new BadRequestError10(error.message));
@@ -2137,7 +2175,8 @@ import { ObjectId as ObjectId6 } from "mongodb";
2137
2175
  import { BadRequestError as BadRequestError11, logger as logger13 } from "@7365admin1/node-server-utils";
2138
2176
  var parentChecklistSchema = Joi6.object({
2139
2177
  createdAt: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null),
2140
- site: Joi6.string().hex().required()
2178
+ site: Joi6.string().hex().required(),
2179
+ serviceType: Joi6.string().valid(...Object.values(ServiceType)).required()
2141
2180
  });
2142
2181
  function MParentChecklist(value) {
2143
2182
  const { error } = parentChecklistSchema.validate(value);
@@ -2154,6 +2193,7 @@ function MParentChecklist(value) {
2154
2193
  }
2155
2194
  return {
2156
2195
  site: value.site,
2196
+ serviceType: value.serviceType,
2157
2197
  status: "open",
2158
2198
  createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
2159
2199
  updatedAt: value.updatedAt ?? ""
@@ -2236,12 +2276,16 @@ function useParentChecklistRepo() {
2236
2276
  );
2237
2277
  return existingChecklist._id;
2238
2278
  }
2279
+ const allServiceTypes = Object.values(ServiceType);
2239
2280
  if (value.site) {
2240
- const checklistDoc = MParentChecklist({
2241
- site: value.site,
2242
- createdAt: currentDate
2243
- });
2244
- const result2 = await collection.insertOne(checklistDoc, { session });
2281
+ const checklistDocs2 = allServiceTypes.map(
2282
+ (serviceType) => MParentChecklist({
2283
+ site: value.site,
2284
+ createdAt: currentDate,
2285
+ serviceType
2286
+ })
2287
+ );
2288
+ const result2 = await collection.insertMany(checklistDocs2, { session });
2245
2289
  delNamespace().then(() => {
2246
2290
  logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
2247
2291
  }).catch((err) => {
@@ -2252,9 +2296,9 @@ function useParentChecklistRepo() {
2252
2296
  });
2253
2297
  const dateStr2 = currentDate.toISOString().split("T")[0];
2254
2298
  logger14.info(
2255
- `Created parent checklist for site ${value.site} for today: ${dateStr2}`
2299
+ `Created ${checklistDocs2.length} parent checklists for site ${value.site} for today: ${dateStr2}`
2256
2300
  );
2257
- return result2.insertedId;
2301
+ return Object.values(result2.insertedIds);
2258
2302
  }
2259
2303
  const siteIds = await getHygieneSiteIds();
2260
2304
  if (!Array.isArray(siteIds)) {
@@ -2267,12 +2311,15 @@ function useParentChecklistRepo() {
2267
2311
  }
2268
2312
  const checklistDocs = [];
2269
2313
  for (const site of siteIds) {
2270
- checklistDocs.push(
2271
- MParentChecklist({
2272
- site,
2273
- createdAt: currentDate
2274
- })
2275
- );
2314
+ for (const serviceType of allServiceTypes) {
2315
+ checklistDocs.push(
2316
+ MParentChecklist({
2317
+ site,
2318
+ createdAt: currentDate,
2319
+ serviceType
2320
+ })
2321
+ );
2322
+ }
2276
2323
  }
2277
2324
  const result = await collection.insertMany(checklistDocs, { session });
2278
2325
  delNamespace().then(() => {
@@ -2300,13 +2347,15 @@ function useParentChecklistRepo() {
2300
2347
  site,
2301
2348
  startDate = "",
2302
2349
  endDate = "",
2303
- status = "all"
2350
+ status = "all",
2351
+ serviceType
2304
2352
  }) {
2305
2353
  page = page > 0 ? page - 1 : 0;
2306
- const query = {};
2354
+ const query = { serviceType };
2307
2355
  const cacheOptions = {
2308
2356
  page,
2309
- limit
2357
+ limit,
2358
+ serviceType
2310
2359
  };
2311
2360
  try {
2312
2361
  site = new ObjectId7(site);
@@ -2486,12 +2535,30 @@ function useParentChecklistRepo() {
2486
2535
  throw error;
2487
2536
  }
2488
2537
  }
2538
+ async function getTodayParentChecklists() {
2539
+ const now = /* @__PURE__ */ new Date();
2540
+ const start = new Date(now);
2541
+ start.setUTCHours(0, 0, 0, 0);
2542
+ const end = new Date(now);
2543
+ end.setUTCHours(23, 59, 59, 999);
2544
+ try {
2545
+ const items = await collection.find(
2546
+ { createdAt: { $gte: start, $lte: end } },
2547
+ { projection: { _id: 1, site: 1 } }
2548
+ ).toArray();
2549
+ return items;
2550
+ } catch (error) {
2551
+ logger14.error("Failed to get today's parent checklists", error);
2552
+ throw error;
2553
+ }
2554
+ }
2489
2555
  return {
2490
2556
  createIndex,
2491
2557
  createParentChecklist,
2492
2558
  getAllParentChecklist,
2493
2559
  updateParentChecklistStatuses,
2494
- closeExpiredParentChecklists
2560
+ closeExpiredParentChecklists,
2561
+ getTodayParentChecklists
2495
2562
  };
2496
2563
  }
2497
2564
 
@@ -2528,6 +2595,7 @@ function useParentChecklistController() {
2528
2595
  limit: Joi7.number().min(1).optional().allow("", null),
2529
2596
  search: Joi7.string().optional().allow("", null),
2530
2597
  site: Joi7.string().hex().required(),
2598
+ serviceType: Joi7.string().valid(...Object.values(ServiceType)).required(),
2531
2599
  startDate: Joi7.alternatives().try(Joi7.date(), Joi7.string()).optional().allow("", null),
2532
2600
  endDate: Joi7.alternatives().try(Joi7.date(), Joi7.string()).optional().allow("", null),
2533
2601
  status: Joi7.string().valid(...allowedStatus, "all").optional().allow("", null)
@@ -2542,6 +2610,7 @@ function useParentChecklistController() {
2542
2610
  const limit = parseInt(req.query.limit) ?? 10;
2543
2611
  const search = req.query.search ?? "";
2544
2612
  const site = req.params.site ?? "";
2613
+ const serviceType = req.query.serviceType ?? "";
2545
2614
  const startDate = req.query.startDate ?? "";
2546
2615
  const endDate = req.query.endDate ?? "";
2547
2616
  const status = req.query.status ?? "all";
@@ -2551,6 +2620,7 @@ function useParentChecklistController() {
2551
2620
  limit,
2552
2621
  search,
2553
2622
  site,
2623
+ serviceType,
2554
2624
  startDate,
2555
2625
  endDate,
2556
2626
  status
@@ -2587,7 +2657,9 @@ var areaChecklistSchema = Joi8.object({
2587
2657
  unit: Joi8.string().hex().required(),
2588
2658
  name: Joi8.string().required()
2589
2659
  }).required()
2590
- ).min(1).required()
2660
+ ).min(1).required(),
2661
+ isScheduleTask: Joi8.boolean().optional().default(false),
2662
+ scheduleTaskId: Joi8.string().hex().optional().allow(null)
2591
2663
  }).required()
2592
2664
  ).optional().default([]),
2593
2665
  createdBy: Joi8.string().hex().required()
@@ -2616,6 +2688,8 @@ function MAreaChecklist(value) {
2616
2688
  value.checklist = value.checklist.map((checklistItem) => {
2617
2689
  return {
2618
2690
  set: checklistItem.set,
2691
+ isScheduleTask: checklistItem.isScheduleTask ?? false,
2692
+ scheduleTaskId: checklistItem.scheduleTaskId ?? void 0,
2619
2693
  units: checklistItem.units.map((unit) => ({
2620
2694
  unit: new ObjectId8(unit.unit),
2621
2695
  name: unit.name,
@@ -2651,6 +2725,7 @@ function MAreaChecklist(value) {
2651
2725
  }
2652
2726
 
2653
2727
  // src/services/hygiene-area-checklist.service.ts
2728
+ import { ObjectId as ObjectId10 } from "mongodb";
2654
2729
  import {
2655
2730
  logger as logger18,
2656
2731
  UnauthorizedError,
@@ -2706,26 +2781,26 @@ function useAreaChecklistRepo() {
2706
2781
  );
2707
2782
  }
2708
2783
  }
2784
+ async function createUniqueIndex() {
2785
+ try {
2786
+ await collection.createIndex({ schedule: 1, area: 1 }, { unique: true });
2787
+ } catch (error) {
2788
+ throw new InternalServerError5(
2789
+ "Failed to create unique index on hygiene checklist area."
2790
+ );
2791
+ }
2792
+ }
2709
2793
  async function createAreaChecklist(value, session) {
2710
2794
  try {
2711
2795
  const parentChecklistId = new ObjectId9(value.schedule);
2712
- const currentDate = /* @__PURE__ */ new Date();
2713
- const startOfDay = new Date(currentDate);
2714
- startOfDay.setUTCHours(0, 0, 0, 0);
2715
- const endOfDay = new Date(currentDate);
2716
- endOfDay.setUTCHours(23, 59, 59, 999);
2717
- const existingChecklist = await collection.findOne({
2718
- type: value.type,
2719
- schedule: parentChecklistId,
2720
- name: value.name,
2721
- createdAt: {
2722
- $gte: startOfDay,
2723
- $lte: endOfDay
2724
- }
2725
- });
2796
+ const areaId = new ObjectId9(value.area);
2797
+ const existingChecklist = await collection.findOne(
2798
+ { schedule: parentChecklistId, area: areaId },
2799
+ { session }
2800
+ );
2726
2801
  if (existingChecklist) {
2727
2802
  logger17.info(
2728
- `Area checklist already exists for area ${value.name} on ${currentDate.toISOString().split("T")[0]}`
2803
+ `Area checklist already exists for area ${value.name} (schedule: ${parentChecklistId}, area: ${areaId})`
2729
2804
  );
2730
2805
  return existingChecklist._id;
2731
2806
  }
@@ -2741,6 +2816,14 @@ function useAreaChecklistRepo() {
2741
2816
  });
2742
2817
  return result.insertedId;
2743
2818
  } catch (error) {
2819
+ if (error?.code === 11e3) {
2820
+ logger17.info(`Duplicate area checklist skipped for area ${value.name}`);
2821
+ const existing = await collection.findOne({
2822
+ schedule: new ObjectId9(value.schedule),
2823
+ area: new ObjectId9(value.area)
2824
+ });
2825
+ return existing._id;
2826
+ }
2744
2827
  throw error;
2745
2828
  }
2746
2829
  }
@@ -3078,6 +3161,9 @@ function useAreaChecklistRepo() {
3078
3161
  $project: {
3079
3162
  _id: 0,
3080
3163
  set: "$checklist.set",
3164
+ isScheduleTask: {
3165
+ $ifNull: ["$checklist.isScheduleTask", false]
3166
+ },
3081
3167
  unit: "$checklist.units.unit",
3082
3168
  name: "$checklist.units.name",
3083
3169
  remarks: "$checklist.units.remarks",
@@ -3108,6 +3194,7 @@ function useAreaChecklistRepo() {
3108
3194
  {
3109
3195
  $group: {
3110
3196
  _id: "$set",
3197
+ isScheduleTask: { $first: "$isScheduleTask" },
3111
3198
  units: {
3112
3199
  $push: {
3113
3200
  unit: "$unit",
@@ -3124,6 +3211,7 @@ function useAreaChecklistRepo() {
3124
3211
  $project: {
3125
3212
  _id: 0,
3126
3213
  set: "$_id",
3214
+ isScheduleTask: 1,
3127
3215
  units: 1
3128
3216
  }
3129
3217
  },
@@ -3240,7 +3328,7 @@ function useAreaChecklistRepo() {
3240
3328
  },
3241
3329
  {
3242
3330
  $sort: {
3243
- set: 1,
3331
+ "checklist.set": 1,
3244
3332
  isActioned: 1,
3245
3333
  "checklist.units.timestamp": 1,
3246
3334
  isCompleted: -1
@@ -3250,6 +3338,12 @@ function useAreaChecklistRepo() {
3250
3338
  $project: {
3251
3339
  _id: 0,
3252
3340
  set: "$checklist.set",
3341
+ isScheduleTask: {
3342
+ $ifNull: ["$checklist.isScheduleTask", false]
3343
+ },
3344
+ scheduleTaskId: {
3345
+ $ifNull: ["$checklist.scheduleTaskId", null]
3346
+ },
3253
3347
  remarks: "$checklist.remarks",
3254
3348
  attachment: "$checklist.attachment",
3255
3349
  unit: "$checklist.units.unit",
@@ -3282,6 +3376,8 @@ function useAreaChecklistRepo() {
3282
3376
  {
3283
3377
  $group: {
3284
3378
  _id: "$set",
3379
+ isScheduleTask: { $first: "$isScheduleTask" },
3380
+ scheduleTaskId: { $first: "$scheduleTaskId" },
3285
3381
  remarks: { $first: "$remarks" },
3286
3382
  attachment: { $first: "$attachment" },
3287
3383
  completedByName: { $first: "$completedByName" },
@@ -3303,6 +3399,8 @@ function useAreaChecklistRepo() {
3303
3399
  $project: {
3304
3400
  _id: 0,
3305
3401
  set: "$_id",
3402
+ isScheduleTask: 1,
3403
+ scheduleTaskId: 1,
3306
3404
  remarks: "$remarks",
3307
3405
  attachment: "$attachment",
3308
3406
  completedByName: 1,
@@ -3327,7 +3425,7 @@ function useAreaChecklistRepo() {
3327
3425
  }
3328
3426
  }
3329
3427
  },
3330
- { $sort: { isFullyActioned: 1 } },
3428
+ { $sort: { isFullyActioned: 1, set: 1 } },
3331
3429
  { $skip: page * limit },
3332
3430
  { $limit: limit }
3333
3431
  ];
@@ -3550,7 +3648,7 @@ function useAreaChecklistRepo() {
3550
3648
  const _id = new ObjectId9(areaId);
3551
3649
  const result = await collection.aggregate(
3552
3650
  [
3553
- { $match: { area: _id.toString() } },
3651
+ { $match: { area: _id } },
3554
3652
  { $unwind: "$checklist" },
3555
3653
  { $group: { _id: null, maxSet: { $max: "$checklist.set" } } }
3556
3654
  ],
@@ -3598,9 +3696,155 @@ function useAreaChecklistRepo() {
3598
3696
  throw error;
3599
3697
  }
3600
3698
  }
3699
+ async function pushScheduleTaskSets(scheduleId, areaId, scheduleTaskId, newSets) {
3700
+ try {
3701
+ const schedule = new ObjectId9(scheduleId);
3702
+ const area = new ObjectId9(areaId);
3703
+ const taskId = new ObjectId9(scheduleTaskId);
3704
+ const result = await collection.updateOne(
3705
+ {
3706
+ schedule,
3707
+ area,
3708
+ checklist: { $not: { $elemMatch: { scheduleTaskId: taskId } } }
3709
+ },
3710
+ {
3711
+ $push: { checklist: { $each: newSets } },
3712
+ $set: { updatedAt: /* @__PURE__ */ new Date() }
3713
+ }
3714
+ );
3715
+ if (result.modifiedCount > 0) {
3716
+ delNamespace().catch((err) => {
3717
+ logger17.error(
3718
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3719
+ err
3720
+ );
3721
+ });
3722
+ }
3723
+ return result.modifiedCount;
3724
+ } catch (error) {
3725
+ logger17.error("Error in pushScheduleTaskSets:", error);
3726
+ throw error;
3727
+ }
3728
+ }
3729
+ async function insertAutoGenSets(scheduleId, areaId, newSets) {
3730
+ try {
3731
+ const schedule = new ObjectId9(scheduleId);
3732
+ const area = new ObjectId9(areaId);
3733
+ const result = await collection.updateOne(
3734
+ {
3735
+ schedule,
3736
+ area,
3737
+ checklist: {
3738
+ $not: { $elemMatch: { isScheduleTask: { $ne: true } } }
3739
+ }
3740
+ },
3741
+ {
3742
+ $push: { checklist: { $each: newSets } },
3743
+ $set: { updatedAt: /* @__PURE__ */ new Date() }
3744
+ }
3745
+ );
3746
+ if (result.modifiedCount > 0) {
3747
+ delNamespace().catch((err) => {
3748
+ logger17.error(
3749
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3750
+ err
3751
+ );
3752
+ });
3753
+ }
3754
+ return result.modifiedCount;
3755
+ } catch (error) {
3756
+ logger17.error("Error in insertAutoGenSets:", error);
3757
+ throw error;
3758
+ }
3759
+ }
3760
+ async function trimOverflowSets() {
3761
+ try {
3762
+ const db2 = useAtlas6.getDb();
3763
+ if (!db2)
3764
+ return 0;
3765
+ const bloated = await collection.aggregate([
3766
+ {
3767
+ $lookup: {
3768
+ from: "site.cleaning.areas",
3769
+ localField: "area",
3770
+ foreignField: "_id",
3771
+ as: "areaDoc"
3772
+ }
3773
+ },
3774
+ {
3775
+ $unwind: { path: "$areaDoc", preserveNullAndEmptyArrays: false }
3776
+ },
3777
+ {
3778
+ $addFields: {
3779
+ configuredSet: { $max: [1, { $ifNull: ["$areaDoc.set", 1] }] },
3780
+ autoGenSets: {
3781
+ $filter: {
3782
+ input: { $ifNull: ["$checklist", []] },
3783
+ as: "item",
3784
+ cond: { $ne: ["$$item.isScheduleTask", true] }
3785
+ }
3786
+ },
3787
+ scheduledSets: {
3788
+ $filter: {
3789
+ input: { $ifNull: ["$checklist", []] },
3790
+ as: "item",
3791
+ cond: { $eq: ["$$item.isScheduleTask", true] }
3792
+ }
3793
+ }
3794
+ }
3795
+ },
3796
+ {
3797
+ $match: {
3798
+ $expr: {
3799
+ $gt: [{ $size: "$autoGenSets" }, "$configuredSet"]
3800
+ }
3801
+ }
3802
+ },
3803
+ {
3804
+ $project: {
3805
+ _id: 1,
3806
+ checklist: 1,
3807
+ configuredSet: 1,
3808
+ autoGenSets: 1,
3809
+ scheduledSets: 1
3810
+ }
3811
+ }
3812
+ ]).toArray();
3813
+ if (bloated.length === 0) {
3814
+ logger17.info(
3815
+ "trimOverflowSets: no bloated area-checklist documents found"
3816
+ );
3817
+ return 0;
3818
+ }
3819
+ let trimmed = 0;
3820
+ for (const doc of bloated) {
3821
+ const limit = Number(doc.configuredSet) || 1;
3822
+ const trimmedAutoGen = [...doc.autoGenSets ?? []].sort((a, b) => (a.set ?? 0) - (b.set ?? 0)).slice(0, limit);
3823
+ const keep = [...trimmedAutoGen, ...doc.scheduledSets ?? []];
3824
+ const res = await collection.updateOne(
3825
+ { _id: doc._id },
3826
+ { $set: { checklist: keep, updatedAt: /* @__PURE__ */ new Date() } }
3827
+ );
3828
+ if (res.modifiedCount > 0) {
3829
+ trimmed++;
3830
+ logger17.info(
3831
+ `trimOverflowSets: trimmed doc ${doc._id} \u2014 kept ${keep.length} sets (${trimmedAutoGen.length} auto-gen, ${(doc.scheduledSets ?? []).length} scheduled), removed ${doc.checklist.length - keep.length} excess auto-gen`
3832
+ );
3833
+ }
3834
+ }
3835
+ delNamespace().catch(() => {
3836
+ });
3837
+ logger17.info(`trimOverflowSets: trimmed ${trimmed} document(s)`);
3838
+ return trimmed;
3839
+ } catch (error) {
3840
+ logger17.error("Error in trimOverflowSets:", error);
3841
+ return 0;
3842
+ }
3843
+ }
3601
3844
  return {
3602
3845
  createIndex,
3603
3846
  createTextIndex,
3847
+ createUniqueIndex,
3604
3848
  createAreaChecklist,
3605
3849
  getAllAreaChecklist,
3606
3850
  getAreaChecklistHistory,
@@ -3613,7 +3857,10 @@ function useAreaChecklistRepo() {
3613
3857
  updateAreaChecklistStatus,
3614
3858
  updateAreaChecklistUnits,
3615
3859
  getMaxSetNumberForArea,
3616
- closeExpiredAreaChecklists
3860
+ closeExpiredAreaChecklists,
3861
+ pushScheduleTaskSets,
3862
+ insertAutoGenSets,
3863
+ trimOverflowSets
3617
3864
  };
3618
3865
  }
3619
3866
 
@@ -3625,19 +3872,18 @@ function useAreaChecklistService() {
3625
3872
  getAllAreaChecklist,
3626
3873
  getAreaChecklistUnits,
3627
3874
  getAreaChecklistById,
3875
+ getAreaChecklistByAreaAndSchedule,
3628
3876
  getAreaChecklistSetOwner,
3629
3877
  updateAreaChecklistUnits: _updateAreaChecklistUnits,
3630
3878
  updateAreaChecklistStatus,
3631
- getMaxSetNumberForArea
3879
+ insertAutoGenSets
3632
3880
  } = useAreaChecklistRepo();
3633
3881
  const { updateParentChecklistStatuses } = useParentChecklistRepo();
3634
3882
  const { getUserById } = useUserRepo();
3635
3883
  async function createAreaChecklist(value) {
3636
- const session = useAtlas7.getClient()?.startSession();
3884
+ const results = [];
3885
+ let totalChecklistsCreated = 0;
3637
3886
  try {
3638
- session?.startTransaction();
3639
- const results = [];
3640
- let totalChecklistsCreated = 0;
3641
3887
  const BATCH_SIZE = 10;
3642
3888
  const areasResult = await getAreasForChecklist(value.site);
3643
3889
  const areas = areasResult || [];
@@ -3651,15 +3897,72 @@ function useAreaChecklistService() {
3651
3897
  );
3652
3898
  return null;
3653
3899
  }
3654
- const maxSet = await getMaxSetNumberForArea(area._id, session);
3655
- const setCount = area.set || 1;
3900
+ let existing = null;
3901
+ try {
3902
+ existing = await getAreaChecklistByAreaAndSchedule(
3903
+ value.schedule.toString(),
3904
+ area._id.toString()
3905
+ );
3906
+ } catch (_) {
3907
+ }
3908
+ if (existing) {
3909
+ const hasAutoGenSets = existing.checklist?.some(
3910
+ (c) => c.isScheduleTask !== true
3911
+ );
3912
+ if (hasAutoGenSets) {
3913
+ logger18.info(
3914
+ `Area checklist already exists with auto-gen sets for area ${area.name}, skipping`
3915
+ );
3916
+ return existing._id;
3917
+ }
3918
+ logger18.info(
3919
+ `Area checklist for area ${area.name} exists but has no auto-gen sets. Inserting auto-gen sets.`
3920
+ );
3921
+ const setCount2 = Number(area.set) || 1;
3922
+ const totalExistingSets = existing.checklist?.length ?? 0;
3923
+ if (totalExistingSets >= setCount2) {
3924
+ logger18.info(
3925
+ `Area checklist for area ${area.name} already has ${totalExistingSets} set(s) (configured: ${setCount2}). Skipping auto-gen sets.`
3926
+ );
3927
+ return existing._id;
3928
+ }
3929
+ const maxExistingSet = existing.checklist?.reduce(
3930
+ (max, c) => Math.max(max, c.set ?? 0),
3931
+ 0
3932
+ ) ?? 0;
3933
+ const setsToAdd = setCount2 - totalExistingSets;
3934
+ const autoGenSets = Array.from(
3935
+ { length: setsToAdd },
3936
+ (_, index) => ({
3937
+ set: maxExistingSet + index + 1,
3938
+ isScheduleTask: false,
3939
+ units: area.units.map((unit) => ({
3940
+ unit: new ObjectId10(unit.unit),
3941
+ name: unit.name,
3942
+ approve: false,
3943
+ reject: false,
3944
+ status: "open",
3945
+ remarks: "",
3946
+ completedBy: "",
3947
+ timestamp: ""
3948
+ }))
3949
+ })
3950
+ );
3951
+ await insertAutoGenSets(
3952
+ value.schedule.toString(),
3953
+ area._id.toString(),
3954
+ autoGenSets
3955
+ );
3956
+ return existing._id;
3957
+ }
3958
+ const setCount = Number(area.set) || 1;
3656
3959
  const checklistData = {
3657
3960
  schedule: value.schedule,
3658
3961
  area: area._id.toString(),
3659
3962
  name: area.name,
3660
3963
  type: area.type,
3661
3964
  checklist: Array.from({ length: setCount }, (_, index) => ({
3662
- set: maxSet + index + 1,
3965
+ set: index + 1,
3663
3966
  units: area.units.map((unit) => ({
3664
3967
  unit: unit.unit.toString(),
3665
3968
  name: unit.name
@@ -3667,10 +3970,7 @@ function useAreaChecklistService() {
3667
3970
  })),
3668
3971
  createdBy: value.createdBy
3669
3972
  };
3670
- const insertedId = await _createAreaChecklist(
3671
- checklistData,
3672
- session
3673
- );
3973
+ const insertedId = await _createAreaChecklist(checklistData);
3674
3974
  totalChecklistsCreated++;
3675
3975
  return insertedId;
3676
3976
  });
@@ -3683,19 +3983,13 @@ function useAreaChecklistService() {
3683
3983
  } else {
3684
3984
  logger18.warn(`No common areas found for site: ${value.site}`);
3685
3985
  }
3686
- await session?.commitTransaction();
3687
3986
  logger18.info(
3688
3987
  `Successfully created ${totalChecklistsCreated} area checklists for site: ${value.site}`
3689
3988
  );
3690
3989
  return results;
3691
3990
  } catch (error) {
3692
3991
  logger18.error(`Error generating area checklists:`, error);
3693
- if (session?.inTransaction()) {
3694
- await session?.abortTransaction();
3695
- }
3696
3992
  throw error;
3697
- } finally {
3698
- session?.endSession();
3699
3993
  }
3700
3994
  }
3701
3995
  async function updateAreaChecklistUnits(_id, set, unitId, value) {
@@ -3799,7 +4093,7 @@ import { BadRequestError as BadRequestError16, InternalServerError as InternalSe
3799
4093
  // src/services/hygiene-checklist-pdf.service.ts
3800
4094
  import { launch } from "puppeteer";
3801
4095
  import { InternalServerError as InternalServerError6, useAtlas as useAtlas8 } from "@7365admin1/node-server-utils";
3802
- import { ObjectId as ObjectId10 } from "mongodb";
4096
+ import { ObjectId as ObjectId11 } from "mongodb";
3803
4097
  function escapeHtml(text) {
3804
4098
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
3805
4099
  }
@@ -3973,7 +4267,7 @@ function useChecklistPdfService() {
3973
4267
  const collection = db.collection("site.cleaning.schedule.areas");
3974
4268
  let scheduleObjectId;
3975
4269
  try {
3976
- scheduleObjectId = new ObjectId10(scheduleId);
4270
+ scheduleObjectId = new ObjectId11(scheduleId);
3977
4271
  } catch {
3978
4272
  throw new InternalServerError6("Invalid schedule ID format.");
3979
4273
  }
@@ -4282,7 +4576,7 @@ function useAreaChecklistController() {
4282
4576
 
4283
4577
  // src/models/hygiene-supply.model.ts
4284
4578
  import Joi10 from "joi";
4285
- import { ObjectId as ObjectId11 } from "mongodb";
4579
+ import { ObjectId as ObjectId12 } from "mongodb";
4286
4580
  import { BadRequestError as BadRequestError17, logger as logger21 } from "@7365admin1/node-server-utils";
4287
4581
  var supplySchema = Joi10.object({
4288
4582
  site: Joi10.string().hex().required(),
@@ -4297,7 +4591,7 @@ function MSupply(value) {
4297
4591
  }
4298
4592
  if (value.site) {
4299
4593
  try {
4300
- value.site = new ObjectId11(value.site);
4594
+ value.site = new ObjectId12(value.site);
4301
4595
  } catch (error2) {
4302
4596
  throw new BadRequestError17("Invalid site ID format.");
4303
4597
  }
@@ -4315,7 +4609,7 @@ function MSupply(value) {
4315
4609
  }
4316
4610
 
4317
4611
  // src/repositories/hygiene-supply.repository.ts
4318
- import { ObjectId as ObjectId12 } from "mongodb";
4612
+ import { ObjectId as ObjectId13 } from "mongodb";
4319
4613
  import {
4320
4614
  useAtlas as useAtlas9,
4321
4615
  InternalServerError as InternalServerError8,
@@ -4400,7 +4694,7 @@ function useSupplyRepository() {
4400
4694
  limit
4401
4695
  };
4402
4696
  try {
4403
- site = new ObjectId12(site);
4697
+ site = new ObjectId13(site);
4404
4698
  query.site = site;
4405
4699
  cacheOptions.site = site.toString();
4406
4700
  } catch (error) {
@@ -4444,7 +4738,7 @@ function useSupplyRepository() {
4444
4738
  }
4445
4739
  async function getSupplyById(_id, session) {
4446
4740
  try {
4447
- _id = new ObjectId12(_id);
4741
+ _id = new ObjectId13(_id);
4448
4742
  } catch (error) {
4449
4743
  throw new BadRequestError18("Invalid supply ID format.");
4450
4744
  }
@@ -4490,7 +4784,7 @@ function useSupplyRepository() {
4490
4784
  }
4491
4785
  async function updateSupply(_id, value, session) {
4492
4786
  try {
4493
- _id = new ObjectId12(_id);
4787
+ _id = new ObjectId13(_id);
4494
4788
  } catch (error) {
4495
4789
  throw new BadRequestError18("Invalid supply ID format.");
4496
4790
  }
@@ -4523,7 +4817,7 @@ function useSupplyRepository() {
4523
4817
  }
4524
4818
  async function deleteSupply(_id, session) {
4525
4819
  try {
4526
- _id = new ObjectId12(_id);
4820
+ _id = new ObjectId13(_id);
4527
4821
  } catch (error) {
4528
4822
  throw new BadRequestError18("Invalid supply ID format.");
4529
4823
  }
@@ -4704,7 +4998,7 @@ function useSupplyController() {
4704
4998
 
4705
4999
  // src/models/hygiene-stock.model.ts
4706
5000
  import Joi12 from "joi";
4707
- import { ObjectId as ObjectId13 } from "mongodb";
5001
+ import { ObjectId as ObjectId14 } from "mongodb";
4708
5002
  import { BadRequestError as BadRequestError20, logger as logger24 } from "@7365admin1/node-server-utils";
4709
5003
  var stockSchema = Joi12.object({
4710
5004
  site: Joi12.string().hex().required(),
@@ -4722,14 +5016,14 @@ function MStock(value) {
4722
5016
  }
4723
5017
  if (value.site) {
4724
5018
  try {
4725
- value.site = new ObjectId13(value.site);
5019
+ value.site = new ObjectId14(value.site);
4726
5020
  } catch (error2) {
4727
5021
  throw new BadRequestError20("Invalid site ID format.");
4728
5022
  }
4729
5023
  }
4730
5024
  if (value.supply) {
4731
5025
  try {
4732
- value.supply = new ObjectId13(value.supply);
5026
+ value.supply = new ObjectId14(value.supply);
4733
5027
  } catch (error2) {
4734
5028
  throw new BadRequestError20("Invalid supply ID format.");
4735
5029
  }
@@ -4749,7 +5043,7 @@ function MStock(value) {
4749
5043
  }
4750
5044
 
4751
5045
  // src/repositories/hygiene-stock.repository.ts
4752
- import { ObjectId as ObjectId14 } from "mongodb";
5046
+ import { ObjectId as ObjectId15 } from "mongodb";
4753
5047
  import {
4754
5048
  useAtlas as useAtlas10,
4755
5049
  InternalServerError as InternalServerError9,
@@ -4822,14 +5116,14 @@ function useStockRepository() {
4822
5116
  limit
4823
5117
  };
4824
5118
  try {
4825
- site = new ObjectId14(site);
5119
+ site = new ObjectId15(site);
4826
5120
  query.site = site;
4827
5121
  cacheOptions.site = site.toString();
4828
5122
  } catch (error) {
4829
5123
  throw new BadRequestError21("Invalid site ID format.");
4830
5124
  }
4831
5125
  try {
4832
- supply = new ObjectId14(supply);
5126
+ supply = new ObjectId15(supply);
4833
5127
  query.supply = supply;
4834
5128
  cacheOptions.supply = supply.toString();
4835
5129
  } catch (error) {
@@ -5007,7 +5301,7 @@ function useStockController() {
5007
5301
 
5008
5302
  // src/models/hygiene-checkout-item.model.ts
5009
5303
  import Joi14 from "joi";
5010
- import { ObjectId as ObjectId15 } from "mongodb";
5304
+ import { ObjectId as ObjectId16 } from "mongodb";
5011
5305
  import { BadRequestError as BadRequestError24, logger as logger27 } from "@7365admin1/node-server-utils";
5012
5306
  var allowedCheckOutItemStatus = ["pending", "completed"];
5013
5307
  var checkOutItemSchema = Joi14.object({
@@ -5027,14 +5321,14 @@ function MCheckOutItem(value) {
5027
5321
  }
5028
5322
  if (value.site) {
5029
5323
  try {
5030
- value.site = new ObjectId15(value.site);
5324
+ value.site = new ObjectId16(value.site);
5031
5325
  } catch (error2) {
5032
5326
  throw new BadRequestError24("Invalid site ID format.");
5033
5327
  }
5034
5328
  }
5035
5329
  if (value.supply) {
5036
5330
  try {
5037
- value.supply = new ObjectId15(value.supply);
5331
+ value.supply = new ObjectId16(value.supply);
5038
5332
  } catch (error2) {
5039
5333
  throw new BadRequestError24("Invalid supply ID format.");
5040
5334
  }
@@ -5055,7 +5349,7 @@ function MCheckOutItem(value) {
5055
5349
  }
5056
5350
 
5057
5351
  // src/repositories/hygiene-checkout-item.repository.ts
5058
- import { ObjectId as ObjectId16 } from "mongodb";
5352
+ import { ObjectId as ObjectId17 } from "mongodb";
5059
5353
  import {
5060
5354
  useAtlas as useAtlas12,
5061
5355
  InternalServerError as InternalServerError10,
@@ -5128,7 +5422,7 @@ function useCheckOutItemRepository() {
5128
5422
  limit
5129
5423
  };
5130
5424
  try {
5131
- site = new ObjectId16(site);
5425
+ site = new ObjectId17(site);
5132
5426
  query.site = site;
5133
5427
  cacheOptions.site = site.toString();
5134
5428
  } catch (error) {
@@ -5201,7 +5495,7 @@ function useCheckOutItemRepository() {
5201
5495
  }
5202
5496
  async function getCheckOutItemById(_id, session) {
5203
5497
  try {
5204
- _id = new ObjectId16(_id);
5498
+ _id = new ObjectId17(_id);
5205
5499
  } catch (error) {
5206
5500
  throw new BadRequestError25("Invalid check out item ID format.");
5207
5501
  }
@@ -5265,7 +5559,7 @@ function useCheckOutItemRepository() {
5265
5559
  }
5266
5560
  async function completeCheckOutItem(_id, session) {
5267
5561
  try {
5268
- _id = new ObjectId16(_id);
5562
+ _id = new ObjectId17(_id);
5269
5563
  } catch (error) {
5270
5564
  throw new BadRequestError25("Invalid check out item ID format.");
5271
5565
  }
@@ -5551,7 +5845,7 @@ function useCheckOutItemController() {
5551
5845
  // src/models/hygiene-schedule-task.model.ts
5552
5846
  import { BadRequestError as BadRequestError28, logger as logger30 } from "@7365admin1/node-server-utils";
5553
5847
  import Joi16 from "joi";
5554
- import { ObjectId as ObjectId17 } from "mongodb";
5848
+ import { ObjectId as ObjectId18 } from "mongodb";
5555
5849
  var scheduleTaskSchema = Joi16.object({
5556
5850
  site: Joi16.string().hex().required(),
5557
5851
  title: Joi16.string().required(),
@@ -5576,7 +5870,7 @@ function MScheduleTask(value) {
5576
5870
  }
5577
5871
  if (value.site) {
5578
5872
  try {
5579
- value.site = new ObjectId17(value.site);
5873
+ value.site = new ObjectId18(value.site);
5580
5874
  } catch (error2) {
5581
5875
  throw new BadRequestError28("Invalid site ID format.");
5582
5876
  }
@@ -5586,7 +5880,7 @@ function MScheduleTask(value) {
5586
5880
  try {
5587
5881
  return {
5588
5882
  name: area.name,
5589
- value: new ObjectId17(area.value.toString())
5883
+ value: new ObjectId18(area.value.toString())
5590
5884
  };
5591
5885
  } catch (error2) {
5592
5886
  throw new BadRequestError28(`Invalid area value format: ${area.name}`);
@@ -5595,7 +5889,7 @@ function MScheduleTask(value) {
5595
5889
  }
5596
5890
  if (value.createdBy) {
5597
5891
  try {
5598
- value.createdBy = new ObjectId17(value.createdBy);
5892
+ value.createdBy = new ObjectId18(value.createdBy);
5599
5893
  } catch (error2) {
5600
5894
  throw new BadRequestError28("Invalid createdBy ID format.");
5601
5895
  }
@@ -5616,7 +5910,7 @@ function MScheduleTask(value) {
5616
5910
  }
5617
5911
 
5618
5912
  // src/repositories/hygiene-schedule-task.repository.ts
5619
- import { ObjectId as ObjectId18 } from "mongodb";
5913
+ import { ObjectId as ObjectId19 } from "mongodb";
5620
5914
  import {
5621
5915
  useAtlas as useAtlas14,
5622
5916
  InternalServerError as InternalServerError11,
@@ -5688,7 +5982,7 @@ function useScheduleTaskRepository() {
5688
5982
  limit
5689
5983
  };
5690
5984
  try {
5691
- site = new ObjectId18(site);
5985
+ site = new ObjectId19(site);
5692
5986
  query.site = site;
5693
5987
  cacheOptions.site = site.toString();
5694
5988
  } catch (error) {
@@ -5756,7 +6050,7 @@ function useScheduleTaskRepository() {
5756
6050
  limit
5757
6051
  };
5758
6052
  try {
5759
- site = new ObjectId18(site);
6053
+ site = new ObjectId19(site);
5760
6054
  query.site = site;
5761
6055
  cacheOptions.site = site.toString();
5762
6056
  } catch (error) {
@@ -5799,7 +6093,7 @@ function useScheduleTaskRepository() {
5799
6093
  }
5800
6094
  async function getScheduleTaskById(_id, session) {
5801
6095
  try {
5802
- _id = new ObjectId18(_id);
6096
+ _id = new ObjectId19(_id);
5803
6097
  } catch (error) {
5804
6098
  throw new BadRequestError29("Invalid schedule task ID format.");
5805
6099
  }
@@ -5849,7 +6143,7 @@ function useScheduleTaskRepository() {
5849
6143
  }
5850
6144
  async function updateScheduleTask(_id, value, session) {
5851
6145
  try {
5852
- _id = new ObjectId18(_id);
6146
+ _id = new ObjectId19(_id);
5853
6147
  } catch (error) {
5854
6148
  throw new BadRequestError29("Invalid schedule task ID format.");
5855
6149
  }
@@ -5858,7 +6152,7 @@ function useScheduleTaskRepository() {
5858
6152
  try {
5859
6153
  return {
5860
6154
  name: area.name,
5861
- value: new ObjectId18(area.value.toString())
6155
+ value: new ObjectId19(area.value.toString())
5862
6156
  };
5863
6157
  } catch (error) {
5864
6158
  throw new BadRequestError29(`Invalid area value format: ${area.name}`);
@@ -5903,6 +6197,7 @@ function useScheduleTaskRepository() {
5903
6197
  }
5904
6198
 
5905
6199
  // src/services/hygiene-schedule-task.service.ts
6200
+ import { ObjectId as ObjectId20 } from "mongodb";
5906
6201
  import { logger as logger32 } from "@7365admin1/node-server-utils";
5907
6202
  function useScheduleTaskService() {
5908
6203
  const { createParentChecklist } = useParentChecklistRepo();
@@ -5910,7 +6205,7 @@ function useScheduleTaskService() {
5910
6205
  const {
5911
6206
  createAreaChecklist,
5912
6207
  getAreaChecklistByAreaAndSchedule,
5913
- updateAreaChecklist
6208
+ pushScheduleTaskSets
5914
6209
  } = useAreaChecklistRepo();
5915
6210
  const { getAreaById } = useAreaRepo();
5916
6211
  function checkScheduleConditions(schedule, currentDate = /* @__PURE__ */ new Date()) {
@@ -5949,6 +6244,35 @@ function useScheduleTaskService() {
5949
6244
  );
5950
6245
  return false;
5951
6246
  }
6247
+ const relevantTimestamp = schedule.updatedAt || schedule.createdAt;
6248
+ if (relevantTimestamp) {
6249
+ const savedAt = new Date(relevantTimestamp);
6250
+ const savedHour = parseInt(
6251
+ savedAt.toLocaleTimeString("en-US", {
6252
+ hour: "2-digit",
6253
+ hour12: false,
6254
+ timeZone: "Asia/Singapore"
6255
+ }),
6256
+ 10
6257
+ );
6258
+ const savedMinute = savedAt.toLocaleString("en-US", {
6259
+ minute: "2-digit",
6260
+ timeZone: "Asia/Singapore"
6261
+ });
6262
+ const savedDateFormatted = savedAt.toLocaleDateString("en-CA", {
6263
+ timeZone: "Asia/Singapore"
6264
+ });
6265
+ const savedMinNum = parseInt(savedMinute, 10);
6266
+ const wasSavedThisMinute = savedDateFormatted === currentDateFormatted && savedHour === currentHour && savedMinNum === currentMinute;
6267
+ if (wasSavedThisMinute) {
6268
+ logger32.info(
6269
+ `Schedule ${schedule._id}: Skipping \u2014 task was saved this minute (${savedHour}:${String(
6270
+ savedMinNum
6271
+ ).padStart(2, "0")}). Will fire on next occurrence.`
6272
+ );
6273
+ return false;
6274
+ }
6275
+ }
5952
6276
  logger32.info(
5953
6277
  `Schedule ${schedule._id}: All conditions matched - Date is within range and time matches`
5954
6278
  );
@@ -6016,8 +6340,14 @@ function useScheduleTaskService() {
6016
6340
  let units = [];
6017
6341
  if (areaDetails.units && areaDetails.units.length > 0) {
6018
6342
  units = areaDetails.units.map((unit) => ({
6019
- unit: unit.unit.toString(),
6020
- name: unit.name
6343
+ unit: new ObjectId20(unit.unit),
6344
+ name: unit.name,
6345
+ approve: false,
6346
+ reject: false,
6347
+ status: "open",
6348
+ remarks: "",
6349
+ completedBy: "",
6350
+ timestamp: ""
6021
6351
  }));
6022
6352
  logger32.info(
6023
6353
  `Area ${area.name} (${areaId}): Using units from area details: ${JSON.stringify(
@@ -6054,79 +6384,62 @@ function useScheduleTaskService() {
6054
6384
  );
6055
6385
  }
6056
6386
  if (existingAreaChecklist) {
6057
- const currentSetNumber = existingAreaChecklist.checklist?.length || 0;
6058
- const newSet = {
6059
- set: currentSetNumber + 1,
6060
- units
6061
- };
6062
- const updatedChecklist = [
6063
- ...existingAreaChecklist.checklist || [],
6064
- newSet
6065
- ];
6066
- logger32.info(
6067
- `Area ${area.name} (${areaId}): Appending new set ${newSet.set} to checklist. Updated checklist: ${JSON.stringify(
6068
- updatedChecklist
6069
- )}`
6387
+ const areaSetConfig = Number(areaDetails.set) || 1;
6388
+ const maxSetInChecklist = existingAreaChecklist.checklist?.length ? Math.max(
6389
+ ...existingAreaChecklist.checklist.map(
6390
+ (c) => c.set
6391
+ )
6392
+ ) : 0;
6393
+ const startSetNumber = maxSetInChecklist + 1;
6394
+ const newSets = Array.from(
6395
+ { length: areaSetConfig },
6396
+ (_, index) => ({
6397
+ set: startSetNumber + index,
6398
+ units,
6399
+ isScheduleTask: true,
6400
+ scheduleTaskId: scheduleTask._id
6401
+ })
6070
6402
  );
6071
- await updateAreaChecklist(existingAreaChecklist._id, {
6072
- checklist: updatedChecklist
6073
- });
6074
- logger32.info(
6075
- `Appended set ${newSet.set} to area checklist for area ${area.name}`
6403
+ const modified = await pushScheduleTaskSets(
6404
+ parentChecklistId.toString(),
6405
+ areaId,
6406
+ scheduleTask._id.toString(),
6407
+ newSets
6076
6408
  );
6077
- try {
6078
- const verifyChecklist = await getAreaChecklistByAreaAndSchedule(
6079
- parentChecklistId.toString(),
6080
- areaId
6081
- );
6409
+ if (modified > 0) {
6082
6410
  logger32.info(
6083
- `Area ${area.name} (${areaId}): Checklist after update: ${JSON.stringify(
6084
- verifyChecklist.checklist
6085
- )}`
6411
+ `Appended ${areaSetConfig} set(s) starting from ${startSetNumber} to area checklist for area ${area.name}`
6086
6412
  );
6087
- } catch (verifyError) {
6088
- logger32.warn(
6089
- `Area ${area.name} (${areaId}): Error verifying checklist after update:`,
6090
- verifyError
6413
+ } else {
6414
+ logger32.info(
6415
+ `Area ${area.name} (${areaId}): Schedule task ${scheduleTask._id} already processed (atomic check), skipping.`
6091
6416
  );
6092
6417
  }
6093
6418
  } else {
6419
+ const areaSetConfig = Number(areaDetails.set) || 1;
6420
+ const schedTaskId = scheduleTask._id;
6421
+ const schedTaskSets = Array.from(
6422
+ { length: areaSetConfig },
6423
+ (_, index) => ({
6424
+ set: index + 1,
6425
+ units,
6426
+ isScheduleTask: true,
6427
+ scheduleTaskId: schedTaskId
6428
+ })
6429
+ );
6094
6430
  const checklistData = {
6095
6431
  schedule: parentChecklistId.toString(),
6096
6432
  area: areaId,
6097
6433
  name: area.name,
6098
6434
  type: areaDetails.type || "common",
6099
- checklist: [
6100
- {
6101
- set: 1,
6102
- units
6103
- }
6104
- ],
6435
+ checklist: schedTaskSets,
6105
6436
  createdBy: scheduleTask.createdBy
6106
6437
  };
6107
6438
  logger32.info(
6108
- `Area ${area.name} (${areaId}): Creating new area checklist with data: ${JSON.stringify(
6109
- checklistData
6110
- )}`
6439
+ `Area ${area.name} (${areaId}): Creating new area checklist with schedule-task sets only`
6111
6440
  );
6112
6441
  await createAreaChecklist(checklistData);
6113
6442
  logger32.info(`Created new area checklist for area ${area.name}`);
6114
- try {
6115
- const verifyChecklist = await getAreaChecklistByAreaAndSchedule(
6116
- parentChecklistId.toString(),
6117
- areaId
6118
- );
6119
- logger32.info(
6120
- `Area ${area.name} (${areaId}): Checklist after creation: ${JSON.stringify(
6121
- verifyChecklist.checklist
6122
- )}`
6123
- );
6124
- } catch (verifyError) {
6125
- logger32.warn(
6126
- `Area ${area.name} (${areaId}): Error verifying checklist after creation:`,
6127
- verifyError
6128
- );
6129
- }
6130
6443
  }
6131
6444
  } catch (error) {
6132
6445
  logger32.error(`Error processing area ${area.name}:`, error);
@@ -6611,6 +6924,7 @@ export {
6611
6924
  MStock,
6612
6925
  MSupply,
6613
6926
  MUnit,
6927
+ ServiceType,
6614
6928
  allowedCheckOutItemStatus,
6615
6929
  allowedChecklistStatus,
6616
6930
  allowedPeriods,