@7365admin1/module-hygiene 4.30.2 → 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/CHANGELOG.md +16 -0
- package/dist/index.d.ts +40 -10
- package/dist/index.js +753 -370
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +753 -370
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/area-import-unit-id.test.mjs +18 -0
- package/test/escape-regex.util.test.mjs +26 -0
- package/test/schedule-task-rules.test.mjs +185 -0
package/dist/index.js
CHANGED
|
@@ -924,21 +924,30 @@ function useHygieneDashboardController() {
|
|
|
924
924
|
}
|
|
925
925
|
|
|
926
926
|
// src/models/hygiene-area.model.ts
|
|
927
|
-
var
|
|
927
|
+
var import_joi3 = __toESM(require("joi"));
|
|
928
928
|
var import_mongodb2 = require("mongodb");
|
|
929
929
|
var import_node_server_utils3 = require("@7365admin1/node-server-utils");
|
|
930
930
|
var import_core2 = require("@7365admin1/core");
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
931
|
+
|
|
932
|
+
// src/utils/area-unit.util.ts
|
|
933
|
+
var import_joi2 = __toESM(require("joi"));
|
|
934
|
+
var areaUnitSchema = import_joi2.default.object({
|
|
935
|
+
unit: import_joi2.default.string().hex().required(),
|
|
936
|
+
name: import_joi2.default.string().required()
|
|
937
|
+
});
|
|
938
|
+
function toAreaUnit(unit) {
|
|
939
|
+
return { unit: String(unit._id), name: unit.name };
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/models/hygiene-area.model.ts
|
|
943
|
+
var areaSchema = import_joi3.default.object({
|
|
944
|
+
site: import_joi3.default.string().hex().required(),
|
|
945
|
+
serviceType: import_joi3.default.string().valid(...Object.values(import_core2.AppServiceType)).required(),
|
|
946
|
+
name: import_joi3.default.string().required(),
|
|
947
|
+
type: import_joi3.default.string().valid(...allowedTypes).required(),
|
|
948
|
+
set: import_joi3.default.number().min(0).optional(),
|
|
949
|
+
units: import_joi3.default.array().items(
|
|
950
|
+
areaUnitSchema.optional()
|
|
942
951
|
).min(1).unique("unit", { ignoreUndefined: true }).messages({
|
|
943
952
|
"array.unique": "Duplicate area units are not allowed"
|
|
944
953
|
})
|
|
@@ -993,6 +1002,11 @@ function capitalizeText(text) {
|
|
|
993
1002
|
return text.toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
994
1003
|
}
|
|
995
1004
|
|
|
1005
|
+
// src/utils/escape-regex.util.ts
|
|
1006
|
+
function escapeRegex(value) {
|
|
1007
|
+
return String(value ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1008
|
+
}
|
|
1009
|
+
|
|
996
1010
|
// src/repositories/hygiene-area.repository.ts
|
|
997
1011
|
function useAreaRepo() {
|
|
998
1012
|
const db = import_node_server_utils4.useAtlas.getDb();
|
|
@@ -1089,7 +1103,7 @@ function useAreaRepo() {
|
|
|
1089
1103
|
cacheOptions.type = type;
|
|
1090
1104
|
}
|
|
1091
1105
|
if (search) {
|
|
1092
|
-
query.$or = [{ name: { $regex: search, $options: "i" } }];
|
|
1106
|
+
query.$or = [{ name: { $regex: escapeRegex(search), $options: "i" } }];
|
|
1093
1107
|
cacheOptions.search = search;
|
|
1094
1108
|
}
|
|
1095
1109
|
const cacheKey = (0, import_node_server_utils4.makeCacheKey)(namespace_collection, cacheOptions);
|
|
@@ -1431,14 +1445,14 @@ var import_mongodb5 = require("mongodb");
|
|
|
1431
1445
|
var import_node_server_utils7 = require("@7365admin1/node-server-utils");
|
|
1432
1446
|
|
|
1433
1447
|
// src/models/hygiene-unit.model.ts
|
|
1434
|
-
var
|
|
1448
|
+
var import_joi4 = __toESM(require("joi"));
|
|
1435
1449
|
var import_mongodb4 = require("mongodb");
|
|
1436
1450
|
var import_node_server_utils6 = require("@7365admin1/node-server-utils");
|
|
1437
1451
|
var import_core3 = require("@7365admin1/core");
|
|
1438
|
-
var unitSchema =
|
|
1439
|
-
site:
|
|
1440
|
-
serviceType:
|
|
1441
|
-
name:
|
|
1452
|
+
var unitSchema = import_joi4.default.object({
|
|
1453
|
+
site: import_joi4.default.string().hex().required(),
|
|
1454
|
+
serviceType: import_joi4.default.string().valid(...Object.values(import_core3.AppServiceType)).required(),
|
|
1455
|
+
name: import_joi4.default.string().required()
|
|
1442
1456
|
});
|
|
1443
1457
|
function MUnit(value) {
|
|
1444
1458
|
const { error } = unitSchema.validate(value);
|
|
@@ -1551,7 +1565,7 @@ function useUnitRepository() {
|
|
|
1551
1565
|
throw new import_node_server_utils7.BadRequestError("Invalid site ID format.");
|
|
1552
1566
|
}
|
|
1553
1567
|
if (search) {
|
|
1554
|
-
query.$or = [{ name: { $regex: search, $options: "i" } }];
|
|
1568
|
+
query.$or = [{ name: { $regex: escapeRegex(search), $options: "i" } }];
|
|
1555
1569
|
cacheOptions.search = search;
|
|
1556
1570
|
}
|
|
1557
1571
|
const cacheKey = (0, import_node_server_utils7.makeCacheKey)(namespace_collection, cacheOptions);
|
|
@@ -1773,10 +1787,7 @@ function useAreaService() {
|
|
|
1773
1787
|
}
|
|
1774
1788
|
}
|
|
1775
1789
|
if (foundUnit) {
|
|
1776
|
-
areaUnits.push(
|
|
1777
|
-
unit: foundUnit._id,
|
|
1778
|
-
name: foundUnit.name
|
|
1779
|
-
});
|
|
1790
|
+
areaUnits.push(toAreaUnit(foundUnit));
|
|
1780
1791
|
}
|
|
1781
1792
|
}
|
|
1782
1793
|
if (areaUnits.length > 0) {
|
|
@@ -1873,7 +1884,7 @@ function useAreaService() {
|
|
|
1873
1884
|
}
|
|
1874
1885
|
|
|
1875
1886
|
// src/controllers/hygiene-area.controller.ts
|
|
1876
|
-
var
|
|
1887
|
+
var import_joi5 = __toESM(require("joi"));
|
|
1877
1888
|
var import_node_server_utils9 = require("@7365admin1/node-server-utils");
|
|
1878
1889
|
var import_core4 = require("@7365admin1/core");
|
|
1879
1890
|
|
|
@@ -1934,13 +1945,13 @@ function useAreaController() {
|
|
|
1934
1945
|
}
|
|
1935
1946
|
async function getAreas(req, res, next) {
|
|
1936
1947
|
const query = { ...req.query, ...req.params };
|
|
1937
|
-
const validation =
|
|
1938
|
-
page:
|
|
1939
|
-
limit:
|
|
1940
|
-
search:
|
|
1941
|
-
type:
|
|
1942
|
-
site:
|
|
1943
|
-
serviceType:
|
|
1948
|
+
const validation = import_joi5.default.object({
|
|
1949
|
+
page: import_joi5.default.number().min(1).optional().allow("", null),
|
|
1950
|
+
limit: import_joi5.default.number().min(1).optional().allow("", null),
|
|
1951
|
+
search: import_joi5.default.string().optional().allow("", null),
|
|
1952
|
+
type: import_joi5.default.string().valid("all", ...allowedTypes).optional().allow("", null),
|
|
1953
|
+
site: import_joi5.default.string().hex().required(),
|
|
1954
|
+
serviceType: import_joi5.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
|
|
1944
1955
|
});
|
|
1945
1956
|
const { error } = validation.validate(query);
|
|
1946
1957
|
if (error) {
|
|
@@ -1971,7 +1982,7 @@ function useAreaController() {
|
|
|
1971
1982
|
}
|
|
1972
1983
|
}
|
|
1973
1984
|
async function getAreaById(req, res, next) {
|
|
1974
|
-
const validation =
|
|
1985
|
+
const validation = import_joi5.default.string().hex().required();
|
|
1975
1986
|
const _id = req.params.id;
|
|
1976
1987
|
const { error, value } = validation.validate(_id);
|
|
1977
1988
|
if (error) {
|
|
@@ -1991,15 +2002,15 @@ function useAreaController() {
|
|
|
1991
2002
|
}
|
|
1992
2003
|
async function updateArea(req, res, next) {
|
|
1993
2004
|
const payload = { id: req.params.id, ...req.body };
|
|
1994
|
-
const schema =
|
|
1995
|
-
id:
|
|
1996
|
-
name:
|
|
1997
|
-
type:
|
|
1998
|
-
set:
|
|
1999
|
-
units:
|
|
2000
|
-
|
|
2001
|
-
unit:
|
|
2002
|
-
name:
|
|
2005
|
+
const schema = import_joi5.default.object({
|
|
2006
|
+
id: import_joi5.default.string().hex().required(),
|
|
2007
|
+
name: import_joi5.default.string().optional().allow("", null),
|
|
2008
|
+
type: import_joi5.default.string().optional().allow("", null, ...allowedTypes),
|
|
2009
|
+
set: import_joi5.default.number().min(0).optional(),
|
|
2010
|
+
units: import_joi5.default.array().items(
|
|
2011
|
+
import_joi5.default.object({
|
|
2012
|
+
unit: import_joi5.default.string().hex().required(),
|
|
2013
|
+
name: import_joi5.default.string().required()
|
|
2003
2014
|
}).required()
|
|
2004
2015
|
).min(1).unique("unit", { ignoreUndefined: true }).optional().messages({
|
|
2005
2016
|
"array.unique": "Duplicate area units are not allowed"
|
|
@@ -2024,7 +2035,7 @@ function useAreaController() {
|
|
|
2024
2035
|
}
|
|
2025
2036
|
async function deleteArea(req, res, next) {
|
|
2026
2037
|
const id = req.params.id;
|
|
2027
|
-
const validation =
|
|
2038
|
+
const validation = import_joi5.default.string().hex().required();
|
|
2028
2039
|
const { error, value } = validation.validate(id);
|
|
2029
2040
|
if (error) {
|
|
2030
2041
|
import_node_server_utils9.logger.log({ level: "error", message: error.message });
|
|
@@ -2047,9 +2058,9 @@ function useAreaController() {
|
|
|
2047
2058
|
return;
|
|
2048
2059
|
}
|
|
2049
2060
|
const query = { ...req.query, ...req.params };
|
|
2050
|
-
const validation =
|
|
2051
|
-
site:
|
|
2052
|
-
serviceType:
|
|
2061
|
+
const validation = import_joi5.default.object({
|
|
2062
|
+
site: import_joi5.default.string().hex().required(),
|
|
2063
|
+
serviceType: import_joi5.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
|
|
2053
2064
|
});
|
|
2054
2065
|
const { error, value } = validation.validate(query);
|
|
2055
2066
|
if (error) {
|
|
@@ -2070,9 +2081,9 @@ function useAreaController() {
|
|
|
2070
2081
|
}
|
|
2071
2082
|
async function exportAreas(req, res, next) {
|
|
2072
2083
|
const query = { ...req.query, ...req.params };
|
|
2073
|
-
const validation =
|
|
2074
|
-
site:
|
|
2075
|
-
serviceType:
|
|
2084
|
+
const validation = import_joi5.default.object({
|
|
2085
|
+
site: import_joi5.default.string().hex().required(),
|
|
2086
|
+
serviceType: import_joi5.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
|
|
2076
2087
|
});
|
|
2077
2088
|
const { error, value } = validation.validate(query);
|
|
2078
2089
|
if (error) {
|
|
@@ -2354,7 +2365,7 @@ function useUnitService() {
|
|
|
2354
2365
|
}
|
|
2355
2366
|
|
|
2356
2367
|
// src/controllers/hygiene-unit.controller.ts
|
|
2357
|
-
var
|
|
2368
|
+
var import_joi6 = __toESM(require("joi"));
|
|
2358
2369
|
var import_node_server_utils12 = require("@7365admin1/node-server-utils");
|
|
2359
2370
|
var import_core5 = require("@7365admin1/core");
|
|
2360
2371
|
function useUnitController() {
|
|
@@ -2385,12 +2396,12 @@ function useUnitController() {
|
|
|
2385
2396
|
}
|
|
2386
2397
|
async function getUnits(req, res, next) {
|
|
2387
2398
|
const query = { ...req.query, ...req.params };
|
|
2388
|
-
const validation =
|
|
2389
|
-
page:
|
|
2390
|
-
limit:
|
|
2391
|
-
search:
|
|
2392
|
-
site:
|
|
2393
|
-
serviceType:
|
|
2399
|
+
const validation = import_joi6.default.object({
|
|
2400
|
+
page: import_joi6.default.number().min(1).optional().allow("", null),
|
|
2401
|
+
limit: import_joi6.default.number().min(1).optional().allow("", null),
|
|
2402
|
+
search: import_joi6.default.string().optional().allow("", null),
|
|
2403
|
+
site: import_joi6.default.string().hex().required(),
|
|
2404
|
+
serviceType: import_joi6.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
|
|
2394
2405
|
});
|
|
2395
2406
|
const { error } = validation.validate(query);
|
|
2396
2407
|
if (error) {
|
|
@@ -2421,9 +2432,9 @@ function useUnitController() {
|
|
|
2421
2432
|
}
|
|
2422
2433
|
async function updateUnit(req, res, next) {
|
|
2423
2434
|
const payload = { id: req.params.id, ...req.body };
|
|
2424
|
-
const validation =
|
|
2425
|
-
id:
|
|
2426
|
-
name:
|
|
2435
|
+
const validation = import_joi6.default.object({
|
|
2436
|
+
id: import_joi6.default.string().hex().required(),
|
|
2437
|
+
name: import_joi6.default.string().required()
|
|
2427
2438
|
});
|
|
2428
2439
|
const { error } = validation.validate(payload);
|
|
2429
2440
|
if (error) {
|
|
@@ -2444,8 +2455,8 @@ function useUnitController() {
|
|
|
2444
2455
|
}
|
|
2445
2456
|
async function deleteUnit(req, res, next) {
|
|
2446
2457
|
const id = req.params.id;
|
|
2447
|
-
const validation =
|
|
2448
|
-
id:
|
|
2458
|
+
const validation = import_joi6.default.object({
|
|
2459
|
+
id: import_joi6.default.string().hex().required()
|
|
2449
2460
|
});
|
|
2450
2461
|
const { error, value } = validation.validate({ id });
|
|
2451
2462
|
if (error) {
|
|
@@ -2469,9 +2480,9 @@ function useUnitController() {
|
|
|
2469
2480
|
return;
|
|
2470
2481
|
}
|
|
2471
2482
|
const query = { ...req.query, ...req.params };
|
|
2472
|
-
const validation =
|
|
2473
|
-
site:
|
|
2474
|
-
serviceType:
|
|
2483
|
+
const validation = import_joi6.default.object({
|
|
2484
|
+
site: import_joi6.default.string().hex().required(),
|
|
2485
|
+
serviceType: import_joi6.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
|
|
2475
2486
|
});
|
|
2476
2487
|
const { error, value } = validation.validate(query);
|
|
2477
2488
|
if (error) {
|
|
@@ -2492,9 +2503,9 @@ function useUnitController() {
|
|
|
2492
2503
|
}
|
|
2493
2504
|
async function exportUnits(req, res, next) {
|
|
2494
2505
|
const query = { ...req.query, ...req.params };
|
|
2495
|
-
const validation =
|
|
2496
|
-
site:
|
|
2497
|
-
serviceType:
|
|
2506
|
+
const validation = import_joi6.default.object({
|
|
2507
|
+
site: import_joi6.default.string().hex().required(),
|
|
2508
|
+
serviceType: import_joi6.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
|
|
2498
2509
|
});
|
|
2499
2510
|
const { error, value } = validation.validate(query);
|
|
2500
2511
|
if (error) {
|
|
@@ -2537,15 +2548,15 @@ function useUnitController() {
|
|
|
2537
2548
|
}
|
|
2538
2549
|
|
|
2539
2550
|
// src/models/hygiene-parent-checklist.model.ts
|
|
2540
|
-
var
|
|
2551
|
+
var import_joi7 = __toESM(require("joi"));
|
|
2541
2552
|
var import_mongodb6 = require("mongodb");
|
|
2542
2553
|
var import_node_server_utils13 = require("@7365admin1/node-server-utils");
|
|
2543
2554
|
var import_core6 = require("@7365admin1/core");
|
|
2544
|
-
var parentChecklistSchema =
|
|
2545
|
-
createdAt:
|
|
2546
|
-
site:
|
|
2547
|
-
serviceType:
|
|
2548
|
-
dateStr:
|
|
2555
|
+
var parentChecklistSchema = import_joi7.default.object({
|
|
2556
|
+
createdAt: import_joi7.default.alternatives().try(import_joi7.default.date(), import_joi7.default.string()).optional().allow("", null),
|
|
2557
|
+
site: import_joi7.default.string().hex().required(),
|
|
2558
|
+
serviceType: import_joi7.default.string().valid(...Object.values(import_core6.AppServiceType)).required(),
|
|
2559
|
+
dateStr: import_joi7.default.string().optional().allow("", null)
|
|
2549
2560
|
});
|
|
2550
2561
|
function MParentChecklist(value) {
|
|
2551
2562
|
const { error } = parentChecklistSchema.validate(value);
|
|
@@ -3118,7 +3129,7 @@ function useParentChecklistRepo() {
|
|
|
3118
3129
|
}
|
|
3119
3130
|
|
|
3120
3131
|
// src/controllers/hygiene-parent-checklist.controller.ts
|
|
3121
|
-
var
|
|
3132
|
+
var import_joi8 = __toESM(require("joi"));
|
|
3122
3133
|
var import_node_server_utils15 = require("@7365admin1/node-server-utils");
|
|
3123
3134
|
var import_core8 = require("@7365admin1/core");
|
|
3124
3135
|
function useParentChecklistController() {
|
|
@@ -3128,10 +3139,10 @@ function useParentChecklistController() {
|
|
|
3128
3139
|
} = useParentChecklistRepo();
|
|
3129
3140
|
async function createParentChecklist(req, res, next) {
|
|
3130
3141
|
const payload = req.body;
|
|
3131
|
-
const validation =
|
|
3132
|
-
createdAt:
|
|
3133
|
-
site:
|
|
3134
|
-
serviceType:
|
|
3142
|
+
const validation = import_joi8.default.object({
|
|
3143
|
+
createdAt: import_joi8.default.alternatives().try(import_joi8.default.date(), import_joi8.default.string()).optional().allow("", null),
|
|
3144
|
+
site: import_joi8.default.string().hex().required(),
|
|
3145
|
+
serviceType: import_joi8.default.string().valid(...Object.values(import_core8.AppServiceType)).optional().allow("", null)
|
|
3135
3146
|
});
|
|
3136
3147
|
const { error } = validation.validate(payload);
|
|
3137
3148
|
if (error) {
|
|
@@ -3151,15 +3162,15 @@ function useParentChecklistController() {
|
|
|
3151
3162
|
}
|
|
3152
3163
|
async function getAllParentChecklist(req, res, next) {
|
|
3153
3164
|
const query = { ...req.query, ...req.params };
|
|
3154
|
-
const validation =
|
|
3155
|
-
page:
|
|
3156
|
-
limit:
|
|
3157
|
-
search:
|
|
3158
|
-
site:
|
|
3159
|
-
serviceType:
|
|
3160
|
-
startDate:
|
|
3161
|
-
endDate:
|
|
3162
|
-
status:
|
|
3165
|
+
const validation = import_joi8.default.object({
|
|
3166
|
+
page: import_joi8.default.number().min(1).optional().allow("", null),
|
|
3167
|
+
limit: import_joi8.default.number().min(1).optional().allow("", null),
|
|
3168
|
+
search: import_joi8.default.string().optional().allow("", null),
|
|
3169
|
+
site: import_joi8.default.string().hex().required(),
|
|
3170
|
+
serviceType: import_joi8.default.string().valid(...Object.values(import_core8.AppServiceType)).required(),
|
|
3171
|
+
startDate: import_joi8.default.alternatives().try(import_joi8.default.date(), import_joi8.default.string()).optional().allow("", null),
|
|
3172
|
+
endDate: import_joi8.default.alternatives().try(import_joi8.default.date(), import_joi8.default.string()).optional().allow("", null),
|
|
3173
|
+
status: import_joi8.default.string().valid(...allowedStatus, "all").optional().allow("", null)
|
|
3163
3174
|
});
|
|
3164
3175
|
const { error } = validation.validate(query);
|
|
3165
3176
|
if (error) {
|
|
@@ -3201,28 +3212,28 @@ function useParentChecklistController() {
|
|
|
3201
3212
|
}
|
|
3202
3213
|
|
|
3203
3214
|
// src/models/hygiene-area-checklist.model.ts
|
|
3204
|
-
var
|
|
3215
|
+
var import_joi9 = __toESM(require("joi"));
|
|
3205
3216
|
var import_mongodb8 = require("mongodb");
|
|
3206
3217
|
var import_node_server_utils16 = require("@7365admin1/node-server-utils");
|
|
3207
3218
|
var import_core9 = require("@7365admin1/core");
|
|
3208
3219
|
var allowedChecklistStatus = ["open", "completed", "closed"];
|
|
3209
|
-
var areaChecklistSchema =
|
|
3210
|
-
schedule:
|
|
3211
|
-
serviceType:
|
|
3212
|
-
area:
|
|
3213
|
-
name:
|
|
3214
|
-
type:
|
|
3215
|
-
checklist:
|
|
3216
|
-
|
|
3217
|
-
set:
|
|
3218
|
-
units:
|
|
3219
|
-
|
|
3220
|
-
unit:
|
|
3221
|
-
name:
|
|
3220
|
+
var areaChecklistSchema = import_joi9.default.object({
|
|
3221
|
+
schedule: import_joi9.default.string().hex().required(),
|
|
3222
|
+
serviceType: import_joi9.default.string().valid(...Object.values(import_core9.AppServiceType)).required(),
|
|
3223
|
+
area: import_joi9.default.string().hex().required(),
|
|
3224
|
+
name: import_joi9.default.string().required(),
|
|
3225
|
+
type: import_joi9.default.string().valid(...allowedTypes).required(),
|
|
3226
|
+
checklist: import_joi9.default.array().items(
|
|
3227
|
+
import_joi9.default.object({
|
|
3228
|
+
set: import_joi9.default.number().min(0).required(),
|
|
3229
|
+
units: import_joi9.default.array().items(
|
|
3230
|
+
import_joi9.default.object({
|
|
3231
|
+
unit: import_joi9.default.string().hex().required(),
|
|
3232
|
+
name: import_joi9.default.string().required()
|
|
3222
3233
|
}).required()
|
|
3223
3234
|
).min(1).required(),
|
|
3224
|
-
isScheduleTask:
|
|
3225
|
-
scheduleTaskId:
|
|
3235
|
+
isScheduleTask: import_joi9.default.boolean().optional().default(false),
|
|
3236
|
+
scheduleTaskId: import_joi9.default.string().hex().optional().allow(null)
|
|
3226
3237
|
}).required()
|
|
3227
3238
|
).optional().default([])
|
|
3228
3239
|
});
|
|
@@ -4460,6 +4471,265 @@ function useAreaChecklistRepo() {
|
|
|
4460
4471
|
};
|
|
4461
4472
|
}
|
|
4462
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
|
+
|
|
4463
4733
|
// src/services/hygiene-area-checklist.service.ts
|
|
4464
4734
|
function useAreaChecklistService() {
|
|
4465
4735
|
const { getAreasForChecklist } = useAreaRepo();
|
|
@@ -4527,10 +4797,10 @@ function useAreaChecklistService() {
|
|
|
4527
4797
|
`Area checklist for area ${area.name} exists but has no auto-gen sets. Inserting auto-gen sets.`
|
|
4528
4798
|
);
|
|
4529
4799
|
const setCount2 = Number(area.set) || 1;
|
|
4530
|
-
const
|
|
4531
|
-
if (
|
|
4800
|
+
const setsToAdd = regularSetsMissing(existing.checklist, setCount2);
|
|
4801
|
+
if (setsToAdd <= 0) {
|
|
4532
4802
|
import_node_server_utils18.logger.info(
|
|
4533
|
-
`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.`
|
|
4534
4804
|
);
|
|
4535
4805
|
return existing._id;
|
|
4536
4806
|
}
|
|
@@ -4538,7 +4808,6 @@ function useAreaChecklistService() {
|
|
|
4538
4808
|
(max, c) => Math.max(max, c.set ?? 0),
|
|
4539
4809
|
0
|
|
4540
4810
|
) ?? 0;
|
|
4541
|
-
const setsToAdd = setCount2 - totalExistingSets;
|
|
4542
4811
|
const autoGenSets = Array.from(
|
|
4543
4812
|
{ length: setsToAdd },
|
|
4544
4813
|
(_, index) => ({
|
|
@@ -4699,7 +4968,7 @@ function useAreaChecklistService() {
|
|
|
4699
4968
|
}
|
|
4700
4969
|
|
|
4701
4970
|
// src/controllers/hygiene-area-checklist.controller.ts
|
|
4702
|
-
var
|
|
4971
|
+
var import_joi11 = __toESM(require("joi"));
|
|
4703
4972
|
var import_node_server_utils20 = require("@7365admin1/node-server-utils");
|
|
4704
4973
|
var import_core11 = require("@7365admin1/core");
|
|
4705
4974
|
|
|
@@ -4940,9 +5209,9 @@ function useAreaChecklistController() {
|
|
|
4940
5209
|
site: req.params.site,
|
|
4941
5210
|
schedule: req.params.schedule
|
|
4942
5211
|
};
|
|
4943
|
-
const validation =
|
|
4944
|
-
site:
|
|
4945
|
-
schedule:
|
|
5212
|
+
const validation = import_joi11.default.object({
|
|
5213
|
+
site: import_joi11.default.string().hex().required(),
|
|
5214
|
+
schedule: import_joi11.default.string().hex().required()
|
|
4946
5215
|
});
|
|
4947
5216
|
const { error, value } = validation.validate(payload);
|
|
4948
5217
|
if (error) {
|
|
@@ -4962,14 +5231,14 @@ function useAreaChecklistController() {
|
|
|
4962
5231
|
}
|
|
4963
5232
|
async function getAllAreaChecklist(req, res, next) {
|
|
4964
5233
|
const query = { ...req.query, ...req.params };
|
|
4965
|
-
const validation =
|
|
4966
|
-
page:
|
|
4967
|
-
limit:
|
|
4968
|
-
search:
|
|
4969
|
-
type:
|
|
4970
|
-
status:
|
|
4971
|
-
schedule:
|
|
4972
|
-
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()
|
|
4973
5242
|
});
|
|
4974
5243
|
const { error } = validation.validate(query);
|
|
4975
5244
|
if (error) {
|
|
@@ -5004,14 +5273,14 @@ function useAreaChecklistController() {
|
|
|
5004
5273
|
}
|
|
5005
5274
|
async function getAreaChecklistHistory(req, res, next) {
|
|
5006
5275
|
const query = { ...req.query, ...req.params };
|
|
5007
|
-
const validation =
|
|
5008
|
-
page:
|
|
5009
|
-
limit:
|
|
5010
|
-
search:
|
|
5011
|
-
type:
|
|
5012
|
-
schedule:
|
|
5013
|
-
status:
|
|
5014
|
-
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)
|
|
5015
5284
|
});
|
|
5016
5285
|
const { error } = validation.validate(query);
|
|
5017
5286
|
if (error) {
|
|
@@ -5045,7 +5314,7 @@ function useAreaChecklistController() {
|
|
|
5045
5314
|
}
|
|
5046
5315
|
}
|
|
5047
5316
|
async function getAreaChecklistHistoryDetails(req, res, next) {
|
|
5048
|
-
const validation =
|
|
5317
|
+
const validation = import_joi11.default.string().hex().required();
|
|
5049
5318
|
const _id = req.params.id;
|
|
5050
5319
|
const { error, value } = validation.validate(_id);
|
|
5051
5320
|
if (error) {
|
|
@@ -5065,12 +5334,12 @@ function useAreaChecklistController() {
|
|
|
5065
5334
|
}
|
|
5066
5335
|
async function getAreaChecklistUnits(req, res, next) {
|
|
5067
5336
|
const query = { ...req.query, ...req.params };
|
|
5068
|
-
const validation =
|
|
5069
|
-
page:
|
|
5070
|
-
limit:
|
|
5071
|
-
search:
|
|
5072
|
-
serviceType:
|
|
5073
|
-
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()
|
|
5074
5343
|
});
|
|
5075
5344
|
const { error } = validation.validate(query);
|
|
5076
5345
|
if (error) {
|
|
@@ -5120,21 +5389,21 @@ function useAreaChecklistController() {
|
|
|
5120
5389
|
...decisionValues,
|
|
5121
5390
|
completedBy
|
|
5122
5391
|
};
|
|
5123
|
-
const validation =
|
|
5124
|
-
id:
|
|
5125
|
-
set:
|
|
5126
|
-
unit:
|
|
5127
|
-
decision:
|
|
5128
|
-
approve:
|
|
5129
|
-
reject:
|
|
5130
|
-
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),
|
|
5131
5400
|
/*
|
|
5132
5401
|
* Shape only. WHETHER a photo is required depends on the set's current
|
|
5133
5402
|
* state in the database, which Joi cannot see — see the check below.
|
|
5134
5403
|
* Each entry must still be a real id: a blank string carries no evidence.
|
|
5135
5404
|
*/
|
|
5136
|
-
attachment:
|
|
5137
|
-
completedBy:
|
|
5405
|
+
attachment: import_joi11.default.array().items(import_joi11.default.string().trim().min(1)).optional(),
|
|
5406
|
+
completedBy: import_joi11.default.string().hex().required()
|
|
5138
5407
|
});
|
|
5139
5408
|
const { error } = validation.validate(payload);
|
|
5140
5409
|
if (error) {
|
|
@@ -5171,7 +5440,7 @@ function useAreaChecklistController() {
|
|
|
5171
5440
|
}
|
|
5172
5441
|
}
|
|
5173
5442
|
async function downloadChecklistPdf(req, res, next) {
|
|
5174
|
-
const validation =
|
|
5443
|
+
const validation = import_joi11.default.string().hex().required();
|
|
5175
5444
|
const schedule = req.params.schedule;
|
|
5176
5445
|
const { error, value } = validation.validate(schedule);
|
|
5177
5446
|
if (error) {
|
|
@@ -5217,16 +5486,16 @@ function useAreaChecklistController() {
|
|
|
5217
5486
|
}
|
|
5218
5487
|
|
|
5219
5488
|
// src/models/hygiene-supply.model.ts
|
|
5220
|
-
var
|
|
5489
|
+
var import_joi12 = __toESM(require("joi"));
|
|
5221
5490
|
var import_mongodb12 = require("mongodb");
|
|
5222
5491
|
var import_node_server_utils21 = require("@7365admin1/node-server-utils");
|
|
5223
5492
|
var import_core12 = require("@7365admin1/core");
|
|
5224
|
-
var supplySchema =
|
|
5225
|
-
site:
|
|
5226
|
-
serviceType:
|
|
5227
|
-
name:
|
|
5228
|
-
unitOfMeasurement:
|
|
5229
|
-
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()
|
|
5230
5499
|
});
|
|
5231
5500
|
function MSupply(value) {
|
|
5232
5501
|
const { error } = supplySchema.validate(value);
|
|
@@ -5354,7 +5623,7 @@ function useSupplyRepository() {
|
|
|
5354
5623
|
throw new import_node_server_utils22.BadRequestError("Invalid site ID format.");
|
|
5355
5624
|
}
|
|
5356
5625
|
if (search) {
|
|
5357
|
-
query.name = { $regex: search, $options: "i" };
|
|
5626
|
+
query.name = { $regex: escapeRegex(search), $options: "i" };
|
|
5358
5627
|
cacheOptions.search = search;
|
|
5359
5628
|
}
|
|
5360
5629
|
const cacheKey = (0, import_node_server_utils22.makeCacheKey)(namespace_collection, cacheOptions);
|
|
@@ -5516,7 +5785,7 @@ function useSupplyRepository() {
|
|
|
5516
5785
|
}
|
|
5517
5786
|
|
|
5518
5787
|
// src/controllers/hygiene-supply.controller.ts
|
|
5519
|
-
var
|
|
5788
|
+
var import_joi13 = __toESM(require("joi"));
|
|
5520
5789
|
var import_node_server_utils23 = require("@7365admin1/node-server-utils");
|
|
5521
5790
|
var import_core13 = require("@7365admin1/core");
|
|
5522
5791
|
function useSupplyController() {
|
|
@@ -5547,12 +5816,12 @@ function useSupplyController() {
|
|
|
5547
5816
|
}
|
|
5548
5817
|
async function getSupplies(req, res, next) {
|
|
5549
5818
|
const query = { ...req.query, ...req.params };
|
|
5550
|
-
const validation =
|
|
5551
|
-
page:
|
|
5552
|
-
limit:
|
|
5553
|
-
search:
|
|
5554
|
-
site:
|
|
5555
|
-
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()
|
|
5556
5825
|
});
|
|
5557
5826
|
const { error } = validation.validate(query);
|
|
5558
5827
|
if (error) {
|
|
@@ -5582,7 +5851,7 @@ function useSupplyController() {
|
|
|
5582
5851
|
}
|
|
5583
5852
|
}
|
|
5584
5853
|
async function getSupplyById(req, res, next) {
|
|
5585
|
-
const validation =
|
|
5854
|
+
const validation = import_joi13.default.string().hex().required();
|
|
5586
5855
|
const _id = req.params.id;
|
|
5587
5856
|
const { error, value } = validation.validate(_id);
|
|
5588
5857
|
if (error) {
|
|
@@ -5602,12 +5871,12 @@ function useSupplyController() {
|
|
|
5602
5871
|
}
|
|
5603
5872
|
async function updateSupply(req, res, next) {
|
|
5604
5873
|
const payload = { id: req.params.id, ...req.body };
|
|
5605
|
-
const validation =
|
|
5606
|
-
id:
|
|
5607
|
-
name:
|
|
5608
|
-
unitOfMeasurement:
|
|
5609
|
-
qty:
|
|
5610
|
-
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)
|
|
5611
5880
|
});
|
|
5612
5881
|
const { error } = validation.validate(payload);
|
|
5613
5882
|
if (error) {
|
|
@@ -5628,7 +5897,7 @@ function useSupplyController() {
|
|
|
5628
5897
|
}
|
|
5629
5898
|
async function deleteSupply(req, res, next) {
|
|
5630
5899
|
const id = req.params.id;
|
|
5631
|
-
const validation =
|
|
5900
|
+
const validation = import_joi13.default.string().hex().required();
|
|
5632
5901
|
const { error, value } = validation.validate(id);
|
|
5633
5902
|
if (error) {
|
|
5634
5903
|
import_node_server_utils23.logger.log({ level: "error", message: error.message });
|
|
@@ -5655,18 +5924,18 @@ function useSupplyController() {
|
|
|
5655
5924
|
}
|
|
5656
5925
|
|
|
5657
5926
|
// src/models/hygiene-stock.model.ts
|
|
5658
|
-
var
|
|
5927
|
+
var import_joi14 = __toESM(require("joi"));
|
|
5659
5928
|
var import_mongodb14 = require("mongodb");
|
|
5660
5929
|
var import_node_server_utils24 = require("@7365admin1/node-server-utils");
|
|
5661
5930
|
var import_core14 = require("@7365admin1/core");
|
|
5662
|
-
var stockSchema =
|
|
5663
|
-
site:
|
|
5664
|
-
serviceType:
|
|
5665
|
-
supply:
|
|
5666
|
-
in:
|
|
5667
|
-
out:
|
|
5668
|
-
balance:
|
|
5669
|
-
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)
|
|
5670
5939
|
});
|
|
5671
5940
|
function MStock(value) {
|
|
5672
5941
|
const { error } = stockSchema.validate(value);
|
|
@@ -5881,7 +6150,7 @@ function useStockService() {
|
|
|
5881
6150
|
}
|
|
5882
6151
|
|
|
5883
6152
|
// src/controllers/hygiene-stock.controller.ts
|
|
5884
|
-
var
|
|
6153
|
+
var import_joi15 = __toESM(require("joi"));
|
|
5885
6154
|
var import_node_server_utils27 = require("@7365admin1/node-server-utils");
|
|
5886
6155
|
var import_core15 = require("@7365admin1/core");
|
|
5887
6156
|
function useStockController() {
|
|
@@ -5889,12 +6158,12 @@ function useStockController() {
|
|
|
5889
6158
|
const { createStock: _createStock } = useStockService();
|
|
5890
6159
|
async function createStock(req, res, next) {
|
|
5891
6160
|
const payload = { ...req.body, ...req.params };
|
|
5892
|
-
const validation =
|
|
5893
|
-
site:
|
|
5894
|
-
serviceType:
|
|
5895
|
-
supply:
|
|
5896
|
-
qty:
|
|
5897
|
-
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)
|
|
5898
6167
|
});
|
|
5899
6168
|
const { error } = validation.validate(payload);
|
|
5900
6169
|
if (error) {
|
|
@@ -5914,13 +6183,13 @@ function useStockController() {
|
|
|
5914
6183
|
}
|
|
5915
6184
|
async function getStocksBySupplyId(req, res, next) {
|
|
5916
6185
|
const query = { ...req.query, ...req.params };
|
|
5917
|
-
const validation =
|
|
5918
|
-
page:
|
|
5919
|
-
limit:
|
|
5920
|
-
search:
|
|
5921
|
-
site:
|
|
5922
|
-
serviceType:
|
|
5923
|
-
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()
|
|
5924
6193
|
});
|
|
5925
6194
|
const { error } = validation.validate(query);
|
|
5926
6195
|
if (error) {
|
|
@@ -5982,21 +6251,21 @@ async function applyCheckOutDecision(decision, decide, returnStock) {
|
|
|
5982
6251
|
}
|
|
5983
6252
|
|
|
5984
6253
|
// src/models/hygiene-checkout-item.model.ts
|
|
5985
|
-
var
|
|
6254
|
+
var import_joi16 = __toESM(require("joi"));
|
|
5986
6255
|
var import_mongodb16 = require("mongodb");
|
|
5987
6256
|
var import_node_server_utils28 = require("@7365admin1/node-server-utils");
|
|
5988
6257
|
var import_core16 = require("@7365admin1/core");
|
|
5989
6258
|
var allowedCheckOutItemStatus = ["pending", "completed"];
|
|
5990
|
-
var checkOutItemSchema =
|
|
5991
|
-
site:
|
|
5992
|
-
serviceType:
|
|
5993
|
-
supply:
|
|
5994
|
-
supplyName:
|
|
5995
|
-
supplyQty:
|
|
5996
|
-
qty:
|
|
5997
|
-
attachment:
|
|
5998
|
-
createdBy:
|
|
5999
|
-
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()
|
|
6000
6269
|
});
|
|
6001
6270
|
function MCheckOutItem(value) {
|
|
6002
6271
|
const { error } = checkOutItemSchema.validate(value);
|
|
@@ -6120,7 +6389,7 @@ function useCheckOutItemRepository() {
|
|
|
6120
6389
|
throw new import_node_server_utils29.BadRequestError("Invalid site ID format.");
|
|
6121
6390
|
}
|
|
6122
6391
|
if (search) {
|
|
6123
|
-
query.supplyName = { $regex: search, $options: "i" };
|
|
6392
|
+
query.supplyName = { $regex: escapeRegex(search), $options: "i" };
|
|
6124
6393
|
cacheOptions.search = search;
|
|
6125
6394
|
}
|
|
6126
6395
|
const cacheKey = (0, import_node_server_utils29.makeCacheKey)(namespace_collection, cacheOptions);
|
|
@@ -6476,7 +6745,7 @@ function useCheckOutItemService() {
|
|
|
6476
6745
|
}
|
|
6477
6746
|
|
|
6478
6747
|
// src/controllers/hygiene-checkout-item.controller.ts
|
|
6479
|
-
var
|
|
6748
|
+
var import_joi17 = __toESM(require("joi"));
|
|
6480
6749
|
var import_node_server_utils31 = require("@7365admin1/node-server-utils");
|
|
6481
6750
|
var import_core18 = require("@7365admin1/core");
|
|
6482
6751
|
function useCheckOutItemController() {
|
|
@@ -6500,13 +6769,13 @@ function useCheckOutItemController() {
|
|
|
6500
6769
|
...req.params,
|
|
6501
6770
|
createdBy
|
|
6502
6771
|
};
|
|
6503
|
-
const validation =
|
|
6504
|
-
site:
|
|
6505
|
-
serviceType:
|
|
6506
|
-
supply:
|
|
6507
|
-
qty:
|
|
6508
|
-
attachment:
|
|
6509
|
-
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()
|
|
6510
6779
|
});
|
|
6511
6780
|
const { error } = validation.validate(payload);
|
|
6512
6781
|
if (error) {
|
|
@@ -6535,15 +6804,15 @@ function useCheckOutItemController() {
|
|
|
6535
6804
|
...req.params,
|
|
6536
6805
|
createdBy
|
|
6537
6806
|
};
|
|
6538
|
-
const validation =
|
|
6539
|
-
site:
|
|
6540
|
-
serviceType:
|
|
6541
|
-
createdBy:
|
|
6542
|
-
items:
|
|
6543
|
-
|
|
6544
|
-
supply:
|
|
6545
|
-
qty:
|
|
6546
|
-
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)
|
|
6547
6816
|
})
|
|
6548
6817
|
).min(1).required()
|
|
6549
6818
|
});
|
|
@@ -6565,12 +6834,12 @@ function useCheckOutItemController() {
|
|
|
6565
6834
|
}
|
|
6566
6835
|
async function getCheckOutItems(req, res, next) {
|
|
6567
6836
|
const query = { ...req.query, ...req.params };
|
|
6568
|
-
const validation =
|
|
6569
|
-
page:
|
|
6570
|
-
limit:
|
|
6571
|
-
search:
|
|
6572
|
-
site:
|
|
6573
|
-
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()
|
|
6574
6843
|
});
|
|
6575
6844
|
const { error } = validation.validate(query);
|
|
6576
6845
|
if (error) {
|
|
@@ -6600,7 +6869,7 @@ function useCheckOutItemController() {
|
|
|
6600
6869
|
}
|
|
6601
6870
|
}
|
|
6602
6871
|
async function getCheckOutItemById(req, res, next) {
|
|
6603
|
-
const validation =
|
|
6872
|
+
const validation = import_joi17.default.string().hex().required();
|
|
6604
6873
|
const _id = req.params.id;
|
|
6605
6874
|
const { error, value } = validation.validate(_id);
|
|
6606
6875
|
if (error) {
|
|
@@ -6639,13 +6908,13 @@ function useCheckOutItemController() {
|
|
|
6639
6908
|
...decisionValues,
|
|
6640
6909
|
completedBy
|
|
6641
6910
|
};
|
|
6642
|
-
const validation =
|
|
6643
|
-
id:
|
|
6644
|
-
decision:
|
|
6645
|
-
approve:
|
|
6646
|
-
reject:
|
|
6647
|
-
remarks:
|
|
6648
|
-
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()
|
|
6649
6918
|
});
|
|
6650
6919
|
const { error } = validation.validate(payload);
|
|
6651
6920
|
if (error) {
|
|
@@ -6678,23 +6947,15 @@ function useCheckOutItemController() {
|
|
|
6678
6947
|
// src/models/hygiene-schedule-task.model.ts
|
|
6679
6948
|
var import_node_server_utils32 = require("@7365admin1/node-server-utils");
|
|
6680
6949
|
var import_core19 = require("@7365admin1/core");
|
|
6681
|
-
var
|
|
6950
|
+
var import_joi18 = __toESM(require("joi"));
|
|
6682
6951
|
var import_mongodb18 = require("mongodb");
|
|
6683
|
-
var scheduleTaskSchema =
|
|
6684
|
-
site:
|
|
6685
|
-
serviceType:
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
).required(),
|
|
6691
|
-
description: import_joi16.default.string().optional().allow("", null),
|
|
6692
|
-
areas: import_joi16.default.array().min(1).items(
|
|
6693
|
-
import_joi16.default.object({
|
|
6694
|
-
name: import_joi16.default.string().required(),
|
|
6695
|
-
value: import_joi16.default.any().required()
|
|
6696
|
-
})
|
|
6697
|
-
).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;
|
|
6698
6959
|
});
|
|
6699
6960
|
function MScheduleTask(value) {
|
|
6700
6961
|
const { error } = scheduleTaskSchema.validate(value);
|
|
@@ -6721,14 +6982,26 @@ function MScheduleTask(value) {
|
|
|
6721
6982
|
}
|
|
6722
6983
|
});
|
|
6723
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];
|
|
6724
6996
|
return {
|
|
6725
6997
|
site: value.site,
|
|
6726
6998
|
serviceType: value.serviceType,
|
|
6727
6999
|
title: value.title,
|
|
6728
7000
|
time: value.time,
|
|
6729
|
-
dates: value.dates,
|
|
7001
|
+
dates: value.dates ?? [],
|
|
6730
7002
|
description: value.description,
|
|
6731
7003
|
areas: value.areas,
|
|
7004
|
+
...extra,
|
|
6732
7005
|
status: "active",
|
|
6733
7006
|
createdAt: /* @__PURE__ */ new Date(),
|
|
6734
7007
|
updatedAt: "",
|
|
@@ -6739,11 +7012,23 @@ function MScheduleTask(value) {
|
|
|
6739
7012
|
// src/repositories/hygiene-schedule-task.repository.ts
|
|
6740
7013
|
var import_mongodb19 = require("mongodb");
|
|
6741
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
|
+
};
|
|
6742
7026
|
function useScheduleTaskRepository() {
|
|
6743
7027
|
const db = import_node_server_utils33.useAtlas.getDb();
|
|
6744
7028
|
if (!db) {
|
|
6745
7029
|
throw new import_node_server_utils33.InternalServerError("Unable to connect to server.");
|
|
6746
7030
|
}
|
|
7031
|
+
const database = db;
|
|
6747
7032
|
const namespace_collection = "site.schedule-tasks";
|
|
6748
7033
|
const collection = db.collection(namespace_collection);
|
|
6749
7034
|
const { delNamespace, setCache, getCache } = (0, import_node_server_utils33.useCache)(namespace_collection);
|
|
@@ -6811,14 +7096,14 @@ function useScheduleTaskRepository() {
|
|
|
6811
7096
|
throw new import_node_server_utils33.BadRequestError("Invalid site ID format.");
|
|
6812
7097
|
}
|
|
6813
7098
|
if (search) {
|
|
6814
|
-
query.$or = [{ title: { $regex: search, $options: "i" } }];
|
|
7099
|
+
query.$or = [{ title: { $regex: escapeRegex(search), $options: "i" } }];
|
|
6815
7100
|
cacheOptions.search = search;
|
|
6816
7101
|
}
|
|
6817
7102
|
const cacheKey = (0, import_node_server_utils33.makeCacheKey)(namespace_collection, cacheOptions);
|
|
6818
7103
|
const cachedData = await getCache(cacheKey);
|
|
6819
7104
|
if (cachedData) {
|
|
6820
7105
|
import_node_server_utils33.logger.info(`Cache hit for key: ${cacheKey}`);
|
|
6821
|
-
return cachedData;
|
|
7106
|
+
return enrichList(cachedData, site, serviceType);
|
|
6822
7107
|
}
|
|
6823
7108
|
try {
|
|
6824
7109
|
const items = await collection.aggregate([
|
|
@@ -6827,7 +7112,8 @@ function useScheduleTaskRepository() {
|
|
|
6827
7112
|
$project: {
|
|
6828
7113
|
title: 1,
|
|
6829
7114
|
areas: 1,
|
|
6830
|
-
status: 1
|
|
7115
|
+
status: 1,
|
|
7116
|
+
...SCHEDULE_FIELDS
|
|
6831
7117
|
}
|
|
6832
7118
|
},
|
|
6833
7119
|
{ $sort: { _id: -1 } },
|
|
@@ -6841,15 +7127,104 @@ function useScheduleTaskRepository() {
|
|
|
6841
7127
|
}).catch((err) => {
|
|
6842
7128
|
import_node_server_utils33.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
6843
7129
|
});
|
|
6844
|
-
return data;
|
|
7130
|
+
return enrichList(data, site, serviceType);
|
|
6845
7131
|
} catch (error) {
|
|
6846
7132
|
throw error;
|
|
6847
7133
|
}
|
|
6848
7134
|
}
|
|
6849
|
-
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) {
|
|
6850
7217
|
const query = {
|
|
6851
7218
|
status: { $ne: "deleted" }
|
|
6852
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
|
+
}
|
|
6853
7228
|
try {
|
|
6854
7229
|
const items = await collection.aggregate([{ $match: query }, { $sort: { _id: -1 } }]).toArray();
|
|
6855
7230
|
return items;
|
|
@@ -6857,6 +7232,9 @@ function useScheduleTaskRepository() {
|
|
|
6857
7232
|
throw error;
|
|
6858
7233
|
}
|
|
6859
7234
|
}
|
|
7235
|
+
function withScheduleDisplay(row) {
|
|
7236
|
+
return { ...row, ...scheduleDisplay(row, /* @__PURE__ */ new Date()) };
|
|
7237
|
+
}
|
|
6860
7238
|
async function getScheduleTaskById(_id, session) {
|
|
6861
7239
|
try {
|
|
6862
7240
|
_id = new import_mongodb19.ObjectId(_id);
|
|
@@ -6874,7 +7252,7 @@ function useScheduleTaskRepository() {
|
|
|
6874
7252
|
const cachedData = await getCache(cacheKey);
|
|
6875
7253
|
if (cachedData) {
|
|
6876
7254
|
import_node_server_utils33.logger.info(`Cache hit for key: ${cacheKey}`);
|
|
6877
|
-
return cachedData;
|
|
7255
|
+
return withScheduleDisplay(cachedData);
|
|
6878
7256
|
}
|
|
6879
7257
|
} else {
|
|
6880
7258
|
import_node_server_utils33.logger.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
@@ -6885,12 +7263,13 @@ function useScheduleTaskRepository() {
|
|
|
6885
7263
|
{
|
|
6886
7264
|
$project: {
|
|
6887
7265
|
title: 1,
|
|
6888
|
-
time: 1,
|
|
6889
|
-
dates: 1,
|
|
6890
7266
|
description: 1,
|
|
6891
7267
|
areas: 1,
|
|
6892
7268
|
status: 1,
|
|
6893
|
-
createdAt: 1
|
|
7269
|
+
createdAt: 1,
|
|
7270
|
+
site: 1,
|
|
7271
|
+
serviceType: 1,
|
|
7272
|
+
...SCHEDULE_FIELDS
|
|
6894
7273
|
}
|
|
6895
7274
|
}
|
|
6896
7275
|
]).toArray();
|
|
@@ -6902,7 +7281,7 @@ function useScheduleTaskRepository() {
|
|
|
6902
7281
|
}).catch((err) => {
|
|
6903
7282
|
import_node_server_utils33.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
6904
7283
|
});
|
|
6905
|
-
return data[0];
|
|
7284
|
+
return withScheduleDisplay(data[0]);
|
|
6906
7285
|
} catch (error) {
|
|
6907
7286
|
throw error;
|
|
6908
7287
|
}
|
|
@@ -6926,16 +7305,20 @@ function useScheduleTaskRepository() {
|
|
|
6926
7305
|
});
|
|
6927
7306
|
}
|
|
6928
7307
|
try {
|
|
6929
|
-
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
|
+
}
|
|
6930
7315
|
const res = await collection.updateOne(
|
|
6931
|
-
{ _id },
|
|
6932
|
-
|
|
7316
|
+
{ _id, status: { $ne: "deleted" } },
|
|
7317
|
+
update,
|
|
6933
7318
|
{ session }
|
|
6934
7319
|
);
|
|
6935
|
-
if (res.
|
|
6936
|
-
throw new import_node_server_utils33.
|
|
6937
|
-
"Unable to update hygiene schedule task."
|
|
6938
|
-
);
|
|
7320
|
+
if (res.matchedCount === 0) {
|
|
7321
|
+
throw new import_node_server_utils33.NotFoundError("Schedule task not found.");
|
|
6939
7322
|
}
|
|
6940
7323
|
delNamespace().then(() => {
|
|
6941
7324
|
import_node_server_utils33.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
@@ -6950,6 +7333,18 @@ function useScheduleTaskRepository() {
|
|
|
6950
7333
|
throw error;
|
|
6951
7334
|
}
|
|
6952
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
|
+
}
|
|
6953
7348
|
async function deleteScheduleTask(_id, session) {
|
|
6954
7349
|
try {
|
|
6955
7350
|
_id = new import_mongodb19.ObjectId(_id);
|
|
@@ -6989,6 +7384,7 @@ function useScheduleTaskRepository() {
|
|
|
6989
7384
|
createScheduleTask,
|
|
6990
7385
|
getScheduleTasks,
|
|
6991
7386
|
getAllScheduleTask,
|
|
7387
|
+
markScheduleTaskRan,
|
|
6992
7388
|
getScheduleTaskById,
|
|
6993
7389
|
updateScheduleTask,
|
|
6994
7390
|
deleteScheduleTask
|
|
@@ -7000,7 +7396,7 @@ var import_mongodb20 = require("mongodb");
|
|
|
7000
7396
|
var import_node_server_utils34 = require("@7365admin1/node-server-utils");
|
|
7001
7397
|
function useScheduleTaskService() {
|
|
7002
7398
|
const { createParentChecklist } = useParentChecklistRepo();
|
|
7003
|
-
const { getAllScheduleTask } = useScheduleTaskRepository();
|
|
7399
|
+
const { getAllScheduleTask, markScheduleTaskRan } = useScheduleTaskRepository();
|
|
7004
7400
|
const {
|
|
7005
7401
|
createAreaChecklist,
|
|
7006
7402
|
getAreaChecklistByAreaAndSchedule,
|
|
@@ -7009,73 +7405,7 @@ function useScheduleTaskService() {
|
|
|
7009
7405
|
const { getAreaById } = useAreaRepo();
|
|
7010
7406
|
function checkScheduleConditions(schedule, serviceType, currentDate = /* @__PURE__ */ new Date()) {
|
|
7011
7407
|
try {
|
|
7012
|
-
|
|
7013
|
-
const timeString = now.toLocaleTimeString("en-US", {
|
|
7014
|
-
hour: "2-digit",
|
|
7015
|
-
minute: "2-digit",
|
|
7016
|
-
hour12: false,
|
|
7017
|
-
timeZone: "Asia/Singapore"
|
|
7018
|
-
});
|
|
7019
|
-
const [currentHour, currentMinute] = timeString.split(":").map(Number);
|
|
7020
|
-
import_node_server_utils34.logger.info(
|
|
7021
|
-
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
|
|
7022
|
-
);
|
|
7023
|
-
const currentDateFormatted = now.toLocaleDateString("en-CA", {
|
|
7024
|
-
timeZone: "Asia/Singapore"
|
|
7025
|
-
});
|
|
7026
|
-
if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
|
|
7027
|
-
import_node_server_utils34.logger.info(`Schedule ${schedule._id}: No dates configured, skipping`);
|
|
7028
|
-
return false;
|
|
7029
|
-
}
|
|
7030
|
-
if (!schedule.dates.includes(currentDateFormatted)) {
|
|
7031
|
-
import_node_server_utils34.logger.info(
|
|
7032
|
-
`Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
|
|
7033
|
-
", "
|
|
7034
|
-
)}]`
|
|
7035
|
-
);
|
|
7036
|
-
return false;
|
|
7037
|
-
}
|
|
7038
|
-
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
7039
|
-
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
7040
|
-
if (!timeMatches) {
|
|
7041
|
-
import_node_server_utils34.logger.info(
|
|
7042
|
-
`Schedule ${schedule._id}: Time does not match. Current: ${currentHour}:${currentMinute}, Expected: ${scheduleHour}:${scheduleMinute}`
|
|
7043
|
-
);
|
|
7044
|
-
return false;
|
|
7045
|
-
}
|
|
7046
|
-
const relevantTimestamp = schedule.updatedAt || schedule.createdAt;
|
|
7047
|
-
if (relevantTimestamp) {
|
|
7048
|
-
const savedAt = new Date(relevantTimestamp);
|
|
7049
|
-
const savedHour = parseInt(
|
|
7050
|
-
savedAt.toLocaleTimeString("en-US", {
|
|
7051
|
-
hour: "2-digit",
|
|
7052
|
-
hour12: false,
|
|
7053
|
-
timeZone: "Asia/Singapore"
|
|
7054
|
-
}),
|
|
7055
|
-
10
|
|
7056
|
-
);
|
|
7057
|
-
const savedMinute = savedAt.toLocaleString("en-US", {
|
|
7058
|
-
minute: "2-digit",
|
|
7059
|
-
timeZone: "Asia/Singapore"
|
|
7060
|
-
});
|
|
7061
|
-
const savedDateFormatted = savedAt.toLocaleDateString("en-CA", {
|
|
7062
|
-
timeZone: "Asia/Singapore"
|
|
7063
|
-
});
|
|
7064
|
-
const savedMinNum = parseInt(savedMinute, 10);
|
|
7065
|
-
const wasSavedThisMinute = savedDateFormatted === currentDateFormatted && savedHour === currentHour && savedMinNum === currentMinute;
|
|
7066
|
-
if (wasSavedThisMinute) {
|
|
7067
|
-
import_node_server_utils34.logger.info(
|
|
7068
|
-
`Schedule ${schedule._id}: Skipping \u2014 task was saved this minute (${savedHour}:${String(
|
|
7069
|
-
savedMinNum
|
|
7070
|
-
).padStart(2, "0")}). Will fire on next occurrence.`
|
|
7071
|
-
);
|
|
7072
|
-
return false;
|
|
7073
|
-
}
|
|
7074
|
-
}
|
|
7075
|
-
import_node_server_utils34.logger.info(
|
|
7076
|
-
`Schedule ${schedule._id}: All conditions matched - Date is within range and time matches`
|
|
7077
|
-
);
|
|
7078
|
-
return true;
|
|
7408
|
+
return shouldRunNow(schedule, currentDate);
|
|
7079
7409
|
} catch (error) {
|
|
7080
7410
|
import_node_server_utils34.logger.error(
|
|
7081
7411
|
`Error checking schedule conditions for ${schedule._id}:`,
|
|
@@ -7084,18 +7414,22 @@ function useScheduleTaskService() {
|
|
|
7084
7414
|
return false;
|
|
7085
7415
|
}
|
|
7086
7416
|
}
|
|
7417
|
+
const MAX_ATTEMPTS = 5;
|
|
7418
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
7087
7419
|
async function processScheduledTasks(currentDate) {
|
|
7088
7420
|
try {
|
|
7089
7421
|
import_node_server_utils34.logger.info("Starting scheduled task processing...");
|
|
7090
|
-
const
|
|
7422
|
+
const today = sgtParts(currentDate ?? /* @__PURE__ */ new Date()).date;
|
|
7423
|
+
const scheduleTasks = await getAllScheduleTask(today);
|
|
7091
7424
|
if (!scheduleTasks || scheduleTasks.length === 0) {
|
|
7092
7425
|
import_node_server_utils34.logger.info("No schedule tasks found to process");
|
|
7093
|
-
return { processed: 0, validated: 0 };
|
|
7426
|
+
return { processed: 0, validated: 0, fired: [] };
|
|
7094
7427
|
}
|
|
7095
7428
|
import_node_server_utils34.logger.info(`Found ${scheduleTasks.length} schedule tasks to check`);
|
|
7096
7429
|
let processedCount = 0;
|
|
7097
7430
|
let validatedCount = 0;
|
|
7098
7431
|
const validatedTasks = [];
|
|
7432
|
+
const fired = [];
|
|
7099
7433
|
for (const scheduleTask of scheduleTasks) {
|
|
7100
7434
|
try {
|
|
7101
7435
|
const serviceType = scheduleTask.serviceType;
|
|
@@ -7138,6 +7472,8 @@ function useScheduleTaskService() {
|
|
|
7138
7472
|
import_node_server_utils34.logger.info(
|
|
7139
7473
|
`Using parent checklist ${parentChecklistId}, now creating/updating area checklists`
|
|
7140
7474
|
);
|
|
7475
|
+
let failedAreas = 0;
|
|
7476
|
+
const addedAreas = [];
|
|
7141
7477
|
for (const area of scheduleTask.areas) {
|
|
7142
7478
|
try {
|
|
7143
7479
|
const areaId = area.value.toString();
|
|
@@ -7217,6 +7553,7 @@ function useScheduleTaskService() {
|
|
|
7217
7553
|
newSets
|
|
7218
7554
|
);
|
|
7219
7555
|
if (modified > 0) {
|
|
7556
|
+
addedAreas.push(area.name);
|
|
7220
7557
|
import_node_server_utils34.logger.info(
|
|
7221
7558
|
`Appended ${areaSetConfig} set(s) starting from ${startSetNumber} to area checklist for area ${area.name}`
|
|
7222
7559
|
);
|
|
@@ -7249,13 +7586,32 @@ function useScheduleTaskService() {
|
|
|
7249
7586
|
`Area ${area.name} (${areaId}): Creating new area checklist with schedule-task sets only`
|
|
7250
7587
|
);
|
|
7251
7588
|
await createAreaChecklist(checklistData);
|
|
7589
|
+
addedAreas.push(area.name);
|
|
7252
7590
|
import_node_server_utils34.logger.info(`Created new area checklist for area ${area.name}`);
|
|
7253
7591
|
}
|
|
7254
7592
|
} catch (error) {
|
|
7255
7593
|
import_node_server_utils34.logger.error(`Error processing area ${area.name}:`, error);
|
|
7594
|
+
failedAreas++;
|
|
7256
7595
|
continue;
|
|
7257
7596
|
}
|
|
7258
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
|
+
}
|
|
7259
7615
|
processedCount++;
|
|
7260
7616
|
validatedCount++;
|
|
7261
7617
|
validatedTasks.push(scheduleTask);
|
|
@@ -7267,6 +7623,17 @@ function useScheduleTaskService() {
|
|
|
7267
7623
|
`Error processing schedule task ${scheduleTask._id}:`,
|
|
7268
7624
|
error
|
|
7269
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
|
+
}
|
|
7270
7637
|
continue;
|
|
7271
7638
|
}
|
|
7272
7639
|
}
|
|
@@ -7276,7 +7643,8 @@ function useScheduleTaskService() {
|
|
|
7276
7643
|
return {
|
|
7277
7644
|
processed: processedCount,
|
|
7278
7645
|
validated: validatedCount,
|
|
7279
|
-
tasks: validatedTasks
|
|
7646
|
+
tasks: validatedTasks,
|
|
7647
|
+
fired
|
|
7280
7648
|
};
|
|
7281
7649
|
} catch (error) {
|
|
7282
7650
|
import_node_server_utils34.logger.error("Error processing scheduled tasks:", error);
|
|
@@ -7287,7 +7655,7 @@ function useScheduleTaskService() {
|
|
|
7287
7655
|
}
|
|
7288
7656
|
|
|
7289
7657
|
// src/controllers/hygiene-schedule-task.controller.ts
|
|
7290
|
-
var
|
|
7658
|
+
var import_joi19 = __toESM(require("joi"));
|
|
7291
7659
|
var import_node_server_utils35 = require("@7365admin1/node-server-utils");
|
|
7292
7660
|
var import_core20 = require("@7365admin1/core");
|
|
7293
7661
|
function useScheduleTaskController() {
|
|
@@ -7318,12 +7686,12 @@ function useScheduleTaskController() {
|
|
|
7318
7686
|
}
|
|
7319
7687
|
async function getScheduleTasks(req, res, next) {
|
|
7320
7688
|
const query = { ...req.query, ...req.params };
|
|
7321
|
-
const validation =
|
|
7322
|
-
page:
|
|
7323
|
-
limit:
|
|
7324
|
-
search:
|
|
7325
|
-
site:
|
|
7326
|
-
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()
|
|
7327
7695
|
});
|
|
7328
7696
|
const { error } = validation.validate(query);
|
|
7329
7697
|
if (error) {
|
|
@@ -7331,8 +7699,8 @@ function useScheduleTaskController() {
|
|
|
7331
7699
|
next(new import_node_server_utils35.BadRequestError(error.message));
|
|
7332
7700
|
return;
|
|
7333
7701
|
}
|
|
7334
|
-
const page = parseInt(req.query.page)
|
|
7335
|
-
const limit = parseInt(req.query.limit)
|
|
7702
|
+
const page = parseInt(req.query.page) || 1;
|
|
7703
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
7336
7704
|
const search = req.query.search ?? "";
|
|
7337
7705
|
const site = req.params.site ?? "";
|
|
7338
7706
|
const serviceType = req.query.serviceType ?? "";
|
|
@@ -7353,7 +7721,7 @@ function useScheduleTaskController() {
|
|
|
7353
7721
|
}
|
|
7354
7722
|
}
|
|
7355
7723
|
async function getScheduleTaskById(req, res, next) {
|
|
7356
|
-
const validation =
|
|
7724
|
+
const validation = import_joi19.default.string().hex().required();
|
|
7357
7725
|
const _id = req.params.id;
|
|
7358
7726
|
const { error, value } = validation.validate(_id);
|
|
7359
7727
|
if (error) {
|
|
@@ -7373,20 +7741,17 @@ function useScheduleTaskController() {
|
|
|
7373
7741
|
}
|
|
7374
7742
|
async function updateScheduleTask(req, res, next) {
|
|
7375
7743
|
const payload = { id: req.params.id, ...req.body };
|
|
7376
|
-
const
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
)
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
value: import_joi17.default.any().required()
|
|
7388
|
-
})
|
|
7389
|
-
).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
|
|
7390
7755
|
});
|
|
7391
7756
|
const { error } = validation.validate(payload);
|
|
7392
7757
|
if (error) {
|
|
@@ -7394,6 +7759,24 @@ function useScheduleTaskController() {
|
|
|
7394
7759
|
next(new import_node_server_utils35.BadRequestError(error.message));
|
|
7395
7760
|
return;
|
|
7396
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
|
+
}
|
|
7397
7780
|
try {
|
|
7398
7781
|
const { id, ...value } = payload;
|
|
7399
7782
|
await _updateScheduleTask(id, value);
|
|
@@ -7406,7 +7789,7 @@ function useScheduleTaskController() {
|
|
|
7406
7789
|
}
|
|
7407
7790
|
}
|
|
7408
7791
|
async function deleteScheduleTask(req, res, next) {
|
|
7409
|
-
const validation =
|
|
7792
|
+
const validation = import_joi19.default.string().hex().required();
|
|
7410
7793
|
const _id = req.params.id;
|
|
7411
7794
|
const { error, value } = validation.validate(_id);
|
|
7412
7795
|
if (error) {
|
|
@@ -7650,17 +8033,17 @@ function useQRService() {
|
|
|
7650
8033
|
}
|
|
7651
8034
|
|
|
7652
8035
|
// src/controllers/hygiene-qr.controller.ts
|
|
7653
|
-
var
|
|
8036
|
+
var import_joi20 = __toESM(require("joi"));
|
|
7654
8037
|
var import_node_server_utils37 = require("@7365admin1/node-server-utils");
|
|
7655
8038
|
function useQRController() {
|
|
7656
8039
|
const { generateQRImage: _generateQRImage, generateQRPDF: _generateQRPDF } = useQRService();
|
|
7657
8040
|
async function generateQR(req, res, next) {
|
|
7658
|
-
const validation =
|
|
7659
|
-
url:
|
|
7660
|
-
filename:
|
|
7661
|
-
title:
|
|
7662
|
-
subtitle:
|
|
7663
|
-
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)
|
|
7664
8047
|
});
|
|
7665
8048
|
const query = { ...req.query };
|
|
7666
8049
|
const { error, value } = validation.validate(query);
|