@7365admin1/module-hygiene 4.29.1-staging.16 → 4.29.1-staging.18
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/extra-visits-fixes.md +5 -0
- package/.changeset/schedule-task-set-fields.md +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +82 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -30
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/schedule-followups.test.mjs +30 -0
- package/test/schedule-task-rules.test.mjs +13 -0
package/dist/index.mjs
CHANGED
|
@@ -2968,6 +2968,18 @@ function useParentChecklistRepo() {
|
|
|
2968
2968
|
throw error;
|
|
2969
2969
|
}
|
|
2970
2970
|
}
|
|
2971
|
+
async function reopenParentChecklist(_id) {
|
|
2972
|
+
const res = await collection.updateOne(
|
|
2973
|
+
{ _id: new ObjectId7(_id), status: "completed" },
|
|
2974
|
+
{ $set: { status: "ongoing", updatedAt: /* @__PURE__ */ new Date() }, $unset: { completedAt: "" } }
|
|
2975
|
+
);
|
|
2976
|
+
if (res.modifiedCount > 0) {
|
|
2977
|
+
delNamespace().catch((err) => {
|
|
2978
|
+
logger14.error(`Failed to clear cache for namespace: ${namespace_collection}`, err);
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
return res.modifiedCount;
|
|
2982
|
+
}
|
|
2971
2983
|
async function getHygieneSiteIds() {
|
|
2972
2984
|
const query = {
|
|
2973
2985
|
status: { $ne: "deleted" }
|
|
@@ -3074,6 +3086,7 @@ function useParentChecklistRepo() {
|
|
|
3074
3086
|
getAllParentChecklist,
|
|
3075
3087
|
getParentChecklistById,
|
|
3076
3088
|
updateParentChecklistStatuses,
|
|
3089
|
+
reopenParentChecklist,
|
|
3077
3090
|
getHygieneSiteIds,
|
|
3078
3091
|
closeExpiredParentChecklists,
|
|
3079
3092
|
getTodayParentChecklists,
|
|
@@ -3186,7 +3199,9 @@ var areaChecklistSchema = Joi9.object({
|
|
|
3186
3199
|
}).required()
|
|
3187
3200
|
).min(1).required(),
|
|
3188
3201
|
isScheduleTask: Joi9.boolean().optional().default(false),
|
|
3189
|
-
scheduleTaskId: Joi9.string().hex().optional().allow(null)
|
|
3202
|
+
scheduleTaskId: Joi9.string().hex().optional().allow(null),
|
|
3203
|
+
scheduleTaskTitle: Joi9.string().optional().allow("", null),
|
|
3204
|
+
scheduleTaskTime: Joi9.string().optional().allow("", null)
|
|
3190
3205
|
}).required()
|
|
3191
3206
|
).optional().default([])
|
|
3192
3207
|
});
|
|
@@ -3216,6 +3231,8 @@ function MAreaChecklist(value) {
|
|
|
3216
3231
|
set: checklistItem.set,
|
|
3217
3232
|
isScheduleTask: checklistItem.isScheduleTask ?? false,
|
|
3218
3233
|
scheduleTaskId: checklistItem.scheduleTaskId ?? void 0,
|
|
3234
|
+
scheduleTaskTitle: checklistItem.scheduleTaskTitle ?? void 0,
|
|
3235
|
+
scheduleTaskTime: checklistItem.scheduleTaskTime ?? void 0,
|
|
3219
3236
|
units: checklistItem.units.map((unit) => ({
|
|
3220
3237
|
unit: new ObjectId8(unit.unit),
|
|
3221
3238
|
name: unit.name,
|
|
@@ -3710,6 +3727,9 @@ function useAreaChecklistRepo() {
|
|
|
3710
3727
|
isScheduleTask: {
|
|
3711
3728
|
$ifNull: ["$checklist.isScheduleTask", false]
|
|
3712
3729
|
},
|
|
3730
|
+
scheduleTaskId: { $ifNull: ["$checklist.scheduleTaskId", null] },
|
|
3731
|
+
scheduleTaskTitle: { $ifNull: ["$checklist.scheduleTaskTitle", null] },
|
|
3732
|
+
scheduleTaskTime: { $ifNull: ["$checklist.scheduleTaskTime", null] },
|
|
3713
3733
|
unit: "$checklist.units.unit",
|
|
3714
3734
|
name: "$checklist.units.name",
|
|
3715
3735
|
approve: "$checklist.units.approve",
|
|
@@ -3746,6 +3766,9 @@ function useAreaChecklistRepo() {
|
|
|
3746
3766
|
$group: {
|
|
3747
3767
|
_id: "$set",
|
|
3748
3768
|
isScheduleTask: { $first: "$isScheduleTask" },
|
|
3769
|
+
scheduleTaskId: { $first: "$scheduleTaskId" },
|
|
3770
|
+
scheduleTaskTitle: { $first: "$scheduleTaskTitle" },
|
|
3771
|
+
scheduleTaskTime: { $first: "$scheduleTaskTime" },
|
|
3749
3772
|
units: {
|
|
3750
3773
|
$push: {
|
|
3751
3774
|
unit: "$unit",
|
|
@@ -3766,6 +3789,9 @@ function useAreaChecklistRepo() {
|
|
|
3766
3789
|
_id: 0,
|
|
3767
3790
|
set: "$_id",
|
|
3768
3791
|
isScheduleTask: 1,
|
|
3792
|
+
scheduleTaskId: 1,
|
|
3793
|
+
scheduleTaskTitle: 1,
|
|
3794
|
+
scheduleTaskTime: 1,
|
|
3769
3795
|
units: 1
|
|
3770
3796
|
}
|
|
3771
3797
|
},
|
|
@@ -3897,6 +3923,8 @@ function useAreaChecklistRepo() {
|
|
|
3897
3923
|
isScheduleTask: {
|
|
3898
3924
|
$ifNull: ["$checklist.isScheduleTask", false]
|
|
3899
3925
|
},
|
|
3926
|
+
scheduleTaskTitle: { $ifNull: ["$checklist.scheduleTaskTitle", null] },
|
|
3927
|
+
scheduleTaskTime: { $ifNull: ["$checklist.scheduleTaskTime", null] },
|
|
3900
3928
|
scheduleTaskId: {
|
|
3901
3929
|
$ifNull: ["$checklist.scheduleTaskId", null]
|
|
3902
3930
|
},
|
|
@@ -3937,6 +3965,8 @@ function useAreaChecklistRepo() {
|
|
|
3937
3965
|
$group: {
|
|
3938
3966
|
_id: "$set",
|
|
3939
3967
|
isScheduleTask: { $first: "$isScheduleTask" },
|
|
3968
|
+
scheduleTaskTitle: { $first: "$scheduleTaskTitle" },
|
|
3969
|
+
scheduleTaskTime: { $first: "$scheduleTaskTime" },
|
|
3940
3970
|
scheduleTaskId: { $first: "$scheduleTaskId" },
|
|
3941
3971
|
remarks: { $first: "$remarks" },
|
|
3942
3972
|
attachment: { $first: "$attachment" },
|
|
@@ -3961,6 +3991,8 @@ function useAreaChecklistRepo() {
|
|
|
3961
3991
|
_id: 0,
|
|
3962
3992
|
set: "$_id",
|
|
3963
3993
|
isScheduleTask: 1,
|
|
3994
|
+
scheduleTaskTitle: 1,
|
|
3995
|
+
scheduleTaskTime: 1,
|
|
3964
3996
|
scheduleTaskId: 1,
|
|
3965
3997
|
remarks: "$remarks",
|
|
3966
3998
|
attachment: "$attachment",
|
|
@@ -4283,6 +4315,10 @@ function useAreaChecklistRepo() {
|
|
|
4283
4315
|
}
|
|
4284
4316
|
);
|
|
4285
4317
|
if (result.modifiedCount > 0) {
|
|
4318
|
+
await collection.updateOne(
|
|
4319
|
+
{ schedule, serviceType, area, status: "completed" },
|
|
4320
|
+
{ $set: { status: "ongoing", updatedAt: /* @__PURE__ */ new Date() }, $unset: { completedAt: "" } }
|
|
4321
|
+
);
|
|
4286
4322
|
delNamespace().catch((err) => {
|
|
4287
4323
|
logger17.error(
|
|
4288
4324
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
@@ -4579,6 +4615,9 @@ function validateScheduleRules(task, now = /* @__PURE__ */ new Date()) {
|
|
|
4579
4615
|
}
|
|
4580
4616
|
if (task.endDate && task.endDate < today)
|
|
4581
4617
|
return "The end date has already passed.";
|
|
4618
|
+
if (!repeating && !(task.dates ?? []).length) {
|
|
4619
|
+
return "Pick at least one date, or choose a repeat.";
|
|
4620
|
+
}
|
|
4582
4621
|
if (!repeating) {
|
|
4583
4622
|
const dates = (task.dates ?? []).filter((d) => !task.endDate || d <= task.endDate);
|
|
4584
4623
|
if (dates.length && dates.every((d) => d < today)) {
|
|
@@ -4945,6 +4984,8 @@ import { AppServiceType as AppServiceType10 } from "@7365admin1/core";
|
|
|
4945
4984
|
import { launch } from "puppeteer";
|
|
4946
4985
|
import { InternalServerError as InternalServerError6, useAtlas as useAtlas8 } from "@7365admin1/node-server-utils";
|
|
4947
4986
|
import { ObjectId as ObjectId11 } from "mongodb";
|
|
4987
|
+
|
|
4988
|
+
// src/utils/checklist-pdf-html.util.ts
|
|
4948
4989
|
function escapeHtml(text) {
|
|
4949
4990
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4950
4991
|
}
|
|
@@ -4957,7 +4998,7 @@ function buildChecklistHtml(areaChecklists) {
|
|
|
4957
4998
|
const unitRows = (setItem.units || []).map(
|
|
4958
4999
|
(unit) => `
|
|
4959
5000
|
<tr>
|
|
4960
|
-
<td class="unit-name">${escapeHtml(unit.name || "")}</td>
|
|
5001
|
+
<td class="unit-name">${escapeHtml(unit.name || "")}${unit.approve || unit.reject ? "" : ' <span class="missed-label">Missed</span>'}</td>
|
|
4961
5002
|
<td class="icon-cell">${unit.reject ? '<span class="reject-icon">✗</span>' : ""}</td>
|
|
4962
5003
|
<td class="icon-cell">${unit.approve ? '<span class="approve-icon">✓</span>' : ""}</td>
|
|
4963
5004
|
</tr>`
|
|
@@ -5094,6 +5135,12 @@ function buildChecklistHtml(areaChecklists) {
|
|
|
5094
5135
|
font-size: 16px;
|
|
5095
5136
|
font-weight: bold;
|
|
5096
5137
|
}
|
|
5138
|
+
.missed-label {
|
|
5139
|
+
color: #b3261e;
|
|
5140
|
+
font-size: 11px;
|
|
5141
|
+
font-weight: bold;
|
|
5142
|
+
margin-left: 6px;
|
|
5143
|
+
}
|
|
5097
5144
|
.reject-icon {
|
|
5098
5145
|
color: #e74c3c;
|
|
5099
5146
|
font-size: 16px;
|
|
@@ -5109,6 +5156,8 @@ function buildChecklistHtml(areaChecklists) {
|
|
|
5109
5156
|
</body>
|
|
5110
5157
|
</html>`;
|
|
5111
5158
|
}
|
|
5159
|
+
|
|
5160
|
+
// src/services/hygiene-checklist-pdf.service.ts
|
|
5112
5161
|
function useChecklistPdfService() {
|
|
5113
5162
|
async function generateChecklistPdf(scheduleId) {
|
|
5114
5163
|
const db = useAtlas8.getDb();
|
|
@@ -7029,7 +7078,11 @@ var SCHEDULE_FIELDS = {
|
|
|
7029
7078
|
startDate: 1,
|
|
7030
7079
|
endDate: 1,
|
|
7031
7080
|
paused: 1,
|
|
7032
|
-
lastRunDate: 1
|
|
7081
|
+
lastRunDate: 1,
|
|
7082
|
+
// when it was saved: a task saved after its time today waits for its next
|
|
7083
|
+
// day, so Next visit must know it (else it says "today" for a visit that never comes)
|
|
7084
|
+
createdAt: 1,
|
|
7085
|
+
updatedAt: 1
|
|
7033
7086
|
};
|
|
7034
7087
|
function useScheduleTaskRepository() {
|
|
7035
7088
|
const db = useAtlas14.getDb();
|
|
@@ -7274,7 +7327,6 @@ function useScheduleTaskRepository() {
|
|
|
7274
7327
|
description: 1,
|
|
7275
7328
|
areas: 1,
|
|
7276
7329
|
status: 1,
|
|
7277
|
-
createdAt: 1,
|
|
7278
7330
|
site: 1,
|
|
7279
7331
|
serviceType: 1,
|
|
7280
7332
|
...SCHEDULE_FIELDS
|
|
@@ -7399,11 +7451,23 @@ function useScheduleTaskRepository() {
|
|
|
7399
7451
|
};
|
|
7400
7452
|
}
|
|
7401
7453
|
|
|
7454
|
+
// src/utils/schedule-task-set.util.ts
|
|
7455
|
+
function buildScheduleTaskSets(task, units, startSet, count = 1) {
|
|
7456
|
+
return Array.from({ length: count }, (_, i) => ({
|
|
7457
|
+
set: startSet + i,
|
|
7458
|
+
units,
|
|
7459
|
+
isScheduleTask: true,
|
|
7460
|
+
scheduleTaskId: task._id,
|
|
7461
|
+
scheduleTaskTitle: task.title,
|
|
7462
|
+
scheduleTaskTime: task.time
|
|
7463
|
+
}));
|
|
7464
|
+
}
|
|
7465
|
+
|
|
7402
7466
|
// src/services/hygiene-schedule-task.service.ts
|
|
7403
7467
|
import { ObjectId as ObjectId20 } from "mongodb";
|
|
7404
7468
|
import { logger as logger32 } from "@7365admin1/node-server-utils";
|
|
7405
7469
|
function useScheduleTaskService() {
|
|
7406
|
-
const { createParentChecklist } = useParentChecklistRepo();
|
|
7470
|
+
const { createParentChecklist, reopenParentChecklist } = useParentChecklistRepo();
|
|
7407
7471
|
const { getAllScheduleTask, markScheduleTaskRan } = useScheduleTaskRepository();
|
|
7408
7472
|
const {
|
|
7409
7473
|
createAreaChecklist,
|
|
@@ -7474,7 +7538,7 @@ function useScheduleTaskService() {
|
|
|
7474
7538
|
const parentChecklistIds = await createParentChecklist({
|
|
7475
7539
|
site: scheduleTask.site.toString(),
|
|
7476
7540
|
serviceType,
|
|
7477
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
7541
|
+
createdAt: currentDate ?? /* @__PURE__ */ new Date()
|
|
7478
7542
|
});
|
|
7479
7543
|
const parentChecklistId = Array.isArray(parentChecklistIds) ? parentChecklistIds[0] : parentChecklistIds;
|
|
7480
7544
|
logger32.info(
|
|
@@ -7537,22 +7601,13 @@ function useScheduleTaskService() {
|
|
|
7537
7601
|
);
|
|
7538
7602
|
}
|
|
7539
7603
|
if (existingAreaChecklist) {
|
|
7540
|
-
const areaSetConfig = Number(areaDetails.set) || 1;
|
|
7541
7604
|
const maxSetInChecklist = existingAreaChecklist.checklist?.length ? Math.max(
|
|
7542
7605
|
...existingAreaChecklist.checklist.map(
|
|
7543
7606
|
(c) => c.set
|
|
7544
7607
|
)
|
|
7545
7608
|
) : 0;
|
|
7546
7609
|
const startSetNumber = maxSetInChecklist + 1;
|
|
7547
|
-
const newSets =
|
|
7548
|
-
{ length: areaSetConfig },
|
|
7549
|
-
(_, index) => ({
|
|
7550
|
-
set: startSetNumber + index,
|
|
7551
|
-
units,
|
|
7552
|
-
isScheduleTask: true,
|
|
7553
|
-
scheduleTaskId: scheduleTask._id
|
|
7554
|
-
})
|
|
7555
|
-
);
|
|
7610
|
+
const newSets = buildScheduleTaskSets(scheduleTask, units, startSetNumber);
|
|
7556
7611
|
const modified = await pushScheduleTaskSets(
|
|
7557
7612
|
parentChecklistId.toString(),
|
|
7558
7613
|
serviceType,
|
|
@@ -7563,7 +7618,7 @@ function useScheduleTaskService() {
|
|
|
7563
7618
|
if (modified > 0) {
|
|
7564
7619
|
addedAreas.push(area.name);
|
|
7565
7620
|
logger32.info(
|
|
7566
|
-
`Appended
|
|
7621
|
+
`Appended extra-visit set ${startSetNumber} to area checklist for area ${area.name}`
|
|
7567
7622
|
);
|
|
7568
7623
|
} else {
|
|
7569
7624
|
logger32.info(
|
|
@@ -7571,17 +7626,7 @@ function useScheduleTaskService() {
|
|
|
7571
7626
|
);
|
|
7572
7627
|
}
|
|
7573
7628
|
} else {
|
|
7574
|
-
const
|
|
7575
|
-
const schedTaskId = scheduleTask._id;
|
|
7576
|
-
const schedTaskSets = Array.from(
|
|
7577
|
-
{ length: areaSetConfig },
|
|
7578
|
-
(_, index) => ({
|
|
7579
|
-
set: index + 1,
|
|
7580
|
-
units,
|
|
7581
|
-
isScheduleTask: true,
|
|
7582
|
-
scheduleTaskId: schedTaskId
|
|
7583
|
-
})
|
|
7584
|
-
);
|
|
7629
|
+
const schedTaskSets = buildScheduleTaskSets(scheduleTask, units, 1);
|
|
7585
7630
|
const checklistData = {
|
|
7586
7631
|
schedule: parentChecklistId.toString(),
|
|
7587
7632
|
serviceType,
|
|
@@ -7603,6 +7648,13 @@ function useScheduleTaskService() {
|
|
|
7603
7648
|
continue;
|
|
7604
7649
|
}
|
|
7605
7650
|
}
|
|
7651
|
+
if (addedAreas.length > 0) {
|
|
7652
|
+
try {
|
|
7653
|
+
await reopenParentChecklist(parentChecklistId);
|
|
7654
|
+
} catch (error) {
|
|
7655
|
+
logger32.error(`Could not reopen day list ${parentChecklistId}`, error);
|
|
7656
|
+
}
|
|
7657
|
+
}
|
|
7606
7658
|
const attemptKey = `${scheduleTask._id}:${today}`;
|
|
7607
7659
|
const tried = (attempts.get(attemptKey) ?? 0) + 1;
|
|
7608
7660
|
attempts.set(attemptKey, tried);
|
|
@@ -7752,8 +7804,8 @@ function useScheduleTaskController() {
|
|
|
7752
7804
|
const optional = Object.fromEntries(
|
|
7753
7805
|
Object.entries(scheduleTaskFields).map(([key, rule]) => [
|
|
7754
7806
|
key,
|
|
7755
|
-
key === "dates" ? Joi19.array().
|
|
7756
|
-
"array.
|
|
7807
|
+
key === "dates" ? Joi19.array().items(scheduleDate).optional().messages({
|
|
7808
|
+
"array.base": "Dates must be a list of dates."
|
|
7757
7809
|
}) : rule.optional()
|
|
7758
7810
|
])
|
|
7759
7811
|
);
|