@7365admin1/module-hygiene 4.29.1-staging.15 → 4.29.1-staging.16
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/.changeset/schedule-task-repeat-pause.md +5 -0
- package/dist/index.d.ts +40 -10
- package/dist/index.js +637 -265
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +637 -265
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/schedule-task-rules.test.mjs +185 -0
package/dist/index.js
CHANGED
|
@@ -4471,6 +4471,265 @@ function useAreaChecklistRepo() {
|
|
|
4471
4471
|
};
|
|
4472
4472
|
}
|
|
4473
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
|
+
|
|
4474
4733
|
// src/services/hygiene-area-checklist.service.ts
|
|
4475
4734
|
function useAreaChecklistService() {
|
|
4476
4735
|
const { getAreasForChecklist } = useAreaRepo();
|
|
@@ -4538,10 +4797,10 @@ function useAreaChecklistService() {
|
|
|
4538
4797
|
`Area checklist for area ${area.name} exists but has no auto-gen sets. Inserting auto-gen sets.`
|
|
4539
4798
|
);
|
|
4540
4799
|
const setCount2 = Number(area.set) || 1;
|
|
4541
|
-
const
|
|
4542
|
-
if (
|
|
4800
|
+
const setsToAdd = regularSetsMissing(existing.checklist, setCount2);
|
|
4801
|
+
if (setsToAdd <= 0) {
|
|
4543
4802
|
import_node_server_utils18.logger.info(
|
|
4544
|
-
`Area checklist for area ${area.name} already has ${
|
|
4803
|
+
`Area checklist for area ${area.name} already has its ${setCount2} regular set(s). Skipping auto-gen sets.`
|
|
4545
4804
|
);
|
|
4546
4805
|
return existing._id;
|
|
4547
4806
|
}
|
|
@@ -4549,7 +4808,6 @@ function useAreaChecklistService() {
|
|
|
4549
4808
|
(max, c) => Math.max(max, c.set ?? 0),
|
|
4550
4809
|
0
|
|
4551
4810
|
) ?? 0;
|
|
4552
|
-
const setsToAdd = setCount2 - totalExistingSets;
|
|
4553
4811
|
const autoGenSets = Array.from(
|
|
4554
4812
|
{ length: setsToAdd },
|
|
4555
4813
|
(_, index) => ({
|
|
@@ -4710,7 +4968,7 @@ function useAreaChecklistService() {
|
|
|
4710
4968
|
}
|
|
4711
4969
|
|
|
4712
4970
|
// src/controllers/hygiene-area-checklist.controller.ts
|
|
4713
|
-
var
|
|
4971
|
+
var import_joi11 = __toESM(require("joi"));
|
|
4714
4972
|
var import_node_server_utils20 = require("@7365admin1/node-server-utils");
|
|
4715
4973
|
var import_core11 = require("@7365admin1/core");
|
|
4716
4974
|
|
|
@@ -4951,9 +5209,9 @@ function useAreaChecklistController() {
|
|
|
4951
5209
|
site: req.params.site,
|
|
4952
5210
|
schedule: req.params.schedule
|
|
4953
5211
|
};
|
|
4954
|
-
const validation =
|
|
4955
|
-
site:
|
|
4956
|
-
schedule:
|
|
5212
|
+
const validation = import_joi11.default.object({
|
|
5213
|
+
site: import_joi11.default.string().hex().required(),
|
|
5214
|
+
schedule: import_joi11.default.string().hex().required()
|
|
4957
5215
|
});
|
|
4958
5216
|
const { error, value } = validation.validate(payload);
|
|
4959
5217
|
if (error) {
|
|
@@ -4973,14 +5231,14 @@ function useAreaChecklistController() {
|
|
|
4973
5231
|
}
|
|
4974
5232
|
async function getAllAreaChecklist(req, res, next) {
|
|
4975
5233
|
const query = { ...req.query, ...req.params };
|
|
4976
|
-
const validation =
|
|
4977
|
-
page:
|
|
4978
|
-
limit:
|
|
4979
|
-
search:
|
|
4980
|
-
type:
|
|
4981
|
-
status:
|
|
4982
|
-
schedule:
|
|
4983
|
-
serviceType:
|
|
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()
|
|
4984
5242
|
});
|
|
4985
5243
|
const { error } = validation.validate(query);
|
|
4986
5244
|
if (error) {
|
|
@@ -5015,14 +5273,14 @@ function useAreaChecklistController() {
|
|
|
5015
5273
|
}
|
|
5016
5274
|
async function getAreaChecklistHistory(req, res, next) {
|
|
5017
5275
|
const query = { ...req.query, ...req.params };
|
|
5018
|
-
const validation =
|
|
5019
|
-
page:
|
|
5020
|
-
limit:
|
|
5021
|
-
search:
|
|
5022
|
-
type:
|
|
5023
|
-
schedule:
|
|
5024
|
-
status:
|
|
5025
|
-
createdAt:
|
|
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)
|
|
5026
5284
|
});
|
|
5027
5285
|
const { error } = validation.validate(query);
|
|
5028
5286
|
if (error) {
|
|
@@ -5056,7 +5314,7 @@ function useAreaChecklistController() {
|
|
|
5056
5314
|
}
|
|
5057
5315
|
}
|
|
5058
5316
|
async function getAreaChecklistHistoryDetails(req, res, next) {
|
|
5059
|
-
const validation =
|
|
5317
|
+
const validation = import_joi11.default.string().hex().required();
|
|
5060
5318
|
const _id = req.params.id;
|
|
5061
5319
|
const { error, value } = validation.validate(_id);
|
|
5062
5320
|
if (error) {
|
|
@@ -5076,12 +5334,12 @@ function useAreaChecklistController() {
|
|
|
5076
5334
|
}
|
|
5077
5335
|
async function getAreaChecklistUnits(req, res, next) {
|
|
5078
5336
|
const query = { ...req.query, ...req.params };
|
|
5079
|
-
const validation =
|
|
5080
|
-
page:
|
|
5081
|
-
limit:
|
|
5082
|
-
search:
|
|
5083
|
-
serviceType:
|
|
5084
|
-
id:
|
|
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()
|
|
5085
5343
|
});
|
|
5086
5344
|
const { error } = validation.validate(query);
|
|
5087
5345
|
if (error) {
|
|
@@ -5131,21 +5389,21 @@ function useAreaChecklistController() {
|
|
|
5131
5389
|
...decisionValues,
|
|
5132
5390
|
completedBy
|
|
5133
5391
|
};
|
|
5134
|
-
const validation =
|
|
5135
|
-
id:
|
|
5136
|
-
set:
|
|
5137
|
-
unit:
|
|
5138
|
-
decision:
|
|
5139
|
-
approve:
|
|
5140
|
-
reject:
|
|
5141
|
-
remarks:
|
|
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),
|
|
5142
5400
|
/*
|
|
5143
5401
|
* Shape only. WHETHER a photo is required depends on the set's current
|
|
5144
5402
|
* state in the database, which Joi cannot see — see the check below.
|
|
5145
5403
|
* Each entry must still be a real id: a blank string carries no evidence.
|
|
5146
5404
|
*/
|
|
5147
|
-
attachment:
|
|
5148
|
-
completedBy:
|
|
5405
|
+
attachment: import_joi11.default.array().items(import_joi11.default.string().trim().min(1)).optional(),
|
|
5406
|
+
completedBy: import_joi11.default.string().hex().required()
|
|
5149
5407
|
});
|
|
5150
5408
|
const { error } = validation.validate(payload);
|
|
5151
5409
|
if (error) {
|
|
@@ -5182,7 +5440,7 @@ function useAreaChecklistController() {
|
|
|
5182
5440
|
}
|
|
5183
5441
|
}
|
|
5184
5442
|
async function downloadChecklistPdf(req, res, next) {
|
|
5185
|
-
const validation =
|
|
5443
|
+
const validation = import_joi11.default.string().hex().required();
|
|
5186
5444
|
const schedule = req.params.schedule;
|
|
5187
5445
|
const { error, value } = validation.validate(schedule);
|
|
5188
5446
|
if (error) {
|
|
@@ -5228,16 +5486,16 @@ function useAreaChecklistController() {
|
|
|
5228
5486
|
}
|
|
5229
5487
|
|
|
5230
5488
|
// src/models/hygiene-supply.model.ts
|
|
5231
|
-
var
|
|
5489
|
+
var import_joi12 = __toESM(require("joi"));
|
|
5232
5490
|
var import_mongodb12 = require("mongodb");
|
|
5233
5491
|
var import_node_server_utils21 = require("@7365admin1/node-server-utils");
|
|
5234
5492
|
var import_core12 = require("@7365admin1/core");
|
|
5235
|
-
var supplySchema =
|
|
5236
|
-
site:
|
|
5237
|
-
serviceType:
|
|
5238
|
-
name:
|
|
5239
|
-
unitOfMeasurement:
|
|
5240
|
-
attachment:
|
|
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()
|
|
5241
5499
|
});
|
|
5242
5500
|
function MSupply(value) {
|
|
5243
5501
|
const { error } = supplySchema.validate(value);
|
|
@@ -5527,7 +5785,7 @@ function useSupplyRepository() {
|
|
|
5527
5785
|
}
|
|
5528
5786
|
|
|
5529
5787
|
// src/controllers/hygiene-supply.controller.ts
|
|
5530
|
-
var
|
|
5788
|
+
var import_joi13 = __toESM(require("joi"));
|
|
5531
5789
|
var import_node_server_utils23 = require("@7365admin1/node-server-utils");
|
|
5532
5790
|
var import_core13 = require("@7365admin1/core");
|
|
5533
5791
|
function useSupplyController() {
|
|
@@ -5558,12 +5816,12 @@ function useSupplyController() {
|
|
|
5558
5816
|
}
|
|
5559
5817
|
async function getSupplies(req, res, next) {
|
|
5560
5818
|
const query = { ...req.query, ...req.params };
|
|
5561
|
-
const validation =
|
|
5562
|
-
page:
|
|
5563
|
-
limit:
|
|
5564
|
-
search:
|
|
5565
|
-
site:
|
|
5566
|
-
serviceType:
|
|
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()
|
|
5567
5825
|
});
|
|
5568
5826
|
const { error } = validation.validate(query);
|
|
5569
5827
|
if (error) {
|
|
@@ -5593,7 +5851,7 @@ function useSupplyController() {
|
|
|
5593
5851
|
}
|
|
5594
5852
|
}
|
|
5595
5853
|
async function getSupplyById(req, res, next) {
|
|
5596
|
-
const validation =
|
|
5854
|
+
const validation = import_joi13.default.string().hex().required();
|
|
5597
5855
|
const _id = req.params.id;
|
|
5598
5856
|
const { error, value } = validation.validate(_id);
|
|
5599
5857
|
if (error) {
|
|
@@ -5613,12 +5871,12 @@ function useSupplyController() {
|
|
|
5613
5871
|
}
|
|
5614
5872
|
async function updateSupply(req, res, next) {
|
|
5615
5873
|
const payload = { id: req.params.id, ...req.body };
|
|
5616
|
-
const validation =
|
|
5617
|
-
id:
|
|
5618
|
-
name:
|
|
5619
|
-
unitOfMeasurement:
|
|
5620
|
-
qty:
|
|
5621
|
-
attachment:
|
|
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)
|
|
5622
5880
|
});
|
|
5623
5881
|
const { error } = validation.validate(payload);
|
|
5624
5882
|
if (error) {
|
|
@@ -5639,7 +5897,7 @@ function useSupplyController() {
|
|
|
5639
5897
|
}
|
|
5640
5898
|
async function deleteSupply(req, res, next) {
|
|
5641
5899
|
const id = req.params.id;
|
|
5642
|
-
const validation =
|
|
5900
|
+
const validation = import_joi13.default.string().hex().required();
|
|
5643
5901
|
const { error, value } = validation.validate(id);
|
|
5644
5902
|
if (error) {
|
|
5645
5903
|
import_node_server_utils23.logger.log({ level: "error", message: error.message });
|
|
@@ -5666,18 +5924,18 @@ function useSupplyController() {
|
|
|
5666
5924
|
}
|
|
5667
5925
|
|
|
5668
5926
|
// src/models/hygiene-stock.model.ts
|
|
5669
|
-
var
|
|
5927
|
+
var import_joi14 = __toESM(require("joi"));
|
|
5670
5928
|
var import_mongodb14 = require("mongodb");
|
|
5671
5929
|
var import_node_server_utils24 = require("@7365admin1/node-server-utils");
|
|
5672
5930
|
var import_core14 = require("@7365admin1/core");
|
|
5673
|
-
var stockSchema =
|
|
5674
|
-
site:
|
|
5675
|
-
serviceType:
|
|
5676
|
-
supply:
|
|
5677
|
-
in:
|
|
5678
|
-
out:
|
|
5679
|
-
balance:
|
|
5680
|
-
remarks:
|
|
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)
|
|
5681
5939
|
});
|
|
5682
5940
|
function MStock(value) {
|
|
5683
5941
|
const { error } = stockSchema.validate(value);
|
|
@@ -5892,7 +6150,7 @@ function useStockService() {
|
|
|
5892
6150
|
}
|
|
5893
6151
|
|
|
5894
6152
|
// src/controllers/hygiene-stock.controller.ts
|
|
5895
|
-
var
|
|
6153
|
+
var import_joi15 = __toESM(require("joi"));
|
|
5896
6154
|
var import_node_server_utils27 = require("@7365admin1/node-server-utils");
|
|
5897
6155
|
var import_core15 = require("@7365admin1/core");
|
|
5898
6156
|
function useStockController() {
|
|
@@ -5900,12 +6158,12 @@ function useStockController() {
|
|
|
5900
6158
|
const { createStock: _createStock } = useStockService();
|
|
5901
6159
|
async function createStock(req, res, next) {
|
|
5902
6160
|
const payload = { ...req.body, ...req.params };
|
|
5903
|
-
const validation =
|
|
5904
|
-
site:
|
|
5905
|
-
serviceType:
|
|
5906
|
-
supply:
|
|
5907
|
-
qty:
|
|
5908
|
-
remarks:
|
|
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)
|
|
5909
6167
|
});
|
|
5910
6168
|
const { error } = validation.validate(payload);
|
|
5911
6169
|
if (error) {
|
|
@@ -5925,13 +6183,13 @@ function useStockController() {
|
|
|
5925
6183
|
}
|
|
5926
6184
|
async function getStocksBySupplyId(req, res, next) {
|
|
5927
6185
|
const query = { ...req.query, ...req.params };
|
|
5928
|
-
const validation =
|
|
5929
|
-
page:
|
|
5930
|
-
limit:
|
|
5931
|
-
search:
|
|
5932
|
-
site:
|
|
5933
|
-
serviceType:
|
|
5934
|
-
supply:
|
|
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()
|
|
5935
6193
|
});
|
|
5936
6194
|
const { error } = validation.validate(query);
|
|
5937
6195
|
if (error) {
|
|
@@ -5993,21 +6251,21 @@ async function applyCheckOutDecision(decision, decide, returnStock) {
|
|
|
5993
6251
|
}
|
|
5994
6252
|
|
|
5995
6253
|
// src/models/hygiene-checkout-item.model.ts
|
|
5996
|
-
var
|
|
6254
|
+
var import_joi16 = __toESM(require("joi"));
|
|
5997
6255
|
var import_mongodb16 = require("mongodb");
|
|
5998
6256
|
var import_node_server_utils28 = require("@7365admin1/node-server-utils");
|
|
5999
6257
|
var import_core16 = require("@7365admin1/core");
|
|
6000
6258
|
var allowedCheckOutItemStatus = ["pending", "completed"];
|
|
6001
|
-
var checkOutItemSchema =
|
|
6002
|
-
site:
|
|
6003
|
-
serviceType:
|
|
6004
|
-
supply:
|
|
6005
|
-
supplyName:
|
|
6006
|
-
supplyQty:
|
|
6007
|
-
qty:
|
|
6008
|
-
attachment:
|
|
6009
|
-
createdBy:
|
|
6010
|
-
createdByName:
|
|
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()
|
|
6011
6269
|
});
|
|
6012
6270
|
function MCheckOutItem(value) {
|
|
6013
6271
|
const { error } = checkOutItemSchema.validate(value);
|
|
@@ -6487,7 +6745,7 @@ function useCheckOutItemService() {
|
|
|
6487
6745
|
}
|
|
6488
6746
|
|
|
6489
6747
|
// src/controllers/hygiene-checkout-item.controller.ts
|
|
6490
|
-
var
|
|
6748
|
+
var import_joi17 = __toESM(require("joi"));
|
|
6491
6749
|
var import_node_server_utils31 = require("@7365admin1/node-server-utils");
|
|
6492
6750
|
var import_core18 = require("@7365admin1/core");
|
|
6493
6751
|
function useCheckOutItemController() {
|
|
@@ -6511,13 +6769,13 @@ function useCheckOutItemController() {
|
|
|
6511
6769
|
...req.params,
|
|
6512
6770
|
createdBy
|
|
6513
6771
|
};
|
|
6514
|
-
const validation =
|
|
6515
|
-
site:
|
|
6516
|
-
serviceType:
|
|
6517
|
-
supply:
|
|
6518
|
-
qty:
|
|
6519
|
-
attachment:
|
|
6520
|
-
createdBy:
|
|
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()
|
|
6521
6779
|
});
|
|
6522
6780
|
const { error } = validation.validate(payload);
|
|
6523
6781
|
if (error) {
|
|
@@ -6546,15 +6804,15 @@ function useCheckOutItemController() {
|
|
|
6546
6804
|
...req.params,
|
|
6547
6805
|
createdBy
|
|
6548
6806
|
};
|
|
6549
|
-
const validation =
|
|
6550
|
-
site:
|
|
6551
|
-
serviceType:
|
|
6552
|
-
createdBy:
|
|
6553
|
-
items:
|
|
6554
|
-
|
|
6555
|
-
supply:
|
|
6556
|
-
qty:
|
|
6557
|
-
attachment:
|
|
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)
|
|
6558
6816
|
})
|
|
6559
6817
|
).min(1).required()
|
|
6560
6818
|
});
|
|
@@ -6576,12 +6834,12 @@ function useCheckOutItemController() {
|
|
|
6576
6834
|
}
|
|
6577
6835
|
async function getCheckOutItems(req, res, next) {
|
|
6578
6836
|
const query = { ...req.query, ...req.params };
|
|
6579
|
-
const validation =
|
|
6580
|
-
page:
|
|
6581
|
-
limit:
|
|
6582
|
-
search:
|
|
6583
|
-
site:
|
|
6584
|
-
serviceType:
|
|
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()
|
|
6585
6843
|
});
|
|
6586
6844
|
const { error } = validation.validate(query);
|
|
6587
6845
|
if (error) {
|
|
@@ -6611,7 +6869,7 @@ function useCheckOutItemController() {
|
|
|
6611
6869
|
}
|
|
6612
6870
|
}
|
|
6613
6871
|
async function getCheckOutItemById(req, res, next) {
|
|
6614
|
-
const validation =
|
|
6872
|
+
const validation = import_joi17.default.string().hex().required();
|
|
6615
6873
|
const _id = req.params.id;
|
|
6616
6874
|
const { error, value } = validation.validate(_id);
|
|
6617
6875
|
if (error) {
|
|
@@ -6650,13 +6908,13 @@ function useCheckOutItemController() {
|
|
|
6650
6908
|
...decisionValues,
|
|
6651
6909
|
completedBy
|
|
6652
6910
|
};
|
|
6653
|
-
const validation =
|
|
6654
|
-
id:
|
|
6655
|
-
decision:
|
|
6656
|
-
approve:
|
|
6657
|
-
reject:
|
|
6658
|
-
remarks:
|
|
6659
|
-
completedBy:
|
|
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()
|
|
6660
6918
|
});
|
|
6661
6919
|
const { error } = validation.validate(payload);
|
|
6662
6920
|
if (error) {
|
|
@@ -6689,23 +6947,15 @@ function useCheckOutItemController() {
|
|
|
6689
6947
|
// src/models/hygiene-schedule-task.model.ts
|
|
6690
6948
|
var import_node_server_utils32 = require("@7365admin1/node-server-utils");
|
|
6691
6949
|
var import_core19 = require("@7365admin1/core");
|
|
6692
|
-
var
|
|
6950
|
+
var import_joi18 = __toESM(require("joi"));
|
|
6693
6951
|
var import_mongodb18 = require("mongodb");
|
|
6694
|
-
var scheduleTaskSchema =
|
|
6695
|
-
site:
|
|
6696
|
-
serviceType:
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
).required(),
|
|
6702
|
-
description: import_joi17.default.string().optional().allow("", null),
|
|
6703
|
-
areas: import_joi17.default.array().min(1).items(
|
|
6704
|
-
import_joi17.default.object({
|
|
6705
|
-
name: import_joi17.default.string().required(),
|
|
6706
|
-
value: import_joi17.default.any().required()
|
|
6707
|
-
})
|
|
6708
|
-
).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;
|
|
6709
6959
|
});
|
|
6710
6960
|
function MScheduleTask(value) {
|
|
6711
6961
|
const { error } = scheduleTaskSchema.validate(value);
|
|
@@ -6732,14 +6982,26 @@ function MScheduleTask(value) {
|
|
|
6732
6982
|
}
|
|
6733
6983
|
});
|
|
6734
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];
|
|
6735
6996
|
return {
|
|
6736
6997
|
site: value.site,
|
|
6737
6998
|
serviceType: value.serviceType,
|
|
6738
6999
|
title: value.title,
|
|
6739
7000
|
time: value.time,
|
|
6740
|
-
dates: value.dates,
|
|
7001
|
+
dates: value.dates ?? [],
|
|
6741
7002
|
description: value.description,
|
|
6742
7003
|
areas: value.areas,
|
|
7004
|
+
...extra,
|
|
6743
7005
|
status: "active",
|
|
6744
7006
|
createdAt: /* @__PURE__ */ new Date(),
|
|
6745
7007
|
updatedAt: "",
|
|
@@ -6750,11 +7012,23 @@ function MScheduleTask(value) {
|
|
|
6750
7012
|
// src/repositories/hygiene-schedule-task.repository.ts
|
|
6751
7013
|
var import_mongodb19 = require("mongodb");
|
|
6752
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
|
+
};
|
|
6753
7026
|
function useScheduleTaskRepository() {
|
|
6754
7027
|
const db = import_node_server_utils33.useAtlas.getDb();
|
|
6755
7028
|
if (!db) {
|
|
6756
7029
|
throw new import_node_server_utils33.InternalServerError("Unable to connect to server.");
|
|
6757
7030
|
}
|
|
7031
|
+
const database = db;
|
|
6758
7032
|
const namespace_collection = "site.schedule-tasks";
|
|
6759
7033
|
const collection = db.collection(namespace_collection);
|
|
6760
7034
|
const { delNamespace, setCache, getCache } = (0, import_node_server_utils33.useCache)(namespace_collection);
|
|
@@ -6829,7 +7103,7 @@ function useScheduleTaskRepository() {
|
|
|
6829
7103
|
const cachedData = await getCache(cacheKey);
|
|
6830
7104
|
if (cachedData) {
|
|
6831
7105
|
import_node_server_utils33.logger.info(`Cache hit for key: ${cacheKey}`);
|
|
6832
|
-
return cachedData;
|
|
7106
|
+
return enrichList(cachedData, site, serviceType);
|
|
6833
7107
|
}
|
|
6834
7108
|
try {
|
|
6835
7109
|
const items = await collection.aggregate([
|
|
@@ -6838,7 +7112,8 @@ function useScheduleTaskRepository() {
|
|
|
6838
7112
|
$project: {
|
|
6839
7113
|
title: 1,
|
|
6840
7114
|
areas: 1,
|
|
6841
|
-
status: 1
|
|
7115
|
+
status: 1,
|
|
7116
|
+
...SCHEDULE_FIELDS
|
|
6842
7117
|
}
|
|
6843
7118
|
},
|
|
6844
7119
|
{ $sort: { _id: -1 } },
|
|
@@ -6852,15 +7127,104 @@ function useScheduleTaskRepository() {
|
|
|
6852
7127
|
}).catch((err) => {
|
|
6853
7128
|
import_node_server_utils33.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
6854
7129
|
});
|
|
6855
|
-
return data;
|
|
7130
|
+
return enrichList(data, site, serviceType);
|
|
6856
7131
|
} catch (error) {
|
|
6857
7132
|
throw error;
|
|
6858
7133
|
}
|
|
6859
7134
|
}
|
|
6860
|
-
async function
|
|
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) {
|
|
6861
7217
|
const query = {
|
|
6862
7218
|
status: { $ne: "deleted" }
|
|
6863
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
|
+
}
|
|
6864
7228
|
try {
|
|
6865
7229
|
const items = await collection.aggregate([{ $match: query }, { $sort: { _id: -1 } }]).toArray();
|
|
6866
7230
|
return items;
|
|
@@ -6868,6 +7232,9 @@ function useScheduleTaskRepository() {
|
|
|
6868
7232
|
throw error;
|
|
6869
7233
|
}
|
|
6870
7234
|
}
|
|
7235
|
+
function withScheduleDisplay(row) {
|
|
7236
|
+
return { ...row, ...scheduleDisplay(row, /* @__PURE__ */ new Date()) };
|
|
7237
|
+
}
|
|
6871
7238
|
async function getScheduleTaskById(_id, session) {
|
|
6872
7239
|
try {
|
|
6873
7240
|
_id = new import_mongodb19.ObjectId(_id);
|
|
@@ -6885,7 +7252,7 @@ function useScheduleTaskRepository() {
|
|
|
6885
7252
|
const cachedData = await getCache(cacheKey);
|
|
6886
7253
|
if (cachedData) {
|
|
6887
7254
|
import_node_server_utils33.logger.info(`Cache hit for key: ${cacheKey}`);
|
|
6888
|
-
return cachedData;
|
|
7255
|
+
return withScheduleDisplay(cachedData);
|
|
6889
7256
|
}
|
|
6890
7257
|
} else {
|
|
6891
7258
|
import_node_server_utils33.logger.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
@@ -6896,12 +7263,13 @@ function useScheduleTaskRepository() {
|
|
|
6896
7263
|
{
|
|
6897
7264
|
$project: {
|
|
6898
7265
|
title: 1,
|
|
6899
|
-
time: 1,
|
|
6900
|
-
dates: 1,
|
|
6901
7266
|
description: 1,
|
|
6902
7267
|
areas: 1,
|
|
6903
7268
|
status: 1,
|
|
6904
|
-
createdAt: 1
|
|
7269
|
+
createdAt: 1,
|
|
7270
|
+
site: 1,
|
|
7271
|
+
serviceType: 1,
|
|
7272
|
+
...SCHEDULE_FIELDS
|
|
6905
7273
|
}
|
|
6906
7274
|
}
|
|
6907
7275
|
]).toArray();
|
|
@@ -6913,7 +7281,7 @@ function useScheduleTaskRepository() {
|
|
|
6913
7281
|
}).catch((err) => {
|
|
6914
7282
|
import_node_server_utils33.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
6915
7283
|
});
|
|
6916
|
-
return data[0];
|
|
7284
|
+
return withScheduleDisplay(data[0]);
|
|
6917
7285
|
} catch (error) {
|
|
6918
7286
|
throw error;
|
|
6919
7287
|
}
|
|
@@ -6937,16 +7305,20 @@ function useScheduleTaskRepository() {
|
|
|
6937
7305
|
});
|
|
6938
7306
|
}
|
|
6939
7307
|
try {
|
|
6940
|
-
const
|
|
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
|
+
}
|
|
6941
7315
|
const res = await collection.updateOne(
|
|
6942
|
-
{ _id },
|
|
6943
|
-
|
|
7316
|
+
{ _id, status: { $ne: "deleted" } },
|
|
7317
|
+
update,
|
|
6944
7318
|
{ session }
|
|
6945
7319
|
);
|
|
6946
|
-
if (res.
|
|
6947
|
-
throw new import_node_server_utils33.
|
|
6948
|
-
"Unable to update hygiene schedule task."
|
|
6949
|
-
);
|
|
7320
|
+
if (res.matchedCount === 0) {
|
|
7321
|
+
throw new import_node_server_utils33.NotFoundError("Schedule task not found.");
|
|
6950
7322
|
}
|
|
6951
7323
|
delNamespace().then(() => {
|
|
6952
7324
|
import_node_server_utils33.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
@@ -6961,6 +7333,18 @@ function useScheduleTaskRepository() {
|
|
|
6961
7333
|
throw error;
|
|
6962
7334
|
}
|
|
6963
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
|
+
}
|
|
6964
7348
|
async function deleteScheduleTask(_id, session) {
|
|
6965
7349
|
try {
|
|
6966
7350
|
_id = new import_mongodb19.ObjectId(_id);
|
|
@@ -7000,6 +7384,7 @@ function useScheduleTaskRepository() {
|
|
|
7000
7384
|
createScheduleTask,
|
|
7001
7385
|
getScheduleTasks,
|
|
7002
7386
|
getAllScheduleTask,
|
|
7387
|
+
markScheduleTaskRan,
|
|
7003
7388
|
getScheduleTaskById,
|
|
7004
7389
|
updateScheduleTask,
|
|
7005
7390
|
deleteScheduleTask
|
|
@@ -7011,7 +7396,7 @@ var import_mongodb20 = require("mongodb");
|
|
|
7011
7396
|
var import_node_server_utils34 = require("@7365admin1/node-server-utils");
|
|
7012
7397
|
function useScheduleTaskService() {
|
|
7013
7398
|
const { createParentChecklist } = useParentChecklistRepo();
|
|
7014
|
-
const { getAllScheduleTask } = useScheduleTaskRepository();
|
|
7399
|
+
const { getAllScheduleTask, markScheduleTaskRan } = useScheduleTaskRepository();
|
|
7015
7400
|
const {
|
|
7016
7401
|
createAreaChecklist,
|
|
7017
7402
|
getAreaChecklistByAreaAndSchedule,
|
|
@@ -7020,73 +7405,7 @@ function useScheduleTaskService() {
|
|
|
7020
7405
|
const { getAreaById } = useAreaRepo();
|
|
7021
7406
|
function checkScheduleConditions(schedule, serviceType, currentDate = /* @__PURE__ */ new Date()) {
|
|
7022
7407
|
try {
|
|
7023
|
-
|
|
7024
|
-
const timeString = now.toLocaleTimeString("en-US", {
|
|
7025
|
-
hour: "2-digit",
|
|
7026
|
-
minute: "2-digit",
|
|
7027
|
-
hour12: false,
|
|
7028
|
-
timeZone: "Asia/Singapore"
|
|
7029
|
-
});
|
|
7030
|
-
const [currentHour, currentMinute] = timeString.split(":").map(Number);
|
|
7031
|
-
import_node_server_utils34.logger.info(
|
|
7032
|
-
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
|
|
7033
|
-
);
|
|
7034
|
-
const currentDateFormatted = now.toLocaleDateString("en-CA", {
|
|
7035
|
-
timeZone: "Asia/Singapore"
|
|
7036
|
-
});
|
|
7037
|
-
if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
|
|
7038
|
-
import_node_server_utils34.logger.info(`Schedule ${schedule._id}: No dates configured, skipping`);
|
|
7039
|
-
return false;
|
|
7040
|
-
}
|
|
7041
|
-
if (!schedule.dates.includes(currentDateFormatted)) {
|
|
7042
|
-
import_node_server_utils34.logger.info(
|
|
7043
|
-
`Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
|
|
7044
|
-
", "
|
|
7045
|
-
)}]`
|
|
7046
|
-
);
|
|
7047
|
-
return false;
|
|
7048
|
-
}
|
|
7049
|
-
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
7050
|
-
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
7051
|
-
if (!timeMatches) {
|
|
7052
|
-
import_node_server_utils34.logger.info(
|
|
7053
|
-
`Schedule ${schedule._id}: Time does not match. Current: ${currentHour}:${currentMinute}, Expected: ${scheduleHour}:${scheduleMinute}`
|
|
7054
|
-
);
|
|
7055
|
-
return false;
|
|
7056
|
-
}
|
|
7057
|
-
const relevantTimestamp = schedule.updatedAt || schedule.createdAt;
|
|
7058
|
-
if (relevantTimestamp) {
|
|
7059
|
-
const savedAt = new Date(relevantTimestamp);
|
|
7060
|
-
const savedHour = parseInt(
|
|
7061
|
-
savedAt.toLocaleTimeString("en-US", {
|
|
7062
|
-
hour: "2-digit",
|
|
7063
|
-
hour12: false,
|
|
7064
|
-
timeZone: "Asia/Singapore"
|
|
7065
|
-
}),
|
|
7066
|
-
10
|
|
7067
|
-
);
|
|
7068
|
-
const savedMinute = savedAt.toLocaleString("en-US", {
|
|
7069
|
-
minute: "2-digit",
|
|
7070
|
-
timeZone: "Asia/Singapore"
|
|
7071
|
-
});
|
|
7072
|
-
const savedDateFormatted = savedAt.toLocaleDateString("en-CA", {
|
|
7073
|
-
timeZone: "Asia/Singapore"
|
|
7074
|
-
});
|
|
7075
|
-
const savedMinNum = parseInt(savedMinute, 10);
|
|
7076
|
-
const wasSavedThisMinute = savedDateFormatted === currentDateFormatted && savedHour === currentHour && savedMinNum === currentMinute;
|
|
7077
|
-
if (wasSavedThisMinute) {
|
|
7078
|
-
import_node_server_utils34.logger.info(
|
|
7079
|
-
`Schedule ${schedule._id}: Skipping \u2014 task was saved this minute (${savedHour}:${String(
|
|
7080
|
-
savedMinNum
|
|
7081
|
-
).padStart(2, "0")}). Will fire on next occurrence.`
|
|
7082
|
-
);
|
|
7083
|
-
return false;
|
|
7084
|
-
}
|
|
7085
|
-
}
|
|
7086
|
-
import_node_server_utils34.logger.info(
|
|
7087
|
-
`Schedule ${schedule._id}: All conditions matched - Date is within range and time matches`
|
|
7088
|
-
);
|
|
7089
|
-
return true;
|
|
7408
|
+
return shouldRunNow(schedule, currentDate);
|
|
7090
7409
|
} catch (error) {
|
|
7091
7410
|
import_node_server_utils34.logger.error(
|
|
7092
7411
|
`Error checking schedule conditions for ${schedule._id}:`,
|
|
@@ -7095,18 +7414,22 @@ function useScheduleTaskService() {
|
|
|
7095
7414
|
return false;
|
|
7096
7415
|
}
|
|
7097
7416
|
}
|
|
7417
|
+
const MAX_ATTEMPTS = 5;
|
|
7418
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
7098
7419
|
async function processScheduledTasks(currentDate) {
|
|
7099
7420
|
try {
|
|
7100
7421
|
import_node_server_utils34.logger.info("Starting scheduled task processing...");
|
|
7101
|
-
const
|
|
7422
|
+
const today = sgtParts(currentDate ?? /* @__PURE__ */ new Date()).date;
|
|
7423
|
+
const scheduleTasks = await getAllScheduleTask(today);
|
|
7102
7424
|
if (!scheduleTasks || scheduleTasks.length === 0) {
|
|
7103
7425
|
import_node_server_utils34.logger.info("No schedule tasks found to process");
|
|
7104
|
-
return { processed: 0, validated: 0 };
|
|
7426
|
+
return { processed: 0, validated: 0, fired: [] };
|
|
7105
7427
|
}
|
|
7106
7428
|
import_node_server_utils34.logger.info(`Found ${scheduleTasks.length} schedule tasks to check`);
|
|
7107
7429
|
let processedCount = 0;
|
|
7108
7430
|
let validatedCount = 0;
|
|
7109
7431
|
const validatedTasks = [];
|
|
7432
|
+
const fired = [];
|
|
7110
7433
|
for (const scheduleTask of scheduleTasks) {
|
|
7111
7434
|
try {
|
|
7112
7435
|
const serviceType = scheduleTask.serviceType;
|
|
@@ -7149,6 +7472,8 @@ function useScheduleTaskService() {
|
|
|
7149
7472
|
import_node_server_utils34.logger.info(
|
|
7150
7473
|
`Using parent checklist ${parentChecklistId}, now creating/updating area checklists`
|
|
7151
7474
|
);
|
|
7475
|
+
let failedAreas = 0;
|
|
7476
|
+
const addedAreas = [];
|
|
7152
7477
|
for (const area of scheduleTask.areas) {
|
|
7153
7478
|
try {
|
|
7154
7479
|
const areaId = area.value.toString();
|
|
@@ -7228,6 +7553,7 @@ function useScheduleTaskService() {
|
|
|
7228
7553
|
newSets
|
|
7229
7554
|
);
|
|
7230
7555
|
if (modified > 0) {
|
|
7556
|
+
addedAreas.push(area.name);
|
|
7231
7557
|
import_node_server_utils34.logger.info(
|
|
7232
7558
|
`Appended ${areaSetConfig} set(s) starting from ${startSetNumber} to area checklist for area ${area.name}`
|
|
7233
7559
|
);
|
|
@@ -7260,13 +7586,32 @@ function useScheduleTaskService() {
|
|
|
7260
7586
|
`Area ${area.name} (${areaId}): Creating new area checklist with schedule-task sets only`
|
|
7261
7587
|
);
|
|
7262
7588
|
await createAreaChecklist(checklistData);
|
|
7589
|
+
addedAreas.push(area.name);
|
|
7263
7590
|
import_node_server_utils34.logger.info(`Created new area checklist for area ${area.name}`);
|
|
7264
7591
|
}
|
|
7265
7592
|
} catch (error) {
|
|
7266
7593
|
import_node_server_utils34.logger.error(`Error processing area ${area.name}:`, error);
|
|
7594
|
+
failedAreas++;
|
|
7267
7595
|
continue;
|
|
7268
7596
|
}
|
|
7269
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
|
+
}
|
|
7270
7615
|
processedCount++;
|
|
7271
7616
|
validatedCount++;
|
|
7272
7617
|
validatedTasks.push(scheduleTask);
|
|
@@ -7278,6 +7623,17 @@ function useScheduleTaskService() {
|
|
|
7278
7623
|
`Error processing schedule task ${scheduleTask._id}:`,
|
|
7279
7624
|
error
|
|
7280
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
|
+
}
|
|
7281
7637
|
continue;
|
|
7282
7638
|
}
|
|
7283
7639
|
}
|
|
@@ -7287,7 +7643,8 @@ function useScheduleTaskService() {
|
|
|
7287
7643
|
return {
|
|
7288
7644
|
processed: processedCount,
|
|
7289
7645
|
validated: validatedCount,
|
|
7290
|
-
tasks: validatedTasks
|
|
7646
|
+
tasks: validatedTasks,
|
|
7647
|
+
fired
|
|
7291
7648
|
};
|
|
7292
7649
|
} catch (error) {
|
|
7293
7650
|
import_node_server_utils34.logger.error("Error processing scheduled tasks:", error);
|
|
@@ -7298,7 +7655,7 @@ function useScheduleTaskService() {
|
|
|
7298
7655
|
}
|
|
7299
7656
|
|
|
7300
7657
|
// src/controllers/hygiene-schedule-task.controller.ts
|
|
7301
|
-
var
|
|
7658
|
+
var import_joi19 = __toESM(require("joi"));
|
|
7302
7659
|
var import_node_server_utils35 = require("@7365admin1/node-server-utils");
|
|
7303
7660
|
var import_core20 = require("@7365admin1/core");
|
|
7304
7661
|
function useScheduleTaskController() {
|
|
@@ -7329,12 +7686,12 @@ function useScheduleTaskController() {
|
|
|
7329
7686
|
}
|
|
7330
7687
|
async function getScheduleTasks(req, res, next) {
|
|
7331
7688
|
const query = { ...req.query, ...req.params };
|
|
7332
|
-
const validation =
|
|
7333
|
-
page:
|
|
7334
|
-
limit:
|
|
7335
|
-
search:
|
|
7336
|
-
site:
|
|
7337
|
-
serviceType:
|
|
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()
|
|
7338
7695
|
});
|
|
7339
7696
|
const { error } = validation.validate(query);
|
|
7340
7697
|
if (error) {
|
|
@@ -7342,8 +7699,8 @@ function useScheduleTaskController() {
|
|
|
7342
7699
|
next(new import_node_server_utils35.BadRequestError(error.message));
|
|
7343
7700
|
return;
|
|
7344
7701
|
}
|
|
7345
|
-
const page = parseInt(req.query.page)
|
|
7346
|
-
const limit = parseInt(req.query.limit)
|
|
7702
|
+
const page = parseInt(req.query.page) || 1;
|
|
7703
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
7347
7704
|
const search = req.query.search ?? "";
|
|
7348
7705
|
const site = req.params.site ?? "";
|
|
7349
7706
|
const serviceType = req.query.serviceType ?? "";
|
|
@@ -7364,7 +7721,7 @@ function useScheduleTaskController() {
|
|
|
7364
7721
|
}
|
|
7365
7722
|
}
|
|
7366
7723
|
async function getScheduleTaskById(req, res, next) {
|
|
7367
|
-
const validation =
|
|
7724
|
+
const validation = import_joi19.default.string().hex().required();
|
|
7368
7725
|
const _id = req.params.id;
|
|
7369
7726
|
const { error, value } = validation.validate(_id);
|
|
7370
7727
|
if (error) {
|
|
@@ -7384,20 +7741,17 @@ function useScheduleTaskController() {
|
|
|
7384
7741
|
}
|
|
7385
7742
|
async function updateScheduleTask(req, res, next) {
|
|
7386
7743
|
const payload = { id: req.params.id, ...req.body };
|
|
7387
|
-
const
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
)
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
value: import_joi18.default.any().required()
|
|
7399
|
-
})
|
|
7400
|
-
).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
|
|
7401
7755
|
});
|
|
7402
7756
|
const { error } = validation.validate(payload);
|
|
7403
7757
|
if (error) {
|
|
@@ -7405,6 +7759,24 @@ function useScheduleTaskController() {
|
|
|
7405
7759
|
next(new import_node_server_utils35.BadRequestError(error.message));
|
|
7406
7760
|
return;
|
|
7407
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
|
+
}
|
|
7408
7780
|
try {
|
|
7409
7781
|
const { id, ...value } = payload;
|
|
7410
7782
|
await _updateScheduleTask(id, value);
|
|
@@ -7417,7 +7789,7 @@ function useScheduleTaskController() {
|
|
|
7417
7789
|
}
|
|
7418
7790
|
}
|
|
7419
7791
|
async function deleteScheduleTask(req, res, next) {
|
|
7420
|
-
const validation =
|
|
7792
|
+
const validation = import_joi19.default.string().hex().required();
|
|
7421
7793
|
const _id = req.params.id;
|
|
7422
7794
|
const { error, value } = validation.validate(_id);
|
|
7423
7795
|
if (error) {
|
|
@@ -7661,17 +8033,17 @@ function useQRService() {
|
|
|
7661
8033
|
}
|
|
7662
8034
|
|
|
7663
8035
|
// src/controllers/hygiene-qr.controller.ts
|
|
7664
|
-
var
|
|
8036
|
+
var import_joi20 = __toESM(require("joi"));
|
|
7665
8037
|
var import_node_server_utils37 = require("@7365admin1/node-server-utils");
|
|
7666
8038
|
function useQRController() {
|
|
7667
8039
|
const { generateQRImage: _generateQRImage, generateQRPDF: _generateQRPDF } = useQRService();
|
|
7668
8040
|
async function generateQR(req, res, next) {
|
|
7669
|
-
const validation =
|
|
7670
|
-
url:
|
|
7671
|
-
filename:
|
|
7672
|
-
title:
|
|
7673
|
-
subtitle:
|
|
7674
|
-
download:
|
|
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)
|
|
7675
8047
|
});
|
|
7676
8048
|
const query = { ...req.query };
|
|
7677
8049
|
const { error, value } = validation.validate(query);
|