@7365admin1/module-hygiene 4.30.3 → 4.31.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.js CHANGED
@@ -1002,6 +1002,11 @@ function capitalizeText(text) {
1002
1002
  return text.toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1003
1003
  }
1004
1004
 
1005
+ // src/utils/escape-regex.util.ts
1006
+ function escapeRegex(value) {
1007
+ return String(value ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1008
+ }
1009
+
1005
1010
  // src/repositories/hygiene-area.repository.ts
1006
1011
  function useAreaRepo() {
1007
1012
  const db = import_node_server_utils4.useAtlas.getDb();
@@ -1098,7 +1103,7 @@ function useAreaRepo() {
1098
1103
  cacheOptions.type = type;
1099
1104
  }
1100
1105
  if (search) {
1101
- query.$or = [{ name: { $regex: search, $options: "i" } }];
1106
+ query.$or = [{ name: { $regex: escapeRegex(search), $options: "i" } }];
1102
1107
  cacheOptions.search = search;
1103
1108
  }
1104
1109
  const cacheKey = (0, import_node_server_utils4.makeCacheKey)(namespace_collection, cacheOptions);
@@ -1560,7 +1565,7 @@ function useUnitRepository() {
1560
1565
  throw new import_node_server_utils7.BadRequestError("Invalid site ID format.");
1561
1566
  }
1562
1567
  if (search) {
1563
- query.$or = [{ name: { $regex: search, $options: "i" } }];
1568
+ query.$or = [{ name: { $regex: escapeRegex(search), $options: "i" } }];
1564
1569
  cacheOptions.search = search;
1565
1570
  }
1566
1571
  const cacheKey = (0, import_node_server_utils7.makeCacheKey)(namespace_collection, cacheOptions);
@@ -4466,6 +4471,265 @@ function useAreaChecklistRepo() {
4466
4471
  };
4467
4472
  }
4468
4473
 
4474
+ // src/utils/schedule-task-rules.util.ts
4475
+ var import_joi10 = __toESM(require("joi"));
4476
+ var REPEATS = ["none", "daily", "weekdays", "weekly", "monthly"];
4477
+ var SGT_MS = 8 * 60 * 60 * 1e3;
4478
+ var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
4479
+ var TIME_RE = /^([0-1]\d|2[0-3]):([0-5]\d)$/;
4480
+ var pad = (n) => String(n).padStart(2, "0");
4481
+ function sgtParts(now) {
4482
+ const s = new Date(now.getTime() + SGT_MS);
4483
+ const date = `${s.getUTCFullYear()}-${pad(s.getUTCMonth() + 1)}-${pad(s.getUTCDate())}`;
4484
+ return { date, minutes: s.getUTCHours() * 60 + s.getUTCMinutes(), weekday: s.getUTCDay() };
4485
+ }
4486
+ function isRealDate(value) {
4487
+ if (typeof value !== "string" || !DATE_RE.test(value))
4488
+ return false;
4489
+ const [y, m, d] = value.split("-").map(Number);
4490
+ const t = new Date(Date.UTC(y, m - 1, d));
4491
+ return t.getUTCFullYear() === y && t.getUTCMonth() === m - 1 && t.getUTCDate() === d;
4492
+ }
4493
+ var toMinutes = (time) => {
4494
+ const [h, m] = time.split(":").map(Number);
4495
+ return h * 60 + m;
4496
+ };
4497
+ var weekdayOf = (date) => {
4498
+ const [y, m, d] = date.split("-").map(Number);
4499
+ return new Date(Date.UTC(y, m - 1, d)).getUTCDay();
4500
+ };
4501
+ var daysInMonth = (date) => {
4502
+ const [y, m] = date.split("-").map(Number);
4503
+ return new Date(Date.UTC(y, m, 0)).getUTCDate();
4504
+ };
4505
+ var addDays = (date, n) => {
4506
+ const [y, m, d] = date.split("-").map(Number);
4507
+ const t = new Date(Date.UTC(y, m - 1, d + n));
4508
+ return `${t.getUTCFullYear()}-${pad(t.getUTCMonth() + 1)}-${pad(t.getUTCDate())}`;
4509
+ };
4510
+ var isRepeating = (task) => Boolean(task.repeat) && task.repeat !== "none";
4511
+ function occursOn(task, date) {
4512
+ if (task.endDate && date > task.endDate)
4513
+ return false;
4514
+ if (!isRepeating(task))
4515
+ return Array.isArray(task.dates) && task.dates.includes(date);
4516
+ if (task.startDate && date < task.startDate)
4517
+ return false;
4518
+ const weekday = weekdayOf(date);
4519
+ switch (task.repeat) {
4520
+ case "daily":
4521
+ return true;
4522
+ case "weekdays":
4523
+ return weekday >= 1 && weekday <= 5;
4524
+ case "weekly":
4525
+ return Array.isArray(task.daysOfWeek) && task.daysOfWeek.includes(weekday);
4526
+ case "monthly": {
4527
+ const wanted = task.dayOfMonth ?? (task.startDate ? Number(task.startDate.slice(8, 10)) : 1);
4528
+ return Number(date.slice(8, 10)) === Math.min(wanted, daysInMonth(date));
4529
+ }
4530
+ default:
4531
+ return false;
4532
+ }
4533
+ }
4534
+ function savedTodayAt(task, today) {
4535
+ const stamp = task.updatedAt || task.createdAt;
4536
+ if (!stamp)
4537
+ return null;
4538
+ const saved = new Date(stamp);
4539
+ if (Number.isNaN(saved.getTime()))
4540
+ return null;
4541
+ const p = sgtParts(saved);
4542
+ return p.date === today ? p.minutes : null;
4543
+ }
4544
+ function shouldRunNow(task, now = /* @__PURE__ */ new Date()) {
4545
+ if (task.paused || !TIME_RE.test(task.time ?? ""))
4546
+ return false;
4547
+ const { date, minutes } = sgtParts(now);
4548
+ if (task.lastRunDate === date || !occursOn(task, date))
4549
+ return false;
4550
+ const due = toMinutes(task.time);
4551
+ if (minutes < due)
4552
+ return false;
4553
+ const saved = savedTodayAt(task, date);
4554
+ return !(saved !== null && saved >= due);
4555
+ }
4556
+ var iso = (date, time) => `${date}T${time}:00+08:00`;
4557
+ function upcoming(task, now) {
4558
+ if (typeof task.time !== "string" || !TIME_RE.test(task.time))
4559
+ return null;
4560
+ const { date: today, minutes } = sgtParts(now);
4561
+ const due = toMinutes(task.time);
4562
+ if (occursOn(task, today) && (minutes < due || shouldRunNow({ ...task, paused: false }, now))) {
4563
+ return iso(today, task.time);
4564
+ }
4565
+ if (!isRepeating(task)) {
4566
+ const later = (task.dates ?? []).filter((d) => d > today && (!task.endDate || d <= task.endDate)).sort();
4567
+ return later.length ? iso(later[0], task.time) : null;
4568
+ }
4569
+ const from = task.startDate && task.startDate > today ? task.startDate : today;
4570
+ for (let i = from === today ? 1 : 0; i <= 400; i++) {
4571
+ const d = addDays(from, i);
4572
+ if (task.endDate && d > task.endDate)
4573
+ return null;
4574
+ if (occursOn(task, d))
4575
+ return iso(d, task.time);
4576
+ }
4577
+ return null;
4578
+ }
4579
+ function nextRun(task, now = /* @__PURE__ */ new Date()) {
4580
+ return task.paused ? null : upcoming(task, now);
4581
+ }
4582
+ function scheduleState(task, now = /* @__PURE__ */ new Date()) {
4583
+ if (task.paused)
4584
+ return "paused";
4585
+ return upcoming(task, now) ? "active" : "ended";
4586
+ }
4587
+ function lastRun(task) {
4588
+ return task.lastRunDate && TIME_RE.test(task.time ?? "") ? iso(task.lastRunDate, task.time) : null;
4589
+ }
4590
+ function scheduleDisplay(task, now = /* @__PURE__ */ new Date()) {
4591
+ try {
4592
+ return {
4593
+ nextRun: nextRun(task, now),
4594
+ lastRun: lastRun(task),
4595
+ scheduleStatus: scheduleState(task, now)
4596
+ };
4597
+ } catch {
4598
+ return { nextRun: null, lastRun: null, scheduleStatus: null };
4599
+ }
4600
+ }
4601
+ function validateScheduleRules(task, now = /* @__PURE__ */ new Date()) {
4602
+ const repeating = isRepeating(task);
4603
+ const { date: today, minutes } = sgtParts(now);
4604
+ if (repeating) {
4605
+ if (!task.startDate)
4606
+ return "Please choose a start date for a repeating schedule.";
4607
+ if (task.repeat === "weekly" && !(task.daysOfWeek && task.daysOfWeek.length)) {
4608
+ return "Pick at least one weekday for a weekly repeat.";
4609
+ }
4610
+ }
4611
+ const start = repeating ? task.startDate : [...task.dates ?? []].sort()[0];
4612
+ if (task.endDate && start && start > task.endDate) {
4613
+ return "The end date must be on or after the start date.";
4614
+ }
4615
+ if (task.endDate && task.endDate < today)
4616
+ return "The end date has already passed.";
4617
+ if (!repeating) {
4618
+ const dates = (task.dates ?? []).filter((d) => !task.endDate || d <= task.endDate);
4619
+ if (dates.length && dates.every((d) => d < today)) {
4620
+ return "All the dates you picked have already passed. Pick today or a later date.";
4621
+ }
4622
+ if (dates.length && dates.every((d) => d <= today) && dates.includes(today) && TIME_RE.test(task.time) && toMinutes(task.time) <= minutes) {
4623
+ return "That time has already passed today. Pick a later time or another date.";
4624
+ }
4625
+ }
4626
+ if (!upcoming({ ...task, paused: false, updatedAt: now }, now)) {
4627
+ return "This schedule would never run. Check the dates, end date and time.";
4628
+ }
4629
+ return null;
4630
+ }
4631
+ var scheduleDate = import_joi10.default.string().pattern(DATE_RE).custom((value, helpers) => isRealDate(value) ? value : helpers.error("date.invalid")).messages({
4632
+ "string.pattern.base": "Dates must look like 2026-10-21.",
4633
+ "string.empty": "Please choose a date.",
4634
+ "date.invalid": "That date does not exist. Dates must look like 2026-10-21.",
4635
+ "string.base": "Dates must look like 2026-10-21."
4636
+ });
4637
+ var REPEATING = import_joi10.default.valid("daily", "weekdays", "weekly", "monthly").required();
4638
+ var scheduleTaskFields = {
4639
+ title: import_joi10.default.string().trim().required().messages({
4640
+ "any.required": "Please enter a title.",
4641
+ "string.empty": "Please enter a title.",
4642
+ "string.base": "Please enter a title."
4643
+ }),
4644
+ time: import_joi10.default.string().pattern(TIME_RE).required().messages({
4645
+ "any.required": "Please choose a time.",
4646
+ "string.empty": "Please choose a time.",
4647
+ "string.pattern.base": "Time must be in 24-hour format like 08:30.",
4648
+ "string.base": "Time must be in 24-hour format like 08:30."
4649
+ }),
4650
+ dates: import_joi10.default.when("repeat", {
4651
+ is: REPEATING,
4652
+ then: import_joi10.default.array().items(scheduleDate).optional().default([]),
4653
+ otherwise: import_joi10.default.array().min(1).items(scheduleDate).required()
4654
+ }).messages({
4655
+ "any.required": "Pick at least one date, or choose a repeat.",
4656
+ "array.min": "Pick at least one date, or choose a repeat.",
4657
+ "array.base": "Dates must be a list of dates."
4658
+ }),
4659
+ description: import_joi10.default.string().optional().allow("", null),
4660
+ areas: import_joi10.default.array().min(1).items(
4661
+ import_joi10.default.object({
4662
+ name: import_joi10.default.string().required(),
4663
+ value: import_joi10.default.any().required()
4664
+ })
4665
+ ).required().messages({
4666
+ "any.required": "Pick at least one area.",
4667
+ "array.min": "Pick at least one area.",
4668
+ "array.base": "Pick at least one area."
4669
+ }),
4670
+ repeat: import_joi10.default.string().valid(...REPEATS).optional().allow("", null).messages({ "any.only": "Repeat must be one of: none, daily, weekdays, weekly, monthly." }),
4671
+ daysOfWeek: import_joi10.default.array().items(import_joi10.default.number().integer().min(0).max(6)).unique().optional().allow(null).messages({
4672
+ "number.base": "Weekdays must be numbers from 0 (Sunday) to 6 (Saturday).",
4673
+ "number.min": "Weekdays must be numbers from 0 (Sunday) to 6 (Saturday).",
4674
+ "number.max": "Weekdays must be numbers from 0 (Sunday) to 6 (Saturday).",
4675
+ "number.integer": "Weekdays must be numbers from 0 (Sunday) to 6 (Saturday).",
4676
+ "array.unique": "Each weekday can only be picked once."
4677
+ }),
4678
+ dayOfMonth: import_joi10.default.number().integer().min(1).max(31).optional().allow(null).messages({ "number.base": "Day of the month must be 1 to 31.", "number.min": "Day of the month must be 1 to 31.", "number.max": "Day of the month must be 1 to 31." }),
4679
+ startDate: scheduleDate.optional().allow("", null),
4680
+ endDate: scheduleDate.optional().allow("", null),
4681
+ paused: import_joi10.default.boolean().optional().allow(null).messages({ "boolean.base": "Paused must be true or false." })
4682
+ };
4683
+ var OPTIONAL_KEYS = ["repeat", "daysOfWeek", "dayOfMonth", "startDate", "endDate", "paused"];
4684
+ var isBlank = (v) => v === "" || v === null;
4685
+ function cleanScheduleInput(value) {
4686
+ const out = { ...value };
4687
+ for (const key of OPTIONAL_KEYS)
4688
+ if (isBlank(out[key]))
4689
+ delete out[key];
4690
+ if (out.repeat === "none") {
4691
+ delete out.daysOfWeek;
4692
+ delete out.dayOfMonth;
4693
+ delete out.startDate;
4694
+ }
4695
+ return out;
4696
+ }
4697
+ var TIMING_KEYS = ["time", "dates", "repeat", "daysOfWeek", "dayOfMonth", "startDate", "endDate"];
4698
+ function splitScheduleUpdate(payload) {
4699
+ const set = {};
4700
+ const unset = [];
4701
+ for (const [key, value] of Object.entries(payload)) {
4702
+ if (OPTIONAL_KEYS.includes(key) && isBlank(value))
4703
+ unset.push(key);
4704
+ else if (value !== void 0)
4705
+ set[key] = value;
4706
+ }
4707
+ if (set.repeat === "none") {
4708
+ for (const key of ["daysOfWeek", "dayOfMonth", "startDate"]) {
4709
+ if (!unset.includes(key))
4710
+ unset.push(key);
4711
+ delete set[key];
4712
+ }
4713
+ }
4714
+ return { set, unset };
4715
+ }
4716
+ function regularSetsMissing(checklist, setCount) {
4717
+ const regular = (checklist ?? []).filter((c) => c.isScheduleTask !== true).length;
4718
+ return Math.max(0, setCount - regular);
4719
+ }
4720
+ var normTiming = (t) => ({
4721
+ time: t.time ?? null,
4722
+ dates: [...Array.isArray(t.dates) ? t.dates : []].sort(),
4723
+ repeat: !t.repeat || t.repeat === "none" ? "none" : t.repeat,
4724
+ daysOfWeek: [...Array.isArray(t.daysOfWeek) ? t.daysOfWeek : []].sort(),
4725
+ dayOfMonth: t.dayOfMonth ?? null,
4726
+ startDate: t.startDate || null,
4727
+ endDate: t.endDate || null
4728
+ });
4729
+ function timingChanged(stored, after) {
4730
+ return JSON.stringify(normTiming(stored)) !== JSON.stringify(normTiming(after));
4731
+ }
4732
+
4469
4733
  // src/services/hygiene-area-checklist.service.ts
4470
4734
  function useAreaChecklistService() {
4471
4735
  const { getAreasForChecklist } = useAreaRepo();
@@ -4533,10 +4797,10 @@ function useAreaChecklistService() {
4533
4797
  `Area checklist for area ${area.name} exists but has no auto-gen sets. Inserting auto-gen sets.`
4534
4798
  );
4535
4799
  const setCount2 = Number(area.set) || 1;
4536
- const totalExistingSets = existing.checklist?.length ?? 0;
4537
- if (totalExistingSets >= setCount2) {
4800
+ const setsToAdd = regularSetsMissing(existing.checklist, setCount2);
4801
+ if (setsToAdd <= 0) {
4538
4802
  import_node_server_utils18.logger.info(
4539
- `Area checklist for area ${area.name} already has ${totalExistingSets} set(s) (configured: ${setCount2}). Skipping auto-gen sets.`
4803
+ `Area checklist for area ${area.name} already has its ${setCount2} regular set(s). Skipping auto-gen sets.`
4540
4804
  );
4541
4805
  return existing._id;
4542
4806
  }
@@ -4544,7 +4808,6 @@ function useAreaChecklistService() {
4544
4808
  (max, c) => Math.max(max, c.set ?? 0),
4545
4809
  0
4546
4810
  ) ?? 0;
4547
- const setsToAdd = setCount2 - totalExistingSets;
4548
4811
  const autoGenSets = Array.from(
4549
4812
  { length: setsToAdd },
4550
4813
  (_, index) => ({
@@ -4705,7 +4968,7 @@ function useAreaChecklistService() {
4705
4968
  }
4706
4969
 
4707
4970
  // src/controllers/hygiene-area-checklist.controller.ts
4708
- var import_joi10 = __toESM(require("joi"));
4971
+ var import_joi11 = __toESM(require("joi"));
4709
4972
  var import_node_server_utils20 = require("@7365admin1/node-server-utils");
4710
4973
  var import_core11 = require("@7365admin1/core");
4711
4974
 
@@ -4946,9 +5209,9 @@ function useAreaChecklistController() {
4946
5209
  site: req.params.site,
4947
5210
  schedule: req.params.schedule
4948
5211
  };
4949
- const validation = import_joi10.default.object({
4950
- site: import_joi10.default.string().hex().required(),
4951
- schedule: import_joi10.default.string().hex().required()
5212
+ const validation = import_joi11.default.object({
5213
+ site: import_joi11.default.string().hex().required(),
5214
+ schedule: import_joi11.default.string().hex().required()
4952
5215
  });
4953
5216
  const { error, value } = validation.validate(payload);
4954
5217
  if (error) {
@@ -4968,14 +5231,14 @@ function useAreaChecklistController() {
4968
5231
  }
4969
5232
  async function getAllAreaChecklist(req, res, next) {
4970
5233
  const query = { ...req.query, ...req.params };
4971
- const validation = import_joi10.default.object({
4972
- page: import_joi10.default.number().min(1).optional().allow("", null),
4973
- limit: import_joi10.default.number().min(1).optional().allow("", null),
4974
- search: import_joi10.default.string().optional().allow("", null),
4975
- type: import_joi10.default.string().valid(...allowedTypes, "all").optional().allow("", null),
4976
- status: import_joi10.default.string().valid(...allowedStatus, "all").optional().allow("", null),
4977
- schedule: import_joi10.default.string().hex().required(),
4978
- serviceType: import_joi10.default.string().valid(...Object.values(import_core11.AppServiceType)).required()
5234
+ const validation = import_joi11.default.object({
5235
+ page: import_joi11.default.number().min(1).optional().allow("", null),
5236
+ limit: import_joi11.default.number().min(1).optional().allow("", null),
5237
+ search: import_joi11.default.string().optional().allow("", null),
5238
+ type: import_joi11.default.string().valid(...allowedTypes, "all").optional().allow("", null),
5239
+ status: import_joi11.default.string().valid(...allowedStatus, "all").optional().allow("", null),
5240
+ schedule: import_joi11.default.string().hex().required(),
5241
+ serviceType: import_joi11.default.string().valid(...Object.values(import_core11.AppServiceType)).required()
4979
5242
  });
4980
5243
  const { error } = validation.validate(query);
4981
5244
  if (error) {
@@ -5010,14 +5273,14 @@ function useAreaChecklistController() {
5010
5273
  }
5011
5274
  async function getAreaChecklistHistory(req, res, next) {
5012
5275
  const query = { ...req.query, ...req.params };
5013
- const validation = import_joi10.default.object({
5014
- page: import_joi10.default.number().min(1).optional().allow("", null),
5015
- limit: import_joi10.default.number().min(1).optional().allow("", null),
5016
- search: import_joi10.default.string().optional().allow("", null),
5017
- type: import_joi10.default.string().optional().allow("", ...allowedTypes),
5018
- schedule: import_joi10.default.string().hex().required(),
5019
- status: import_joi10.default.string().allow("", null, ...allowedStatus),
5020
- createdAt: import_joi10.default.alternatives().try(import_joi10.default.date(), import_joi10.default.string()).optional().allow("", null)
5276
+ const validation = import_joi11.default.object({
5277
+ page: import_joi11.default.number().min(1).optional().allow("", null),
5278
+ limit: import_joi11.default.number().min(1).optional().allow("", null),
5279
+ search: import_joi11.default.string().optional().allow("", null),
5280
+ type: import_joi11.default.string().optional().allow("", ...allowedTypes),
5281
+ schedule: import_joi11.default.string().hex().required(),
5282
+ status: import_joi11.default.string().allow("", null, ...allowedStatus),
5283
+ createdAt: import_joi11.default.alternatives().try(import_joi11.default.date(), import_joi11.default.string()).optional().allow("", null)
5021
5284
  });
5022
5285
  const { error } = validation.validate(query);
5023
5286
  if (error) {
@@ -5051,7 +5314,7 @@ function useAreaChecklistController() {
5051
5314
  }
5052
5315
  }
5053
5316
  async function getAreaChecklistHistoryDetails(req, res, next) {
5054
- const validation = import_joi10.default.string().hex().required();
5317
+ const validation = import_joi11.default.string().hex().required();
5055
5318
  const _id = req.params.id;
5056
5319
  const { error, value } = validation.validate(_id);
5057
5320
  if (error) {
@@ -5071,12 +5334,12 @@ function useAreaChecklistController() {
5071
5334
  }
5072
5335
  async function getAreaChecklistUnits(req, res, next) {
5073
5336
  const query = { ...req.query, ...req.params };
5074
- const validation = import_joi10.default.object({
5075
- page: import_joi10.default.number().min(1).optional().allow("", null),
5076
- limit: import_joi10.default.number().min(1).optional().allow("", null),
5077
- search: import_joi10.default.string().optional().allow("", null),
5078
- serviceType: import_joi10.default.string().valid(...Object.values(import_core11.AppServiceType)).required(),
5079
- id: import_joi10.default.string().hex().required()
5337
+ const validation = import_joi11.default.object({
5338
+ page: import_joi11.default.number().min(1).optional().allow("", null),
5339
+ limit: import_joi11.default.number().min(1).optional().allow("", null),
5340
+ search: import_joi11.default.string().optional().allow("", null),
5341
+ serviceType: import_joi11.default.string().valid(...Object.values(import_core11.AppServiceType)).required(),
5342
+ id: import_joi11.default.string().hex().required()
5080
5343
  });
5081
5344
  const { error } = validation.validate(query);
5082
5345
  if (error) {
@@ -5126,21 +5389,21 @@ function useAreaChecklistController() {
5126
5389
  ...decisionValues,
5127
5390
  completedBy
5128
5391
  };
5129
- const validation = import_joi10.default.object({
5130
- id: import_joi10.default.string().hex().required(),
5131
- set: import_joi10.default.number().integer().min(1).required(),
5132
- unit: import_joi10.default.string().hex().required(),
5133
- decision: import_joi10.default.string().valid("approve", "reject").required(),
5134
- approve: import_joi10.default.boolean().optional(),
5135
- reject: import_joi10.default.boolean().optional(),
5136
- remarks: import_joi10.default.string().optional().allow("", null),
5392
+ const validation = import_joi11.default.object({
5393
+ id: import_joi11.default.string().hex().required(),
5394
+ set: import_joi11.default.number().integer().min(1).required(),
5395
+ unit: import_joi11.default.string().hex().required(),
5396
+ decision: import_joi11.default.string().valid("approve", "reject").required(),
5397
+ approve: import_joi11.default.boolean().optional(),
5398
+ reject: import_joi11.default.boolean().optional(),
5399
+ remarks: import_joi11.default.string().optional().allow("", null),
5137
5400
  /*
5138
5401
  * Shape only. WHETHER a photo is required depends on the set's current
5139
5402
  * state in the database, which Joi cannot see — see the check below.
5140
5403
  * Each entry must still be a real id: a blank string carries no evidence.
5141
5404
  */
5142
- attachment: import_joi10.default.array().items(import_joi10.default.string().trim().min(1)).optional(),
5143
- completedBy: import_joi10.default.string().hex().required()
5405
+ attachment: import_joi11.default.array().items(import_joi11.default.string().trim().min(1)).optional(),
5406
+ completedBy: import_joi11.default.string().hex().required()
5144
5407
  });
5145
5408
  const { error } = validation.validate(payload);
5146
5409
  if (error) {
@@ -5177,7 +5440,7 @@ function useAreaChecklistController() {
5177
5440
  }
5178
5441
  }
5179
5442
  async function downloadChecklistPdf(req, res, next) {
5180
- const validation = import_joi10.default.string().hex().required();
5443
+ const validation = import_joi11.default.string().hex().required();
5181
5444
  const schedule = req.params.schedule;
5182
5445
  const { error, value } = validation.validate(schedule);
5183
5446
  if (error) {
@@ -5223,16 +5486,16 @@ function useAreaChecklistController() {
5223
5486
  }
5224
5487
 
5225
5488
  // src/models/hygiene-supply.model.ts
5226
- var import_joi11 = __toESM(require("joi"));
5489
+ var import_joi12 = __toESM(require("joi"));
5227
5490
  var import_mongodb12 = require("mongodb");
5228
5491
  var import_node_server_utils21 = require("@7365admin1/node-server-utils");
5229
5492
  var import_core12 = require("@7365admin1/core");
5230
- var supplySchema = import_joi11.default.object({
5231
- site: import_joi11.default.string().hex().required(),
5232
- serviceType: import_joi11.default.string().valid(...Object.values(import_core12.AppServiceType)).required(),
5233
- name: import_joi11.default.string().required(),
5234
- unitOfMeasurement: import_joi11.default.string().required(),
5235
- attachment: import_joi11.default.string().allow(null, "").optional()
5493
+ var supplySchema = import_joi12.default.object({
5494
+ site: import_joi12.default.string().hex().required(),
5495
+ serviceType: import_joi12.default.string().valid(...Object.values(import_core12.AppServiceType)).required(),
5496
+ name: import_joi12.default.string().required(),
5497
+ unitOfMeasurement: import_joi12.default.string().required(),
5498
+ attachment: import_joi12.default.string().allow(null, "").optional()
5236
5499
  });
5237
5500
  function MSupply(value) {
5238
5501
  const { error } = supplySchema.validate(value);
@@ -5360,7 +5623,7 @@ function useSupplyRepository() {
5360
5623
  throw new import_node_server_utils22.BadRequestError("Invalid site ID format.");
5361
5624
  }
5362
5625
  if (search) {
5363
- query.name = { $regex: search, $options: "i" };
5626
+ query.name = { $regex: escapeRegex(search), $options: "i" };
5364
5627
  cacheOptions.search = search;
5365
5628
  }
5366
5629
  const cacheKey = (0, import_node_server_utils22.makeCacheKey)(namespace_collection, cacheOptions);
@@ -5522,7 +5785,7 @@ function useSupplyRepository() {
5522
5785
  }
5523
5786
 
5524
5787
  // src/controllers/hygiene-supply.controller.ts
5525
- var import_joi12 = __toESM(require("joi"));
5788
+ var import_joi13 = __toESM(require("joi"));
5526
5789
  var import_node_server_utils23 = require("@7365admin1/node-server-utils");
5527
5790
  var import_core13 = require("@7365admin1/core");
5528
5791
  function useSupplyController() {
@@ -5553,12 +5816,12 @@ function useSupplyController() {
5553
5816
  }
5554
5817
  async function getSupplies(req, res, next) {
5555
5818
  const query = { ...req.query, ...req.params };
5556
- const validation = import_joi12.default.object({
5557
- page: import_joi12.default.number().min(1).optional().allow("", null),
5558
- limit: import_joi12.default.number().min(1).optional().allow("", null),
5559
- search: import_joi12.default.string().optional().allow("", null),
5560
- site: import_joi12.default.string().hex().required(),
5561
- serviceType: import_joi12.default.string().valid(...Object.values(import_core13.AppServiceType)).required()
5819
+ const validation = import_joi13.default.object({
5820
+ page: import_joi13.default.number().min(1).optional().allow("", null),
5821
+ limit: import_joi13.default.number().min(1).optional().allow("", null),
5822
+ search: import_joi13.default.string().optional().allow("", null),
5823
+ site: import_joi13.default.string().hex().required(),
5824
+ serviceType: import_joi13.default.string().valid(...Object.values(import_core13.AppServiceType)).required()
5562
5825
  });
5563
5826
  const { error } = validation.validate(query);
5564
5827
  if (error) {
@@ -5588,7 +5851,7 @@ function useSupplyController() {
5588
5851
  }
5589
5852
  }
5590
5853
  async function getSupplyById(req, res, next) {
5591
- const validation = import_joi12.default.string().hex().required();
5854
+ const validation = import_joi13.default.string().hex().required();
5592
5855
  const _id = req.params.id;
5593
5856
  const { error, value } = validation.validate(_id);
5594
5857
  if (error) {
@@ -5608,12 +5871,12 @@ function useSupplyController() {
5608
5871
  }
5609
5872
  async function updateSupply(req, res, next) {
5610
5873
  const payload = { id: req.params.id, ...req.body };
5611
- const validation = import_joi12.default.object({
5612
- id: import_joi12.default.string().hex().required(),
5613
- name: import_joi12.default.string().optional().allow("", null),
5614
- unitOfMeasurement: import_joi12.default.string().optional().allow("", null),
5615
- qty: import_joi12.default.number().min(0).optional().allow("", null),
5616
- attachment: import_joi12.default.string().optional().allow("", null)
5874
+ const validation = import_joi13.default.object({
5875
+ id: import_joi13.default.string().hex().required(),
5876
+ name: import_joi13.default.string().optional().allow("", null),
5877
+ unitOfMeasurement: import_joi13.default.string().optional().allow("", null),
5878
+ qty: import_joi13.default.number().min(0).optional().allow("", null),
5879
+ attachment: import_joi13.default.string().optional().allow("", null)
5617
5880
  });
5618
5881
  const { error } = validation.validate(payload);
5619
5882
  if (error) {
@@ -5634,7 +5897,7 @@ function useSupplyController() {
5634
5897
  }
5635
5898
  async function deleteSupply(req, res, next) {
5636
5899
  const id = req.params.id;
5637
- const validation = import_joi12.default.string().hex().required();
5900
+ const validation = import_joi13.default.string().hex().required();
5638
5901
  const { error, value } = validation.validate(id);
5639
5902
  if (error) {
5640
5903
  import_node_server_utils23.logger.log({ level: "error", message: error.message });
@@ -5661,18 +5924,18 @@ function useSupplyController() {
5661
5924
  }
5662
5925
 
5663
5926
  // src/models/hygiene-stock.model.ts
5664
- var import_joi13 = __toESM(require("joi"));
5927
+ var import_joi14 = __toESM(require("joi"));
5665
5928
  var import_mongodb14 = require("mongodb");
5666
5929
  var import_node_server_utils24 = require("@7365admin1/node-server-utils");
5667
5930
  var import_core14 = require("@7365admin1/core");
5668
- var stockSchema = import_joi13.default.object({
5669
- site: import_joi13.default.string().hex().required(),
5670
- serviceType: import_joi13.default.string().valid(...Object.values(import_core14.AppServiceType)).required(),
5671
- supply: import_joi13.default.string().hex().required(),
5672
- in: import_joi13.default.number().min(0).optional(),
5673
- out: import_joi13.default.number().min(0).optional(),
5674
- balance: import_joi13.default.number().min(0).required(),
5675
- remarks: import_joi13.default.string().optional().allow("", null)
5931
+ var stockSchema = import_joi14.default.object({
5932
+ site: import_joi14.default.string().hex().required(),
5933
+ serviceType: import_joi14.default.string().valid(...Object.values(import_core14.AppServiceType)).required(),
5934
+ supply: import_joi14.default.string().hex().required(),
5935
+ in: import_joi14.default.number().min(0).optional(),
5936
+ out: import_joi14.default.number().min(0).optional(),
5937
+ balance: import_joi14.default.number().min(0).required(),
5938
+ remarks: import_joi14.default.string().optional().allow("", null)
5676
5939
  });
5677
5940
  function MStock(value) {
5678
5941
  const { error } = stockSchema.validate(value);
@@ -5887,7 +6150,7 @@ function useStockService() {
5887
6150
  }
5888
6151
 
5889
6152
  // src/controllers/hygiene-stock.controller.ts
5890
- var import_joi14 = __toESM(require("joi"));
6153
+ var import_joi15 = __toESM(require("joi"));
5891
6154
  var import_node_server_utils27 = require("@7365admin1/node-server-utils");
5892
6155
  var import_core15 = require("@7365admin1/core");
5893
6156
  function useStockController() {
@@ -5895,12 +6158,12 @@ function useStockController() {
5895
6158
  const { createStock: _createStock } = useStockService();
5896
6159
  async function createStock(req, res, next) {
5897
6160
  const payload = { ...req.body, ...req.params };
5898
- const validation = import_joi14.default.object({
5899
- site: import_joi14.default.string().hex().required(),
5900
- serviceType: import_joi14.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
5901
- supply: import_joi14.default.string().hex().required(),
5902
- qty: import_joi14.default.number().min(0).required(),
5903
- remarks: import_joi14.default.string().optional().allow("", null)
6161
+ const validation = import_joi15.default.object({
6162
+ site: import_joi15.default.string().hex().required(),
6163
+ serviceType: import_joi15.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
6164
+ supply: import_joi15.default.string().hex().required(),
6165
+ qty: import_joi15.default.number().min(0).required(),
6166
+ remarks: import_joi15.default.string().optional().allow("", null)
5904
6167
  });
5905
6168
  const { error } = validation.validate(payload);
5906
6169
  if (error) {
@@ -5920,13 +6183,13 @@ function useStockController() {
5920
6183
  }
5921
6184
  async function getStocksBySupplyId(req, res, next) {
5922
6185
  const query = { ...req.query, ...req.params };
5923
- const validation = import_joi14.default.object({
5924
- page: import_joi14.default.number().min(1).optional().allow("", null),
5925
- limit: import_joi14.default.number().min(1).optional().allow("", null),
5926
- search: import_joi14.default.string().optional().allow("", null),
5927
- site: import_joi14.default.string().hex().required(),
5928
- serviceType: import_joi14.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
5929
- supply: import_joi14.default.string().hex().required()
6186
+ const validation = import_joi15.default.object({
6187
+ page: import_joi15.default.number().min(1).optional().allow("", null),
6188
+ limit: import_joi15.default.number().min(1).optional().allow("", null),
6189
+ search: import_joi15.default.string().optional().allow("", null),
6190
+ site: import_joi15.default.string().hex().required(),
6191
+ serviceType: import_joi15.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
6192
+ supply: import_joi15.default.string().hex().required()
5930
6193
  });
5931
6194
  const { error } = validation.validate(query);
5932
6195
  if (error) {
@@ -5988,21 +6251,21 @@ async function applyCheckOutDecision(decision, decide, returnStock) {
5988
6251
  }
5989
6252
 
5990
6253
  // src/models/hygiene-checkout-item.model.ts
5991
- var import_joi15 = __toESM(require("joi"));
6254
+ var import_joi16 = __toESM(require("joi"));
5992
6255
  var import_mongodb16 = require("mongodb");
5993
6256
  var import_node_server_utils28 = require("@7365admin1/node-server-utils");
5994
6257
  var import_core16 = require("@7365admin1/core");
5995
6258
  var allowedCheckOutItemStatus = ["pending", "completed"];
5996
- var checkOutItemSchema = import_joi15.default.object({
5997
- site: import_joi15.default.string().hex().required(),
5998
- serviceType: import_joi15.default.string().valid(...Object.values(import_core16.AppServiceType)).required(),
5999
- supply: import_joi15.default.string().hex().required(),
6000
- supplyName: import_joi15.default.string().required(),
6001
- supplyQty: import_joi15.default.number().min(0).required(),
6002
- qty: import_joi15.default.number().min(0).required(),
6003
- attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null),
6004
- createdBy: import_joi15.default.string().hex().required(),
6005
- createdByName: import_joi15.default.string().required()
6259
+ var checkOutItemSchema = import_joi16.default.object({
6260
+ site: import_joi16.default.string().hex().required(),
6261
+ serviceType: import_joi16.default.string().valid(...Object.values(import_core16.AppServiceType)).required(),
6262
+ supply: import_joi16.default.string().hex().required(),
6263
+ supplyName: import_joi16.default.string().required(),
6264
+ supplyQty: import_joi16.default.number().min(0).required(),
6265
+ qty: import_joi16.default.number().min(0).required(),
6266
+ attachment: import_joi16.default.array().items(import_joi16.default.string()).optional().allow(null),
6267
+ createdBy: import_joi16.default.string().hex().required(),
6268
+ createdByName: import_joi16.default.string().required()
6006
6269
  });
6007
6270
  function MCheckOutItem(value) {
6008
6271
  const { error } = checkOutItemSchema.validate(value);
@@ -6126,7 +6389,7 @@ function useCheckOutItemRepository() {
6126
6389
  throw new import_node_server_utils29.BadRequestError("Invalid site ID format.");
6127
6390
  }
6128
6391
  if (search) {
6129
- query.supplyName = { $regex: search, $options: "i" };
6392
+ query.supplyName = { $regex: escapeRegex(search), $options: "i" };
6130
6393
  cacheOptions.search = search;
6131
6394
  }
6132
6395
  const cacheKey = (0, import_node_server_utils29.makeCacheKey)(namespace_collection, cacheOptions);
@@ -6482,7 +6745,7 @@ function useCheckOutItemService() {
6482
6745
  }
6483
6746
 
6484
6747
  // src/controllers/hygiene-checkout-item.controller.ts
6485
- var import_joi16 = __toESM(require("joi"));
6748
+ var import_joi17 = __toESM(require("joi"));
6486
6749
  var import_node_server_utils31 = require("@7365admin1/node-server-utils");
6487
6750
  var import_core18 = require("@7365admin1/core");
6488
6751
  function useCheckOutItemController() {
@@ -6506,13 +6769,13 @@ function useCheckOutItemController() {
6506
6769
  ...req.params,
6507
6770
  createdBy
6508
6771
  };
6509
- const validation = import_joi16.default.object({
6510
- site: import_joi16.default.string().hex().required(),
6511
- serviceType: import_joi16.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
6512
- supply: import_joi16.default.string().hex().required(),
6513
- qty: import_joi16.default.number().min(0).required(),
6514
- attachment: import_joi16.default.array().items(import_joi16.default.string()).optional().allow(null),
6515
- createdBy: import_joi16.default.string().hex().required()
6772
+ const validation = import_joi17.default.object({
6773
+ site: import_joi17.default.string().hex().required(),
6774
+ serviceType: import_joi17.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
6775
+ supply: import_joi17.default.string().hex().required(),
6776
+ qty: import_joi17.default.number().min(0).required(),
6777
+ attachment: import_joi17.default.array().items(import_joi17.default.string()).optional().allow(null),
6778
+ createdBy: import_joi17.default.string().hex().required()
6516
6779
  });
6517
6780
  const { error } = validation.validate(payload);
6518
6781
  if (error) {
@@ -6541,15 +6804,15 @@ function useCheckOutItemController() {
6541
6804
  ...req.params,
6542
6805
  createdBy
6543
6806
  };
6544
- const validation = import_joi16.default.object({
6545
- site: import_joi16.default.string().hex().required(),
6546
- serviceType: import_joi16.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
6547
- createdBy: import_joi16.default.string().hex().required(),
6548
- items: import_joi16.default.array().items(
6549
- import_joi16.default.object({
6550
- supply: import_joi16.default.string().hex().required(),
6551
- qty: import_joi16.default.number().min(0).required(),
6552
- attachment: import_joi16.default.array().items(import_joi16.default.string()).optional().allow(null)
6807
+ const validation = import_joi17.default.object({
6808
+ site: import_joi17.default.string().hex().required(),
6809
+ serviceType: import_joi17.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
6810
+ createdBy: import_joi17.default.string().hex().required(),
6811
+ items: import_joi17.default.array().items(
6812
+ import_joi17.default.object({
6813
+ supply: import_joi17.default.string().hex().required(),
6814
+ qty: import_joi17.default.number().min(0).required(),
6815
+ attachment: import_joi17.default.array().items(import_joi17.default.string()).optional().allow(null)
6553
6816
  })
6554
6817
  ).min(1).required()
6555
6818
  });
@@ -6571,12 +6834,12 @@ function useCheckOutItemController() {
6571
6834
  }
6572
6835
  async function getCheckOutItems(req, res, next) {
6573
6836
  const query = { ...req.query, ...req.params };
6574
- const validation = import_joi16.default.object({
6575
- page: import_joi16.default.number().min(1).optional().allow("", null),
6576
- limit: import_joi16.default.number().min(1).optional().allow("", null),
6577
- search: import_joi16.default.string().optional().allow("", null),
6578
- site: import_joi16.default.string().hex().required(),
6579
- serviceType: import_joi16.default.string().valid(...Object.values(import_core18.AppServiceType)).required()
6837
+ const validation = import_joi17.default.object({
6838
+ page: import_joi17.default.number().min(1).optional().allow("", null),
6839
+ limit: import_joi17.default.number().min(1).optional().allow("", null),
6840
+ search: import_joi17.default.string().optional().allow("", null),
6841
+ site: import_joi17.default.string().hex().required(),
6842
+ serviceType: import_joi17.default.string().valid(...Object.values(import_core18.AppServiceType)).required()
6580
6843
  });
6581
6844
  const { error } = validation.validate(query);
6582
6845
  if (error) {
@@ -6606,7 +6869,7 @@ function useCheckOutItemController() {
6606
6869
  }
6607
6870
  }
6608
6871
  async function getCheckOutItemById(req, res, next) {
6609
- const validation = import_joi16.default.string().hex().required();
6872
+ const validation = import_joi17.default.string().hex().required();
6610
6873
  const _id = req.params.id;
6611
6874
  const { error, value } = validation.validate(_id);
6612
6875
  if (error) {
@@ -6645,13 +6908,13 @@ function useCheckOutItemController() {
6645
6908
  ...decisionValues,
6646
6909
  completedBy
6647
6910
  };
6648
- const validation = import_joi16.default.object({
6649
- id: import_joi16.default.string().hex().required(),
6650
- decision: import_joi16.default.string().valid("approve", "disapprove").required(),
6651
- approve: import_joi16.default.boolean().required(),
6652
- reject: import_joi16.default.boolean().required(),
6653
- remarks: import_joi16.default.string().optional().allow("", null),
6654
- completedBy: import_joi16.default.string().hex().required()
6911
+ const validation = import_joi17.default.object({
6912
+ id: import_joi17.default.string().hex().required(),
6913
+ decision: import_joi17.default.string().valid("approve", "disapprove").required(),
6914
+ approve: import_joi17.default.boolean().required(),
6915
+ reject: import_joi17.default.boolean().required(),
6916
+ remarks: import_joi17.default.string().optional().allow("", null),
6917
+ completedBy: import_joi17.default.string().hex().required()
6655
6918
  });
6656
6919
  const { error } = validation.validate(payload);
6657
6920
  if (error) {
@@ -6684,23 +6947,15 @@ function useCheckOutItemController() {
6684
6947
  // src/models/hygiene-schedule-task.model.ts
6685
6948
  var import_node_server_utils32 = require("@7365admin1/node-server-utils");
6686
6949
  var import_core19 = require("@7365admin1/core");
6687
- var import_joi17 = __toESM(require("joi"));
6950
+ var import_joi18 = __toESM(require("joi"));
6688
6951
  var import_mongodb18 = require("mongodb");
6689
- var scheduleTaskSchema = import_joi17.default.object({
6690
- site: import_joi17.default.string().hex().required(),
6691
- serviceType: import_joi17.default.string().valid(...Object.values(import_core19.AppServiceType)).required(),
6692
- title: import_joi17.default.string().required(),
6693
- time: import_joi17.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
6694
- dates: import_joi17.default.array().min(1).items(
6695
- import_joi17.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
6696
- ).required(),
6697
- description: import_joi17.default.string().optional().allow("", null),
6698
- areas: import_joi17.default.array().min(1).items(
6699
- import_joi17.default.object({
6700
- name: import_joi17.default.string().required(),
6701
- value: import_joi17.default.any().required()
6702
- })
6703
- ).required()
6952
+ var scheduleTaskSchema = import_joi18.default.object({
6953
+ site: import_joi18.default.string().hex().required(),
6954
+ serviceType: import_joi18.default.string().valid(...Object.values(import_core19.AppServiceType)).required(),
6955
+ ...scheduleTaskFields
6956
+ }).custom((value, helpers) => {
6957
+ const message = validateScheduleRules(cleanScheduleInput(value));
6958
+ return message ? helpers.message({ custom: message }) : value;
6704
6959
  });
6705
6960
  function MScheduleTask(value) {
6706
6961
  const { error } = scheduleTaskSchema.validate(value);
@@ -6727,14 +6982,26 @@ function MScheduleTask(value) {
6727
6982
  }
6728
6983
  });
6729
6984
  }
6985
+ const extra = cleanScheduleInput({
6986
+ repeat: value.repeat,
6987
+ daysOfWeek: value.daysOfWeek,
6988
+ dayOfMonth: value.dayOfMonth,
6989
+ startDate: value.startDate,
6990
+ endDate: value.endDate,
6991
+ paused: value.paused
6992
+ });
6993
+ for (const key of Object.keys(extra))
6994
+ if (extra[key] === void 0)
6995
+ delete extra[key];
6730
6996
  return {
6731
6997
  site: value.site,
6732
6998
  serviceType: value.serviceType,
6733
6999
  title: value.title,
6734
7000
  time: value.time,
6735
- dates: value.dates,
7001
+ dates: value.dates ?? [],
6736
7002
  description: value.description,
6737
7003
  areas: value.areas,
7004
+ ...extra,
6738
7005
  status: "active",
6739
7006
  createdAt: /* @__PURE__ */ new Date(),
6740
7007
  updatedAt: "",
@@ -6745,11 +7012,23 @@ function MScheduleTask(value) {
6745
7012
  // src/repositories/hygiene-schedule-task.repository.ts
6746
7013
  var import_mongodb19 = require("mongodb");
6747
7014
  var import_node_server_utils33 = require("@7365admin1/node-server-utils");
7015
+ var SCHEDULE_FIELDS = {
7016
+ time: 1,
7017
+ dates: 1,
7018
+ repeat: 1,
7019
+ daysOfWeek: 1,
7020
+ dayOfMonth: 1,
7021
+ startDate: 1,
7022
+ endDate: 1,
7023
+ paused: 1,
7024
+ lastRunDate: 1
7025
+ };
6748
7026
  function useScheduleTaskRepository() {
6749
7027
  const db = import_node_server_utils33.useAtlas.getDb();
6750
7028
  if (!db) {
6751
7029
  throw new import_node_server_utils33.InternalServerError("Unable to connect to server.");
6752
7030
  }
7031
+ const database = db;
6753
7032
  const namespace_collection = "site.schedule-tasks";
6754
7033
  const collection = db.collection(namespace_collection);
6755
7034
  const { delNamespace, setCache, getCache } = (0, import_node_server_utils33.useCache)(namespace_collection);
@@ -6817,14 +7096,14 @@ function useScheduleTaskRepository() {
6817
7096
  throw new import_node_server_utils33.BadRequestError("Invalid site ID format.");
6818
7097
  }
6819
7098
  if (search) {
6820
- query.$or = [{ title: { $regex: search, $options: "i" } }];
7099
+ query.$or = [{ title: { $regex: escapeRegex(search), $options: "i" } }];
6821
7100
  cacheOptions.search = search;
6822
7101
  }
6823
7102
  const cacheKey = (0, import_node_server_utils33.makeCacheKey)(namespace_collection, cacheOptions);
6824
7103
  const cachedData = await getCache(cacheKey);
6825
7104
  if (cachedData) {
6826
7105
  import_node_server_utils33.logger.info(`Cache hit for key: ${cacheKey}`);
6827
- return cachedData;
7106
+ return enrichList(cachedData, site, serviceType);
6828
7107
  }
6829
7108
  try {
6830
7109
  const items = await collection.aggregate([
@@ -6833,7 +7112,8 @@ function useScheduleTaskRepository() {
6833
7112
  $project: {
6834
7113
  title: 1,
6835
7114
  areas: 1,
6836
- status: 1
7115
+ status: 1,
7116
+ ...SCHEDULE_FIELDS
6837
7117
  }
6838
7118
  },
6839
7119
  { $sort: { _id: -1 } },
@@ -6847,15 +7127,104 @@ function useScheduleTaskRepository() {
6847
7127
  }).catch((err) => {
6848
7128
  import_node_server_utils33.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
6849
7129
  });
6850
- return data;
7130
+ return enrichList(data, site, serviceType);
6851
7131
  } catch (error) {
6852
7132
  throw error;
6853
7133
  }
6854
7134
  }
6855
- async function getAllScheduleTask() {
7135
+ async function enrichList(data, site, serviceType) {
7136
+ const items = data?.items ?? [];
7137
+ const now = /* @__PURE__ */ new Date();
7138
+ let statuses = /* @__PURE__ */ new Map();
7139
+ try {
7140
+ statuses = await getRunStatuses(items, site, serviceType);
7141
+ } catch (error) {
7142
+ import_node_server_utils33.logger.error("Could not read schedule task run status", error);
7143
+ }
7144
+ return {
7145
+ ...data,
7146
+ items: items.map((item) => {
7147
+ try {
7148
+ return {
7149
+ ...item,
7150
+ ...scheduleDisplay(item, now),
7151
+ lastRunStatus: statuses.get(String(item._id)) ?? null
7152
+ };
7153
+ } catch (error) {
7154
+ import_node_server_utils33.logger.error(`Could not enrich schedule task ${item?._id}`, error);
7155
+ return { ...item, nextRun: null, lastRun: null, scheduleStatus: null, lastRunStatus: null };
7156
+ }
7157
+ })
7158
+ };
7159
+ }
7160
+ async function getRunStatuses(items, site, serviceType) {
7161
+ const result = /* @__PURE__ */ new Map();
7162
+ const ran = items.filter((i) => i.lastRunDate);
7163
+ if (!ran.length)
7164
+ return result;
7165
+ const parents = await database.collection("site.cleaning.schedules").find(
7166
+ {
7167
+ site,
7168
+ serviceType,
7169
+ dateStr: { $in: [...new Set(ran.map((i) => i.lastRunDate))] }
7170
+ },
7171
+ { projection: { dateStr: 1 } }
7172
+ ).toArray();
7173
+ if (!parents.length)
7174
+ return result;
7175
+ const dateOf = new Map(parents.map((p) => [String(p._id), p.dateStr]));
7176
+ const taskIds = ran.map((i) => new import_mongodb19.ObjectId(i._id));
7177
+ const rows = await database.collection("site.cleaning.schedule.areas").aggregate([
7178
+ {
7179
+ $match: {
7180
+ schedule: { $in: parents.map((p) => p._id) },
7181
+ "checklist.scheduleTaskId": { $in: taskIds }
7182
+ }
7183
+ },
7184
+ { $unwind: "$checklist" },
7185
+ { $match: { "checklist.scheduleTaskId": { $in: taskIds } } },
7186
+ { $unwind: "$checklist.units" },
7187
+ {
7188
+ $group: {
7189
+ _id: { task: "$checklist.scheduleTaskId", schedule: "$schedule" },
7190
+ total: { $sum: 1 },
7191
+ completed: {
7192
+ $sum: { $cond: [{ $eq: ["$checklist.units.status", "completed"] }, 1, 0] }
7193
+ },
7194
+ closed: {
7195
+ $sum: { $cond: [{ $eq: ["$checklist.units.status", "closed"] }, 1, 0] }
7196
+ }
7197
+ }
7198
+ }
7199
+ ]).toArray();
7200
+ const sums = /* @__PURE__ */ new Map();
7201
+ for (const row of rows) {
7202
+ const task = ran.find((i) => String(i._id) === String(row._id.task));
7203
+ if (!task || dateOf.get(String(row._id.schedule)) !== task.lastRunDate)
7204
+ continue;
7205
+ const sum = sums.get(String(task._id)) ?? { total: 0, completed: 0, closed: 0 };
7206
+ sum.total += row.total;
7207
+ sum.completed += row.completed;
7208
+ sum.closed += row.closed;
7209
+ sums.set(String(task._id), sum);
7210
+ }
7211
+ for (const [id, sum] of sums) {
7212
+ result.set(id, sum.completed === sum.total ? "done" : sum.closed > 0 ? "missed" : "pending");
7213
+ }
7214
+ return result;
7215
+ }
7216
+ async function getAllScheduleTask(today) {
6856
7217
  const query = {
6857
7218
  status: { $ne: "deleted" }
6858
7219
  };
7220
+ if (today) {
7221
+ query.paused = { $ne: true };
7222
+ query.lastRunDate = { $ne: today };
7223
+ query.$or = [
7224
+ { dates: today },
7225
+ { repeat: { $in: ["daily", "weekdays", "weekly", "monthly"] } }
7226
+ ];
7227
+ }
6859
7228
  try {
6860
7229
  const items = await collection.aggregate([{ $match: query }, { $sort: { _id: -1 } }]).toArray();
6861
7230
  return items;
@@ -6863,6 +7232,9 @@ function useScheduleTaskRepository() {
6863
7232
  throw error;
6864
7233
  }
6865
7234
  }
7235
+ function withScheduleDisplay(row) {
7236
+ return { ...row, ...scheduleDisplay(row, /* @__PURE__ */ new Date()) };
7237
+ }
6866
7238
  async function getScheduleTaskById(_id, session) {
6867
7239
  try {
6868
7240
  _id = new import_mongodb19.ObjectId(_id);
@@ -6880,7 +7252,7 @@ function useScheduleTaskRepository() {
6880
7252
  const cachedData = await getCache(cacheKey);
6881
7253
  if (cachedData) {
6882
7254
  import_node_server_utils33.logger.info(`Cache hit for key: ${cacheKey}`);
6883
- return cachedData;
7255
+ return withScheduleDisplay(cachedData);
6884
7256
  }
6885
7257
  } else {
6886
7258
  import_node_server_utils33.logger.info(`Skipping cache during transaction for key: ${cacheKey}`);
@@ -6891,12 +7263,13 @@ function useScheduleTaskRepository() {
6891
7263
  {
6892
7264
  $project: {
6893
7265
  title: 1,
6894
- time: 1,
6895
- dates: 1,
6896
7266
  description: 1,
6897
7267
  areas: 1,
6898
7268
  status: 1,
6899
- createdAt: 1
7269
+ createdAt: 1,
7270
+ site: 1,
7271
+ serviceType: 1,
7272
+ ...SCHEDULE_FIELDS
6900
7273
  }
6901
7274
  }
6902
7275
  ]).toArray();
@@ -6908,7 +7281,7 @@ function useScheduleTaskRepository() {
6908
7281
  }).catch((err) => {
6909
7282
  import_node_server_utils33.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
6910
7283
  });
6911
- return data[0];
7284
+ return withScheduleDisplay(data[0]);
6912
7285
  } catch (error) {
6913
7286
  throw error;
6914
7287
  }
@@ -6932,16 +7305,20 @@ function useScheduleTaskRepository() {
6932
7305
  });
6933
7306
  }
6934
7307
  try {
6935
- const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
7308
+ const { set, unset } = splitScheduleUpdate(value);
7309
+ const update = {
7310
+ $set: { ...set, updatedAt: /* @__PURE__ */ new Date() }
7311
+ };
7312
+ if (unset.length) {
7313
+ update.$unset = Object.fromEntries(unset.map((key) => [key, ""]));
7314
+ }
6936
7315
  const res = await collection.updateOne(
6937
- { _id },
6938
- { $set: updateValue },
7316
+ { _id, status: { $ne: "deleted" } },
7317
+ update,
6939
7318
  { session }
6940
7319
  );
6941
- if (res.modifiedCount === 0) {
6942
- throw new import_node_server_utils33.InternalServerError(
6943
- "Unable to update hygiene schedule task."
6944
- );
7320
+ if (res.matchedCount === 0) {
7321
+ throw new import_node_server_utils33.NotFoundError("Schedule task not found.");
6945
7322
  }
6946
7323
  delNamespace().then(() => {
6947
7324
  import_node_server_utils33.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
@@ -6956,6 +7333,18 @@ function useScheduleTaskRepository() {
6956
7333
  throw error;
6957
7334
  }
6958
7335
  }
7336
+ async function markScheduleTaskRan(_id, date) {
7337
+ await collection.updateOne(
7338
+ { _id: new import_mongodb19.ObjectId(_id) },
7339
+ { $set: { lastRunDate: date, lastRunAt: /* @__PURE__ */ new Date() } }
7340
+ );
7341
+ delNamespace().catch((err) => {
7342
+ import_node_server_utils33.logger.error(
7343
+ `Failed to clear cache for namespace: ${namespace_collection}`,
7344
+ err
7345
+ );
7346
+ });
7347
+ }
6959
7348
  async function deleteScheduleTask(_id, session) {
6960
7349
  try {
6961
7350
  _id = new import_mongodb19.ObjectId(_id);
@@ -6995,6 +7384,7 @@ function useScheduleTaskRepository() {
6995
7384
  createScheduleTask,
6996
7385
  getScheduleTasks,
6997
7386
  getAllScheduleTask,
7387
+ markScheduleTaskRan,
6998
7388
  getScheduleTaskById,
6999
7389
  updateScheduleTask,
7000
7390
  deleteScheduleTask
@@ -7006,7 +7396,7 @@ var import_mongodb20 = require("mongodb");
7006
7396
  var import_node_server_utils34 = require("@7365admin1/node-server-utils");
7007
7397
  function useScheduleTaskService() {
7008
7398
  const { createParentChecklist } = useParentChecklistRepo();
7009
- const { getAllScheduleTask } = useScheduleTaskRepository();
7399
+ const { getAllScheduleTask, markScheduleTaskRan } = useScheduleTaskRepository();
7010
7400
  const {
7011
7401
  createAreaChecklist,
7012
7402
  getAreaChecklistByAreaAndSchedule,
@@ -7015,73 +7405,7 @@ function useScheduleTaskService() {
7015
7405
  const { getAreaById } = useAreaRepo();
7016
7406
  function checkScheduleConditions(schedule, serviceType, currentDate = /* @__PURE__ */ new Date()) {
7017
7407
  try {
7018
- const now = currentDate;
7019
- const timeString = now.toLocaleTimeString("en-US", {
7020
- hour: "2-digit",
7021
- minute: "2-digit",
7022
- hour12: false,
7023
- timeZone: "Asia/Singapore"
7024
- });
7025
- const [currentHour, currentMinute] = timeString.split(":").map(Number);
7026
- import_node_server_utils34.logger.info(
7027
- `Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
7028
- );
7029
- const currentDateFormatted = now.toLocaleDateString("en-CA", {
7030
- timeZone: "Asia/Singapore"
7031
- });
7032
- if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
7033
- import_node_server_utils34.logger.info(`Schedule ${schedule._id}: No dates configured, skipping`);
7034
- return false;
7035
- }
7036
- if (!schedule.dates.includes(currentDateFormatted)) {
7037
- import_node_server_utils34.logger.info(
7038
- `Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
7039
- ", "
7040
- )}]`
7041
- );
7042
- return false;
7043
- }
7044
- const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
7045
- const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
7046
- if (!timeMatches) {
7047
- import_node_server_utils34.logger.info(
7048
- `Schedule ${schedule._id}: Time does not match. Current: ${currentHour}:${currentMinute}, Expected: ${scheduleHour}:${scheduleMinute}`
7049
- );
7050
- return false;
7051
- }
7052
- const relevantTimestamp = schedule.updatedAt || schedule.createdAt;
7053
- if (relevantTimestamp) {
7054
- const savedAt = new Date(relevantTimestamp);
7055
- const savedHour = parseInt(
7056
- savedAt.toLocaleTimeString("en-US", {
7057
- hour: "2-digit",
7058
- hour12: false,
7059
- timeZone: "Asia/Singapore"
7060
- }),
7061
- 10
7062
- );
7063
- const savedMinute = savedAt.toLocaleString("en-US", {
7064
- minute: "2-digit",
7065
- timeZone: "Asia/Singapore"
7066
- });
7067
- const savedDateFormatted = savedAt.toLocaleDateString("en-CA", {
7068
- timeZone: "Asia/Singapore"
7069
- });
7070
- const savedMinNum = parseInt(savedMinute, 10);
7071
- const wasSavedThisMinute = savedDateFormatted === currentDateFormatted && savedHour === currentHour && savedMinNum === currentMinute;
7072
- if (wasSavedThisMinute) {
7073
- import_node_server_utils34.logger.info(
7074
- `Schedule ${schedule._id}: Skipping \u2014 task was saved this minute (${savedHour}:${String(
7075
- savedMinNum
7076
- ).padStart(2, "0")}). Will fire on next occurrence.`
7077
- );
7078
- return false;
7079
- }
7080
- }
7081
- import_node_server_utils34.logger.info(
7082
- `Schedule ${schedule._id}: All conditions matched - Date is within range and time matches`
7083
- );
7084
- return true;
7408
+ return shouldRunNow(schedule, currentDate);
7085
7409
  } catch (error) {
7086
7410
  import_node_server_utils34.logger.error(
7087
7411
  `Error checking schedule conditions for ${schedule._id}:`,
@@ -7090,18 +7414,22 @@ function useScheduleTaskService() {
7090
7414
  return false;
7091
7415
  }
7092
7416
  }
7417
+ const MAX_ATTEMPTS = 5;
7418
+ const attempts = /* @__PURE__ */ new Map();
7093
7419
  async function processScheduledTasks(currentDate) {
7094
7420
  try {
7095
7421
  import_node_server_utils34.logger.info("Starting scheduled task processing...");
7096
- const scheduleTasks = await getAllScheduleTask();
7422
+ const today = sgtParts(currentDate ?? /* @__PURE__ */ new Date()).date;
7423
+ const scheduleTasks = await getAllScheduleTask(today);
7097
7424
  if (!scheduleTasks || scheduleTasks.length === 0) {
7098
7425
  import_node_server_utils34.logger.info("No schedule tasks found to process");
7099
- return { processed: 0, validated: 0 };
7426
+ return { processed: 0, validated: 0, fired: [] };
7100
7427
  }
7101
7428
  import_node_server_utils34.logger.info(`Found ${scheduleTasks.length} schedule tasks to check`);
7102
7429
  let processedCount = 0;
7103
7430
  let validatedCount = 0;
7104
7431
  const validatedTasks = [];
7432
+ const fired = [];
7105
7433
  for (const scheduleTask of scheduleTasks) {
7106
7434
  try {
7107
7435
  const serviceType = scheduleTask.serviceType;
@@ -7144,6 +7472,8 @@ function useScheduleTaskService() {
7144
7472
  import_node_server_utils34.logger.info(
7145
7473
  `Using parent checklist ${parentChecklistId}, now creating/updating area checklists`
7146
7474
  );
7475
+ let failedAreas = 0;
7476
+ const addedAreas = [];
7147
7477
  for (const area of scheduleTask.areas) {
7148
7478
  try {
7149
7479
  const areaId = area.value.toString();
@@ -7223,6 +7553,7 @@ function useScheduleTaskService() {
7223
7553
  newSets
7224
7554
  );
7225
7555
  if (modified > 0) {
7556
+ addedAreas.push(area.name);
7226
7557
  import_node_server_utils34.logger.info(
7227
7558
  `Appended ${areaSetConfig} set(s) starting from ${startSetNumber} to area checklist for area ${area.name}`
7228
7559
  );
@@ -7255,13 +7586,32 @@ function useScheduleTaskService() {
7255
7586
  `Area ${area.name} (${areaId}): Creating new area checklist with schedule-task sets only`
7256
7587
  );
7257
7588
  await createAreaChecklist(checklistData);
7589
+ addedAreas.push(area.name);
7258
7590
  import_node_server_utils34.logger.info(`Created new area checklist for area ${area.name}`);
7259
7591
  }
7260
7592
  } catch (error) {
7261
7593
  import_node_server_utils34.logger.error(`Error processing area ${area.name}:`, error);
7594
+ failedAreas++;
7262
7595
  continue;
7263
7596
  }
7264
7597
  }
7598
+ const attemptKey = `${scheduleTask._id}:${today}`;
7599
+ const tried = (attempts.get(attemptKey) ?? 0) + 1;
7600
+ attempts.set(attemptKey, tried);
7601
+ if (failedAreas === 0 || tried >= MAX_ATTEMPTS) {
7602
+ await markScheduleTaskRan(scheduleTask._id, today);
7603
+ attempts.delete(attemptKey);
7604
+ }
7605
+ if (addedAreas.length > 0) {
7606
+ fired.push({
7607
+ _id: scheduleTask._id,
7608
+ site: scheduleTask.site,
7609
+ serviceType,
7610
+ title: scheduleTask.title,
7611
+ time: scheduleTask.time,
7612
+ areas: addedAreas
7613
+ });
7614
+ }
7265
7615
  processedCount++;
7266
7616
  validatedCount++;
7267
7617
  validatedTasks.push(scheduleTask);
@@ -7273,6 +7623,17 @@ function useScheduleTaskService() {
7273
7623
  `Error processing schedule task ${scheduleTask._id}:`,
7274
7624
  error
7275
7625
  );
7626
+ try {
7627
+ const key = `${scheduleTask._id}:${today}`;
7628
+ const tried = (attempts.get(key) ?? 0) + 1;
7629
+ attempts.set(key, tried);
7630
+ if (tried >= MAX_ATTEMPTS && scheduleTask._id) {
7631
+ await markScheduleTaskRan(scheduleTask._id, today);
7632
+ attempts.delete(key);
7633
+ }
7634
+ } catch (markError) {
7635
+ import_node_server_utils34.logger.error("Could not mark failing schedule task", markError);
7636
+ }
7276
7637
  continue;
7277
7638
  }
7278
7639
  }
@@ -7282,7 +7643,8 @@ function useScheduleTaskService() {
7282
7643
  return {
7283
7644
  processed: processedCount,
7284
7645
  validated: validatedCount,
7285
- tasks: validatedTasks
7646
+ tasks: validatedTasks,
7647
+ fired
7286
7648
  };
7287
7649
  } catch (error) {
7288
7650
  import_node_server_utils34.logger.error("Error processing scheduled tasks:", error);
@@ -7293,7 +7655,7 @@ function useScheduleTaskService() {
7293
7655
  }
7294
7656
 
7295
7657
  // src/controllers/hygiene-schedule-task.controller.ts
7296
- var import_joi18 = __toESM(require("joi"));
7658
+ var import_joi19 = __toESM(require("joi"));
7297
7659
  var import_node_server_utils35 = require("@7365admin1/node-server-utils");
7298
7660
  var import_core20 = require("@7365admin1/core");
7299
7661
  function useScheduleTaskController() {
@@ -7324,12 +7686,12 @@ function useScheduleTaskController() {
7324
7686
  }
7325
7687
  async function getScheduleTasks(req, res, next) {
7326
7688
  const query = { ...req.query, ...req.params };
7327
- const validation = import_joi18.default.object({
7328
- page: import_joi18.default.number().min(1).optional().allow("", null),
7329
- limit: import_joi18.default.number().min(1).optional().allow("", null),
7330
- search: import_joi18.default.string().optional().allow("", null),
7331
- site: import_joi18.default.string().hex().required(),
7332
- serviceType: import_joi18.default.string().valid(...Object.values(import_core20.AppServiceType)).required()
7689
+ const validation = import_joi19.default.object({
7690
+ page: import_joi19.default.number().min(1).optional().allow("", null),
7691
+ limit: import_joi19.default.number().min(1).optional().allow("", null),
7692
+ search: import_joi19.default.string().optional().allow("", null),
7693
+ site: import_joi19.default.string().hex().required(),
7694
+ serviceType: import_joi19.default.string().valid(...Object.values(import_core20.AppServiceType)).required()
7333
7695
  });
7334
7696
  const { error } = validation.validate(query);
7335
7697
  if (error) {
@@ -7337,8 +7699,8 @@ function useScheduleTaskController() {
7337
7699
  next(new import_node_server_utils35.BadRequestError(error.message));
7338
7700
  return;
7339
7701
  }
7340
- const page = parseInt(req.query.page) ?? 1;
7341
- const limit = parseInt(req.query.limit) ?? 10;
7702
+ const page = parseInt(req.query.page) || 1;
7703
+ const limit = parseInt(req.query.limit) || 10;
7342
7704
  const search = req.query.search ?? "";
7343
7705
  const site = req.params.site ?? "";
7344
7706
  const serviceType = req.query.serviceType ?? "";
@@ -7359,7 +7721,7 @@ function useScheduleTaskController() {
7359
7721
  }
7360
7722
  }
7361
7723
  async function getScheduleTaskById(req, res, next) {
7362
- const validation = import_joi18.default.string().hex().required();
7724
+ const validation = import_joi19.default.string().hex().required();
7363
7725
  const _id = req.params.id;
7364
7726
  const { error, value } = validation.validate(_id);
7365
7727
  if (error) {
@@ -7379,20 +7741,17 @@ function useScheduleTaskController() {
7379
7741
  }
7380
7742
  async function updateScheduleTask(req, res, next) {
7381
7743
  const payload = { id: req.params.id, ...req.body };
7382
- const validation = import_joi18.default.object({
7383
- id: import_joi18.default.string().hex().required(),
7384
- title: import_joi18.default.string().optional().allow("", null),
7385
- time: import_joi18.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).optional().allow("", null),
7386
- dates: import_joi18.default.array().min(1).items(
7387
- import_joi18.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
7388
- ).optional(),
7389
- description: import_joi18.default.string().optional().allow("", null),
7390
- areas: import_joi18.default.array().min(1).items(
7391
- import_joi18.default.object({
7392
- name: import_joi18.default.string().required(),
7393
- value: import_joi18.default.any().required()
7394
- })
7395
- ).optional()
7744
+ const optional = Object.fromEntries(
7745
+ Object.entries(scheduleTaskFields).map(([key, rule]) => [
7746
+ key,
7747
+ key === "dates" ? import_joi19.default.array().min(1).items(scheduleDate).optional().messages({
7748
+ "array.min": "Pick at least one date, or choose a repeat."
7749
+ }) : rule.optional()
7750
+ ])
7751
+ );
7752
+ const validation = import_joi19.default.object({
7753
+ id: import_joi19.default.string().hex().required(),
7754
+ ...optional
7396
7755
  });
7397
7756
  const { error } = validation.validate(payload);
7398
7757
  if (error) {
@@ -7400,6 +7759,24 @@ function useScheduleTaskController() {
7400
7759
  next(new import_node_server_utils35.BadRequestError(error.message));
7401
7760
  return;
7402
7761
  }
7762
+ try {
7763
+ const { set, unset } = splitScheduleUpdate(req.body ?? {});
7764
+ if (TIMING_KEYS.some((key) => key in set || unset.includes(key))) {
7765
+ const stored = await _getScheduleTaskById(req.params.id);
7766
+ const merged = { ...stored, ...set };
7767
+ for (const key of unset)
7768
+ delete merged[key];
7769
+ const problem = timingChanged(stored, merged) ? validateScheduleRules(cleanScheduleInput(merged)) : null;
7770
+ if (problem) {
7771
+ next(new import_node_server_utils35.BadRequestError(problem));
7772
+ return;
7773
+ }
7774
+ }
7775
+ } catch (error2) {
7776
+ import_node_server_utils35.logger.log({ level: "error", message: error2.message });
7777
+ next(error2);
7778
+ return;
7779
+ }
7403
7780
  try {
7404
7781
  const { id, ...value } = payload;
7405
7782
  await _updateScheduleTask(id, value);
@@ -7412,7 +7789,7 @@ function useScheduleTaskController() {
7412
7789
  }
7413
7790
  }
7414
7791
  async function deleteScheduleTask(req, res, next) {
7415
- const validation = import_joi18.default.string().hex().required();
7792
+ const validation = import_joi19.default.string().hex().required();
7416
7793
  const _id = req.params.id;
7417
7794
  const { error, value } = validation.validate(_id);
7418
7795
  if (error) {
@@ -7656,17 +8033,17 @@ function useQRService() {
7656
8033
  }
7657
8034
 
7658
8035
  // src/controllers/hygiene-qr.controller.ts
7659
- var import_joi19 = __toESM(require("joi"));
8036
+ var import_joi20 = __toESM(require("joi"));
7660
8037
  var import_node_server_utils37 = require("@7365admin1/node-server-utils");
7661
8038
  function useQRController() {
7662
8039
  const { generateQRImage: _generateQRImage, generateQRPDF: _generateQRPDF } = useQRService();
7663
8040
  async function generateQR(req, res, next) {
7664
- const validation = import_joi19.default.object({
7665
- url: import_joi19.default.string().uri().required(),
7666
- filename: import_joi19.default.string().optional().allow("", null),
7667
- title: import_joi19.default.string().optional().allow("", null),
7668
- subtitle: import_joi19.default.string().optional().allow("", null),
7669
- download: import_joi19.default.boolean().optional().default(false)
8041
+ const validation = import_joi20.default.object({
8042
+ url: import_joi20.default.string().uri().required(),
8043
+ filename: import_joi20.default.string().optional().allow("", null),
8044
+ title: import_joi20.default.string().optional().allow("", null),
8045
+ subtitle: import_joi20.default.string().optional().allow("", null),
8046
+ download: import_joi20.default.boolean().optional().default(false)
7670
8047
  });
7671
8048
  const query = { ...req.query };
7672
8049
  const { error, value } = validation.validate(query);