@7365admin1/module-hygiene 4.3.0 → 4.5.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 +12 -0
- package/dist/index.d.ts +43 -1
- package/dist/index.js +1235 -705
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1076 -550
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
|
@@ -1014,12 +1014,294 @@ function useAreaRepo() {
|
|
|
1014
1014
|
|
|
1015
1015
|
// src/services/hygiene-area.service.ts
|
|
1016
1016
|
import {
|
|
1017
|
-
BadRequestError as
|
|
1018
|
-
logger as
|
|
1017
|
+
BadRequestError as BadRequestError7,
|
|
1018
|
+
logger as logger8,
|
|
1019
1019
|
NotFoundError as NotFoundError2
|
|
1020
1020
|
} from "@7365admin1/node-server-utils";
|
|
1021
|
+
|
|
1022
|
+
// src/services/hygiene-area-export.service.ts
|
|
1023
|
+
import { logger as logger5 } from "@7365admin1/node-server-utils";
|
|
1024
|
+
import * as xlsx from "xlsx";
|
|
1025
|
+
function useAreaExportService() {
|
|
1026
|
+
async function generateAreaExcel(areas) {
|
|
1027
|
+
try {
|
|
1028
|
+
const rows = [];
|
|
1029
|
+
rows.push(["AREA", "TYPE", "SET", "UNITS"]);
|
|
1030
|
+
for (const area of areas) {
|
|
1031
|
+
const areaName = area.name || "";
|
|
1032
|
+
const areaType = area.type || "";
|
|
1033
|
+
const areaSet = String(area.set || 0);
|
|
1034
|
+
const unitsString = area.units && area.units.length > 0 ? area.units.map((u) => u.name || "").join(", ") : "";
|
|
1035
|
+
rows.push([areaName, areaType, areaSet, unitsString]);
|
|
1036
|
+
}
|
|
1037
|
+
const workbook = xlsx.utils.book_new();
|
|
1038
|
+
const worksheet = xlsx.utils.aoa_to_sheet(rows);
|
|
1039
|
+
worksheet["!cols"] = [
|
|
1040
|
+
{ width: 30 },
|
|
1041
|
+
// AREA
|
|
1042
|
+
{ width: 20 },
|
|
1043
|
+
// TYPE
|
|
1044
|
+
{ width: 10 },
|
|
1045
|
+
// SET
|
|
1046
|
+
{ width: 40 }
|
|
1047
|
+
// UNITS
|
|
1048
|
+
];
|
|
1049
|
+
xlsx.utils.book_append_sheet(workbook, worksheet, "Areas");
|
|
1050
|
+
const excelBuffer = xlsx.write(workbook, {
|
|
1051
|
+
type: "buffer",
|
|
1052
|
+
bookType: "xlsx"
|
|
1053
|
+
});
|
|
1054
|
+
return excelBuffer;
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
logger5.log({
|
|
1057
|
+
level: "error",
|
|
1058
|
+
message: `Failed to generate area Excel: ${error.message}`
|
|
1059
|
+
});
|
|
1060
|
+
throw error;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return {
|
|
1064
|
+
generateAreaExcel
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// src/repositories/hygiene-unit.repository.ts
|
|
1069
|
+
import { ObjectId as ObjectId5 } from "mongodb";
|
|
1070
|
+
import {
|
|
1071
|
+
useAtlas as useAtlas3,
|
|
1072
|
+
InternalServerError as InternalServerError3,
|
|
1073
|
+
paginate as paginate2,
|
|
1074
|
+
BadRequestError as BadRequestError6,
|
|
1075
|
+
useCache as useCache3,
|
|
1076
|
+
logger as logger7,
|
|
1077
|
+
makeCacheKey as makeCacheKey3
|
|
1078
|
+
} from "@7365admin1/node-server-utils";
|
|
1079
|
+
|
|
1080
|
+
// src/models/hygiene-unit.model.ts
|
|
1081
|
+
import Joi3 from "joi";
|
|
1082
|
+
import { ObjectId as ObjectId4 } from "mongodb";
|
|
1083
|
+
import { BadRequestError as BadRequestError5, logger as logger6 } from "@7365admin1/node-server-utils";
|
|
1084
|
+
var unitSchema = Joi3.object({
|
|
1085
|
+
site: Joi3.string().hex().required(),
|
|
1086
|
+
name: Joi3.string().required()
|
|
1087
|
+
});
|
|
1088
|
+
function MUnit(value) {
|
|
1089
|
+
const { error } = unitSchema.validate(value);
|
|
1090
|
+
if (error) {
|
|
1091
|
+
logger6.info(`Hygiene Unit Model: ${error.message}`);
|
|
1092
|
+
throw new BadRequestError5(error.message);
|
|
1093
|
+
}
|
|
1094
|
+
if (value.site) {
|
|
1095
|
+
try {
|
|
1096
|
+
value.site = new ObjectId4(value.site);
|
|
1097
|
+
} catch (error2) {
|
|
1098
|
+
throw new BadRequestError5("Invalid site ID format.");
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return {
|
|
1102
|
+
site: value.site,
|
|
1103
|
+
name: value.name,
|
|
1104
|
+
status: "active",
|
|
1105
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1106
|
+
updatedAt: "",
|
|
1107
|
+
deletedAt: ""
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/repositories/hygiene-unit.repository.ts
|
|
1112
|
+
function useUnitRepository() {
|
|
1113
|
+
const db = useAtlas3.getDb();
|
|
1114
|
+
if (!db) {
|
|
1115
|
+
throw new InternalServerError3("Unable to connect to server.");
|
|
1116
|
+
}
|
|
1117
|
+
const namespace_collection = "site.cleaning.area.unit";
|
|
1118
|
+
const collection = db.collection(namespace_collection);
|
|
1119
|
+
const { delNamespace, setCache, getCache } = useCache3(namespace_collection);
|
|
1120
|
+
async function createIndex() {
|
|
1121
|
+
try {
|
|
1122
|
+
await collection.createIndexes([
|
|
1123
|
+
{ key: { site: 1 } },
|
|
1124
|
+
{ key: { status: 1 } }
|
|
1125
|
+
]);
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
throw new InternalServerError3("Failed to create index on hygiene unit.");
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
async function createTextIndex() {
|
|
1131
|
+
try {
|
|
1132
|
+
await collection.createIndex({ name: "text" });
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
throw new InternalServerError3(
|
|
1135
|
+
"Failed to create text index on hygiene unit."
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async function createUniqueIndex() {
|
|
1140
|
+
try {
|
|
1141
|
+
await collection.createIndex(
|
|
1142
|
+
{ site: 1, name: 1, deletedAt: 1 },
|
|
1143
|
+
{ unique: true }
|
|
1144
|
+
);
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
throw new InternalServerError3(
|
|
1147
|
+
"Failed to create unique index on hygiene unit."
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
async function createUnit(value, session) {
|
|
1152
|
+
try {
|
|
1153
|
+
value = MUnit(value);
|
|
1154
|
+
const res = await collection.insertOne(value, { session });
|
|
1155
|
+
delNamespace().then(() => {
|
|
1156
|
+
logger7.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1157
|
+
}).catch((err) => {
|
|
1158
|
+
logger7.error(
|
|
1159
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1160
|
+
err
|
|
1161
|
+
);
|
|
1162
|
+
});
|
|
1163
|
+
return res.insertedId;
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
1166
|
+
if (isDuplicated) {
|
|
1167
|
+
throw new BadRequestError6("Unit already exists.");
|
|
1168
|
+
}
|
|
1169
|
+
throw error;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
async function getUnits({
|
|
1173
|
+
page = 1,
|
|
1174
|
+
limit = 10,
|
|
1175
|
+
search = "",
|
|
1176
|
+
site
|
|
1177
|
+
}) {
|
|
1178
|
+
page = page > 0 ? page - 1 : 0;
|
|
1179
|
+
const query = {
|
|
1180
|
+
status: { $ne: "deleted" }
|
|
1181
|
+
};
|
|
1182
|
+
const cacheOptions = {
|
|
1183
|
+
page,
|
|
1184
|
+
limit
|
|
1185
|
+
};
|
|
1186
|
+
try {
|
|
1187
|
+
site = new ObjectId5(site);
|
|
1188
|
+
query.site = site;
|
|
1189
|
+
cacheOptions.site = site.toString();
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
throw new BadRequestError6("Invalid site ID format.");
|
|
1192
|
+
}
|
|
1193
|
+
if (search) {
|
|
1194
|
+
query.$or = [{ name: { $regex: search, $options: "i" } }];
|
|
1195
|
+
cacheOptions.search = search;
|
|
1196
|
+
}
|
|
1197
|
+
const cacheKey = makeCacheKey3(namespace_collection, cacheOptions);
|
|
1198
|
+
const cachedData = await getCache(cacheKey);
|
|
1199
|
+
if (cachedData) {
|
|
1200
|
+
logger7.info(`Cache hit for key: ${cacheKey}`);
|
|
1201
|
+
return cachedData;
|
|
1202
|
+
}
|
|
1203
|
+
try {
|
|
1204
|
+
const items = await collection.aggregate([
|
|
1205
|
+
{ $match: query },
|
|
1206
|
+
{ $project: { name: 1 } },
|
|
1207
|
+
{ $sort: { _id: -1 } },
|
|
1208
|
+
{ $skip: page * limit },
|
|
1209
|
+
{ $limit: limit }
|
|
1210
|
+
]).toArray();
|
|
1211
|
+
const length = await collection.countDocuments(query);
|
|
1212
|
+
const data = paginate2(items, page, limit, length);
|
|
1213
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
1214
|
+
logger7.info(`Cache set for key: ${cacheKey}`);
|
|
1215
|
+
}).catch((err) => {
|
|
1216
|
+
logger7.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
1217
|
+
});
|
|
1218
|
+
return data;
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
throw error;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
async function updateUnit(_id, value, session) {
|
|
1224
|
+
try {
|
|
1225
|
+
_id = new ObjectId5(_id);
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
throw new BadRequestError6("Invalid unit ID format.");
|
|
1228
|
+
}
|
|
1229
|
+
try {
|
|
1230
|
+
const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
|
|
1231
|
+
const res = await collection.updateOne(
|
|
1232
|
+
{ _id },
|
|
1233
|
+
{ $set: updateValue },
|
|
1234
|
+
{ session }
|
|
1235
|
+
);
|
|
1236
|
+
if (res.modifiedCount === 0) {
|
|
1237
|
+
throw new InternalServerError3("Unable to update cleaning unit.");
|
|
1238
|
+
}
|
|
1239
|
+
delNamespace().then(() => {
|
|
1240
|
+
logger7.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1241
|
+
}).catch((err) => {
|
|
1242
|
+
logger7.error(
|
|
1243
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1244
|
+
err
|
|
1245
|
+
);
|
|
1246
|
+
});
|
|
1247
|
+
return res.modifiedCount;
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
1250
|
+
if (isDuplicated) {
|
|
1251
|
+
throw new BadRequestError6("Area already exists.");
|
|
1252
|
+
}
|
|
1253
|
+
throw error;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async function deleteUnit(_id, session) {
|
|
1257
|
+
try {
|
|
1258
|
+
_id = new ObjectId5(_id);
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
throw new BadRequestError6("Invalid unit ID format.");
|
|
1261
|
+
}
|
|
1262
|
+
try {
|
|
1263
|
+
const updateValue = {
|
|
1264
|
+
status: "deleted",
|
|
1265
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
1266
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
1267
|
+
};
|
|
1268
|
+
const res = await collection.updateOne(
|
|
1269
|
+
{ _id },
|
|
1270
|
+
{ $set: updateValue },
|
|
1271
|
+
{ session }
|
|
1272
|
+
);
|
|
1273
|
+
if (res.modifiedCount === 0) {
|
|
1274
|
+
throw new InternalServerError3("Unable to delete unit.");
|
|
1275
|
+
}
|
|
1276
|
+
delNamespace().then(() => {
|
|
1277
|
+
logger7.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1278
|
+
}).catch((err) => {
|
|
1279
|
+
logger7.error(
|
|
1280
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1281
|
+
err
|
|
1282
|
+
);
|
|
1283
|
+
});
|
|
1284
|
+
return res.modifiedCount;
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
throw error;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return {
|
|
1290
|
+
createIndex,
|
|
1291
|
+
createTextIndex,
|
|
1292
|
+
createUniqueIndex,
|
|
1293
|
+
createUnit,
|
|
1294
|
+
getUnits,
|
|
1295
|
+
updateUnit,
|
|
1296
|
+
deleteUnit
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// src/services/hygiene-area.service.ts
|
|
1021
1301
|
function useAreaService() {
|
|
1022
|
-
const { createArea: _createArea } = useAreaRepo();
|
|
1302
|
+
const { createArea: _createArea, getAreasForChecklist } = useAreaRepo();
|
|
1303
|
+
const { generateAreaExcel } = useAreaExportService();
|
|
1304
|
+
const { getUnits: _getUnits } = useUnitRepository();
|
|
1023
1305
|
async function importArea({
|
|
1024
1306
|
dataJson,
|
|
1025
1307
|
site
|
|
@@ -1028,11 +1310,25 @@ function useAreaService() {
|
|
|
1028
1310
|
try {
|
|
1029
1311
|
dataArray = JSON.parse(dataJson);
|
|
1030
1312
|
} catch (error) {
|
|
1031
|
-
throw new
|
|
1313
|
+
throw new BadRequestError7("Invalid JSON format for data in excel");
|
|
1032
1314
|
}
|
|
1033
1315
|
if (!dataArray || dataArray.length === 0) {
|
|
1034
1316
|
throw new NotFoundError2("No data found in the uploaded file");
|
|
1035
1317
|
}
|
|
1318
|
+
let availableUnits = [];
|
|
1319
|
+
try {
|
|
1320
|
+
const unitsData = await _getUnits({
|
|
1321
|
+
page: 1,
|
|
1322
|
+
limit: 999999,
|
|
1323
|
+
search: "",
|
|
1324
|
+
site
|
|
1325
|
+
});
|
|
1326
|
+
if (unitsData && unitsData.items) {
|
|
1327
|
+
availableUnits = unitsData.items;
|
|
1328
|
+
}
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
logger8.warn(`Failed to fetch units for site: ${error}`);
|
|
1331
|
+
}
|
|
1036
1332
|
const insertedAreaIds = [];
|
|
1037
1333
|
const duplicateAreas = [];
|
|
1038
1334
|
const failedAreas = [];
|
|
@@ -1041,37 +1337,74 @@ function useAreaService() {
|
|
|
1041
1337
|
for (let i = 0; i < dataArray.length; i++) {
|
|
1042
1338
|
const row = dataArray[i];
|
|
1043
1339
|
if (!row?.TYPE) {
|
|
1044
|
-
|
|
1340
|
+
logger8.warn(`Skipping row ${i + 1} with missing TYPE:`, row);
|
|
1045
1341
|
skippedRows.push(i + 1);
|
|
1046
1342
|
continue;
|
|
1047
1343
|
}
|
|
1048
1344
|
const areaType = String(row.TYPE).trim().toLowerCase();
|
|
1049
1345
|
if (!areaType) {
|
|
1050
|
-
|
|
1346
|
+
logger8.warn(`Skipping row ${i + 1} with empty area type`);
|
|
1051
1347
|
skippedRows.push(i + 1);
|
|
1052
1348
|
continue;
|
|
1053
1349
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1350
|
+
const areaNameValue = row?.NAME || row?.AREA;
|
|
1351
|
+
if (!areaNameValue) {
|
|
1352
|
+
logger8.warn(`Skipping row ${i + 1} with missing NAME/AREA:`, row);
|
|
1056
1353
|
skippedRows.push(i + 1);
|
|
1057
1354
|
continue;
|
|
1058
1355
|
}
|
|
1059
|
-
const areaName = String(
|
|
1356
|
+
const areaName = String(areaNameValue).trim();
|
|
1060
1357
|
if (!areaName) {
|
|
1061
|
-
|
|
1358
|
+
logger8.warn(`Skipping row ${i + 1} with empty ${areaType} area name`);
|
|
1359
|
+
skippedRows.push(i + 1);
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
if (areaName.startsWith("Sample:")) {
|
|
1363
|
+
logger8.warn(`Skipping row ${i + 1} with sample area: ${areaName}`);
|
|
1062
1364
|
skippedRows.push(i + 1);
|
|
1063
1365
|
continue;
|
|
1064
1366
|
}
|
|
1065
1367
|
try {
|
|
1066
|
-
const
|
|
1368
|
+
const areaData = {
|
|
1067
1369
|
type: areaType,
|
|
1068
1370
|
name: areaName,
|
|
1069
1371
|
site
|
|
1070
|
-
}
|
|
1372
|
+
};
|
|
1373
|
+
if (row.SET !== void 0 && row.SET !== null && row.SET !== "") {
|
|
1374
|
+
const setNumber = parseInt(String(row.SET).trim());
|
|
1375
|
+
if (!isNaN(setNumber) && setNumber >= 0) {
|
|
1376
|
+
areaData.set = setNumber;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (row.UNITS && availableUnits.length > 0) {
|
|
1380
|
+
const unitNames = String(row.UNITS).split(",").map((u) => u.trim()).filter((u) => u);
|
|
1381
|
+
if (unitNames.length > 0) {
|
|
1382
|
+
const areaUnits = [];
|
|
1383
|
+
for (const unitName of unitNames) {
|
|
1384
|
+
const foundUnit = availableUnits.find(
|
|
1385
|
+
(u) => u.name.toLowerCase() === unitName.toLowerCase()
|
|
1386
|
+
);
|
|
1387
|
+
if (foundUnit) {
|
|
1388
|
+
areaUnits.push({
|
|
1389
|
+
unit: foundUnit._id,
|
|
1390
|
+
name: foundUnit.name
|
|
1391
|
+
});
|
|
1392
|
+
} else {
|
|
1393
|
+
logger8.warn(
|
|
1394
|
+
`Unit "${unitName}" not found in site for area "${areaName}"`
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
if (areaUnits.length > 0) {
|
|
1399
|
+
areaData.units = areaUnits;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const insertedId = await _createArea(areaData);
|
|
1071
1404
|
insertedAreaIds.push(insertedId);
|
|
1072
|
-
|
|
1405
|
+
logger8.info(`Successfully created ${areaType} area: ${areaName}`);
|
|
1073
1406
|
} catch (error) {
|
|
1074
|
-
|
|
1407
|
+
logger8.error(
|
|
1075
1408
|
`Error creating ${areaType} area "${areaName}": ${error.message}`
|
|
1076
1409
|
);
|
|
1077
1410
|
if (error.message.includes("duplicate")) {
|
|
@@ -1091,18 +1424,18 @@ function useAreaService() {
|
|
|
1091
1424
|
if (skippedRows.length > 0) {
|
|
1092
1425
|
message += `, ${skippedRows.length} rows skipped (invalid data)`;
|
|
1093
1426
|
}
|
|
1094
|
-
|
|
1427
|
+
logger8.info(message);
|
|
1095
1428
|
if (insertedAreaIds.length === 0) {
|
|
1096
1429
|
if (duplicateAreas.length > 0 && failedAreas.length === 0 && skippedRows.length === 0) {
|
|
1097
1430
|
return {
|
|
1098
1431
|
message: `No new areas were created. All ${duplicateAreas.length} areas already exist in the system: ${duplicateAreas.join(", ")}`
|
|
1099
1432
|
};
|
|
1100
1433
|
} else if (failedAreas.length > 0) {
|
|
1101
|
-
throw new
|
|
1434
|
+
throw new BadRequestError7(
|
|
1102
1435
|
`No areas were created. ${failedAreas.length} areas failed due to errors. Please check your data format and ensure area names are valid.`
|
|
1103
1436
|
);
|
|
1104
1437
|
} else if (skippedRows.length > 0 && duplicateAreas.length === 0) {
|
|
1105
|
-
throw new
|
|
1438
|
+
throw new BadRequestError7(
|
|
1106
1439
|
`No areas were created. All ${skippedRows.length} rows contained invalid or missing data.`
|
|
1107
1440
|
);
|
|
1108
1441
|
} else {
|
|
@@ -1113,30 +1446,46 @@ function useAreaService() {
|
|
|
1113
1446
|
}
|
|
1114
1447
|
return { message };
|
|
1115
1448
|
} catch (error) {
|
|
1116
|
-
|
|
1117
|
-
if (error instanceof
|
|
1449
|
+
logger8.error("Error while uploading area information", error);
|
|
1450
|
+
if (error instanceof BadRequestError7) {
|
|
1118
1451
|
throw error;
|
|
1119
1452
|
} else if (error.message.includes("validation")) {
|
|
1120
|
-
throw new
|
|
1453
|
+
throw new BadRequestError7(
|
|
1121
1454
|
"Upload failed due to invalid data format. Please check that all required fields are properly filled."
|
|
1122
1455
|
);
|
|
1123
1456
|
} else {
|
|
1124
|
-
throw new
|
|
1457
|
+
throw new BadRequestError7(
|
|
1125
1458
|
`Upload failed: ${error.message || "Please check your data format and try again."}`
|
|
1126
1459
|
);
|
|
1127
1460
|
}
|
|
1128
1461
|
}
|
|
1129
1462
|
}
|
|
1130
|
-
|
|
1463
|
+
async function exportAreas(site) {
|
|
1464
|
+
try {
|
|
1465
|
+
const areas = await getAreasForChecklist(site);
|
|
1466
|
+
if (!areas || !Array.isArray(areas) || areas.length === 0) {
|
|
1467
|
+
throw new BadRequestError7("No data found to export");
|
|
1468
|
+
}
|
|
1469
|
+
const excelBuffer = await generateAreaExcel(areas);
|
|
1470
|
+
if (!excelBuffer || excelBuffer.length === 0) {
|
|
1471
|
+
throw new Error("Generated Excel file is empty or invalid.");
|
|
1472
|
+
}
|
|
1473
|
+
return excelBuffer;
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
logger8.error(`Error downloading areas: ${error.message}`);
|
|
1476
|
+
throw error;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return { importArea, exportAreas };
|
|
1131
1480
|
}
|
|
1132
1481
|
|
|
1133
1482
|
// src/controllers/hygiene-area.controller.ts
|
|
1134
|
-
import
|
|
1135
|
-
import { BadRequestError as
|
|
1483
|
+
import Joi4 from "joi";
|
|
1484
|
+
import { BadRequestError as BadRequestError8, logger as logger9 } from "@7365admin1/node-server-utils";
|
|
1136
1485
|
|
|
1137
1486
|
// src/utils/convert-excel.util.ts
|
|
1138
1487
|
import { Readable } from "stream";
|
|
1139
|
-
import * as
|
|
1488
|
+
import * as xlsx2 from "xlsx";
|
|
1140
1489
|
function convertBufferFile(bufferFile) {
|
|
1141
1490
|
return new Promise((resolve, reject) => {
|
|
1142
1491
|
const fileStream = Readable.from(bufferFile);
|
|
@@ -1146,10 +1495,10 @@ function convertBufferFile(bufferFile) {
|
|
|
1146
1495
|
});
|
|
1147
1496
|
fileStream.on("end", () => {
|
|
1148
1497
|
try {
|
|
1149
|
-
const workbook =
|
|
1498
|
+
const workbook = xlsx2.read(fileBuffer, { type: "buffer" });
|
|
1150
1499
|
const sheetName = workbook.SheetNames[0];
|
|
1151
1500
|
const sheet = workbook.Sheets[sheetName];
|
|
1152
|
-
const jsonData =
|
|
1501
|
+
const jsonData = xlsx2.utils.sheet_to_json(sheet);
|
|
1153
1502
|
resolve(jsonData);
|
|
1154
1503
|
} catch (error) {
|
|
1155
1504
|
reject("Error parsing file");
|
|
@@ -1170,13 +1519,13 @@ function useAreaController() {
|
|
|
1170
1519
|
updateArea: _updateArea,
|
|
1171
1520
|
deleteArea: _deleteById
|
|
1172
1521
|
} = useAreaRepo();
|
|
1173
|
-
const { importArea: _importArea } = useAreaService();
|
|
1522
|
+
const { importArea: _importArea, exportAreas: _exportAreas } = useAreaService();
|
|
1174
1523
|
async function createArea(req, res, next) {
|
|
1175
1524
|
const payload = { ...req.body, ...req.params };
|
|
1176
1525
|
const { error } = areaSchema.validate(payload);
|
|
1177
1526
|
if (error) {
|
|
1178
|
-
|
|
1179
|
-
next(new
|
|
1527
|
+
logger9.log({ level: "error", message: error.message });
|
|
1528
|
+
next(new BadRequestError8(error.message));
|
|
1180
1529
|
return;
|
|
1181
1530
|
}
|
|
1182
1531
|
try {
|
|
@@ -1184,24 +1533,24 @@ function useAreaController() {
|
|
|
1184
1533
|
res.status(201).json({ message: "Area created successfully.", id });
|
|
1185
1534
|
return;
|
|
1186
1535
|
} catch (error2) {
|
|
1187
|
-
|
|
1536
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1188
1537
|
next(error2);
|
|
1189
1538
|
return;
|
|
1190
1539
|
}
|
|
1191
1540
|
}
|
|
1192
1541
|
async function getAreas(req, res, next) {
|
|
1193
1542
|
const query = { ...req.query, ...req.params };
|
|
1194
|
-
const validation =
|
|
1195
|
-
page:
|
|
1196
|
-
limit:
|
|
1197
|
-
search:
|
|
1198
|
-
type:
|
|
1199
|
-
site:
|
|
1543
|
+
const validation = Joi4.object({
|
|
1544
|
+
page: Joi4.number().min(1).optional().allow("", null),
|
|
1545
|
+
limit: Joi4.number().min(1).optional().allow("", null),
|
|
1546
|
+
search: Joi4.string().optional().allow("", null),
|
|
1547
|
+
type: Joi4.string().valid("all", ...allowedTypes).optional().allow("", null),
|
|
1548
|
+
site: Joi4.string().hex().required()
|
|
1200
1549
|
});
|
|
1201
1550
|
const { error } = validation.validate(query);
|
|
1202
1551
|
if (error) {
|
|
1203
|
-
|
|
1204
|
-
next(new
|
|
1552
|
+
logger9.log({ level: "error", message: error.message });
|
|
1553
|
+
next(new BadRequestError8(error.message));
|
|
1205
1554
|
return;
|
|
1206
1555
|
}
|
|
1207
1556
|
const page = parseInt(req.query.page) ?? 1;
|
|
@@ -1225,12 +1574,12 @@ function useAreaController() {
|
|
|
1225
1574
|
}
|
|
1226
1575
|
}
|
|
1227
1576
|
async function getAreaById(req, res, next) {
|
|
1228
|
-
const validation =
|
|
1577
|
+
const validation = Joi4.string().hex().required();
|
|
1229
1578
|
const _id = req.params.id;
|
|
1230
1579
|
const { error, value } = validation.validate(_id);
|
|
1231
1580
|
if (error) {
|
|
1232
|
-
|
|
1233
|
-
next(new
|
|
1581
|
+
logger9.log({ level: "error", message: error.message });
|
|
1582
|
+
next(new BadRequestError8(error.message));
|
|
1234
1583
|
return;
|
|
1235
1584
|
}
|
|
1236
1585
|
try {
|
|
@@ -1238,22 +1587,22 @@ function useAreaController() {
|
|
|
1238
1587
|
res.json(data);
|
|
1239
1588
|
return;
|
|
1240
1589
|
} catch (error2) {
|
|
1241
|
-
|
|
1590
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1242
1591
|
next(error2);
|
|
1243
1592
|
return;
|
|
1244
1593
|
}
|
|
1245
1594
|
}
|
|
1246
1595
|
async function updateArea(req, res, next) {
|
|
1247
1596
|
const payload = { id: req.params.id, ...req.body };
|
|
1248
|
-
const schema =
|
|
1249
|
-
id:
|
|
1250
|
-
name:
|
|
1251
|
-
type:
|
|
1252
|
-
set:
|
|
1253
|
-
units:
|
|
1254
|
-
|
|
1255
|
-
unit:
|
|
1256
|
-
name:
|
|
1597
|
+
const schema = Joi4.object({
|
|
1598
|
+
id: Joi4.string().hex().required(),
|
|
1599
|
+
name: Joi4.string().optional().allow("", null),
|
|
1600
|
+
type: Joi4.string().optional().allow("", null, ...allowedTypes),
|
|
1601
|
+
set: Joi4.number().min(0).optional(),
|
|
1602
|
+
units: Joi4.array().items(
|
|
1603
|
+
Joi4.object({
|
|
1604
|
+
unit: Joi4.string().hex().required(),
|
|
1605
|
+
name: Joi4.string().required()
|
|
1257
1606
|
}).required()
|
|
1258
1607
|
).min(1).unique("unit", { ignoreUndefined: true }).optional().messages({
|
|
1259
1608
|
"array.unique": "Duplicate area units are not allowed"
|
|
@@ -1261,8 +1610,8 @@ function useAreaController() {
|
|
|
1261
1610
|
});
|
|
1262
1611
|
const { error } = schema.validate(payload);
|
|
1263
1612
|
if (error) {
|
|
1264
|
-
|
|
1265
|
-
next(new
|
|
1613
|
+
logger9.log({ level: "error", message: error.message });
|
|
1614
|
+
next(new BadRequestError8(error.message));
|
|
1266
1615
|
return;
|
|
1267
1616
|
}
|
|
1268
1617
|
try {
|
|
@@ -1271,299 +1620,140 @@ function useAreaController() {
|
|
|
1271
1620
|
res.json({ message: "Area updated successfully." });
|
|
1272
1621
|
return;
|
|
1273
1622
|
} catch (error2) {
|
|
1274
|
-
|
|
1623
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1275
1624
|
next(error2);
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
async function deleteArea(req, res, next) {
|
|
1280
|
-
const id = req.params.id;
|
|
1281
|
-
const validation = Joi3.string().hex().required();
|
|
1282
|
-
const { error, value } = validation.validate(id);
|
|
1283
|
-
if (error) {
|
|
1284
|
-
logger6.log({ level: "error", message: error.message });
|
|
1285
|
-
next(new BadRequestError6(error.message));
|
|
1286
|
-
return;
|
|
1287
|
-
}
|
|
1288
|
-
try {
|
|
1289
|
-
await _deleteById(value);
|
|
1290
|
-
res.json({ message: "Area deleted successfully." });
|
|
1291
|
-
return;
|
|
1292
|
-
} catch (error2) {
|
|
1293
|
-
logger6.log({ level: "error", message: error2.message });
|
|
1294
|
-
next(error2);
|
|
1295
|
-
return;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
async function importArea(req, res, next) {
|
|
1299
|
-
if (!req.file) {
|
|
1300
|
-
next(new BadRequestError6("File is required!"));
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
const { site } = req.params;
|
|
1304
|
-
const schema = Joi3.string().hex().required();
|
|
1305
|
-
const { error, value } = schema.validate(site);
|
|
1306
|
-
if (error) {
|
|
1307
|
-
logger6.log({ level: "error", message: error.message });
|
|
1308
|
-
next(new BadRequestError6(error.message));
|
|
1309
|
-
return;
|
|
1310
|
-
}
|
|
1311
|
-
try {
|
|
1312
|
-
const xlsData = await convertBufferFile(req.file.buffer);
|
|
1313
|
-
const dataJson = JSON.stringify(xlsData);
|
|
1314
|
-
const result = await _importArea({ dataJson, site: value });
|
|
1315
|
-
return res.status(201).json(result);
|
|
1316
|
-
} catch (error2) {
|
|
1317
|
-
logger6.log({ level: "error", message: error2.message });
|
|
1318
|
-
next(error2);
|
|
1319
|
-
return;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
return {
|
|
1323
|
-
createArea,
|
|
1324
|
-
getAreas,
|
|
1325
|
-
getAreaById,
|
|
1326
|
-
updateArea,
|
|
1327
|
-
deleteArea,
|
|
1328
|
-
importArea
|
|
1329
|
-
};
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
// src/models/hygiene-unit.model.ts
|
|
1333
|
-
import Joi4 from "joi";
|
|
1334
|
-
import { ObjectId as ObjectId4 } from "mongodb";
|
|
1335
|
-
import { BadRequestError as BadRequestError7, logger as logger7 } from "@7365admin1/node-server-utils";
|
|
1336
|
-
var unitSchema = Joi4.object({
|
|
1337
|
-
site: Joi4.string().hex().required(),
|
|
1338
|
-
name: Joi4.string().required()
|
|
1339
|
-
});
|
|
1340
|
-
function MUnit(value) {
|
|
1341
|
-
const { error } = unitSchema.validate(value);
|
|
1342
|
-
if (error) {
|
|
1343
|
-
logger7.info(`Hygiene Unit Model: ${error.message}`);
|
|
1344
|
-
throw new BadRequestError7(error.message);
|
|
1345
|
-
}
|
|
1346
|
-
if (value.site) {
|
|
1347
|
-
try {
|
|
1348
|
-
value.site = new ObjectId4(value.site);
|
|
1349
|
-
} catch (error2) {
|
|
1350
|
-
throw new BadRequestError7("Invalid site ID format.");
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
return {
|
|
1354
|
-
site: value.site,
|
|
1355
|
-
name: value.name,
|
|
1356
|
-
status: "active",
|
|
1357
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
1358
|
-
updatedAt: "",
|
|
1359
|
-
deletedAt: ""
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
// src/services/hygiene-unit.service.ts
|
|
1364
|
-
import {
|
|
1365
|
-
BadRequestError as BadRequestError9,
|
|
1366
|
-
logger as logger9,
|
|
1367
|
-
NotFoundError as NotFoundError3,
|
|
1368
|
-
useAtlas as useAtlas4
|
|
1369
|
-
} from "@7365admin1/node-server-utils";
|
|
1370
|
-
|
|
1371
|
-
// src/repositories/hygiene-unit.repository.ts
|
|
1372
|
-
import { ObjectId as ObjectId5 } from "mongodb";
|
|
1373
|
-
import {
|
|
1374
|
-
useAtlas as useAtlas3,
|
|
1375
|
-
InternalServerError as InternalServerError3,
|
|
1376
|
-
paginate as paginate2,
|
|
1377
|
-
BadRequestError as BadRequestError8,
|
|
1378
|
-
useCache as useCache3,
|
|
1379
|
-
logger as logger8,
|
|
1380
|
-
makeCacheKey as makeCacheKey3
|
|
1381
|
-
} from "@7365admin1/node-server-utils";
|
|
1382
|
-
function useUnitRepository() {
|
|
1383
|
-
const db = useAtlas3.getDb();
|
|
1384
|
-
if (!db) {
|
|
1385
|
-
throw new InternalServerError3("Unable to connect to server.");
|
|
1386
|
-
}
|
|
1387
|
-
const namespace_collection = "site.cleaning.area.unit";
|
|
1388
|
-
const collection = db.collection(namespace_collection);
|
|
1389
|
-
const { delNamespace, setCache, getCache } = useCache3(namespace_collection);
|
|
1390
|
-
async function createIndex() {
|
|
1391
|
-
try {
|
|
1392
|
-
await collection.createIndexes([
|
|
1393
|
-
{ key: { site: 1 } },
|
|
1394
|
-
{ key: { status: 1 } }
|
|
1395
|
-
]);
|
|
1396
|
-
} catch (error) {
|
|
1397
|
-
throw new InternalServerError3("Failed to create index on hygiene unit.");
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
async function createTextIndex() {
|
|
1401
|
-
try {
|
|
1402
|
-
await collection.createIndex({ name: "text" });
|
|
1403
|
-
} catch (error) {
|
|
1404
|
-
throw new InternalServerError3(
|
|
1405
|
-
"Failed to create text index on hygiene unit."
|
|
1406
|
-
);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
async function createUniqueIndex() {
|
|
1410
|
-
try {
|
|
1411
|
-
await collection.createIndex(
|
|
1412
|
-
{ site: 1, name: 1, deletedAt: 1 },
|
|
1413
|
-
{ unique: true }
|
|
1414
|
-
);
|
|
1415
|
-
} catch (error) {
|
|
1416
|
-
throw new InternalServerError3(
|
|
1417
|
-
"Failed to create unique index on hygiene unit."
|
|
1418
|
-
);
|
|
1625
|
+
return;
|
|
1419
1626
|
}
|
|
1420
1627
|
}
|
|
1421
|
-
async function
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1430
|
-
err
|
|
1431
|
-
);
|
|
1432
|
-
});
|
|
1433
|
-
return res.insertedId;
|
|
1434
|
-
} catch (error) {
|
|
1435
|
-
const isDuplicated = error.message.includes("duplicate");
|
|
1436
|
-
if (isDuplicated) {
|
|
1437
|
-
throw new BadRequestError8("Unit already exists.");
|
|
1438
|
-
}
|
|
1439
|
-
throw error;
|
|
1628
|
+
async function deleteArea(req, res, next) {
|
|
1629
|
+
const id = req.params.id;
|
|
1630
|
+
const validation = Joi4.string().hex().required();
|
|
1631
|
+
const { error, value } = validation.validate(id);
|
|
1632
|
+
if (error) {
|
|
1633
|
+
logger9.log({ level: "error", message: error.message });
|
|
1634
|
+
next(new BadRequestError8(error.message));
|
|
1635
|
+
return;
|
|
1440
1636
|
}
|
|
1441
|
-
}
|
|
1442
|
-
async function getUnits({
|
|
1443
|
-
page = 1,
|
|
1444
|
-
limit = 10,
|
|
1445
|
-
search = "",
|
|
1446
|
-
site
|
|
1447
|
-
}) {
|
|
1448
|
-
page = page > 0 ? page - 1 : 0;
|
|
1449
|
-
const query = {
|
|
1450
|
-
status: { $ne: "deleted" }
|
|
1451
|
-
};
|
|
1452
|
-
const cacheOptions = {
|
|
1453
|
-
page,
|
|
1454
|
-
limit
|
|
1455
|
-
};
|
|
1456
1637
|
try {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
} catch (
|
|
1461
|
-
|
|
1638
|
+
await _deleteById(value);
|
|
1639
|
+
res.json({ message: "Area deleted successfully." });
|
|
1640
|
+
return;
|
|
1641
|
+
} catch (error2) {
|
|
1642
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1643
|
+
next(error2);
|
|
1644
|
+
return;
|
|
1462
1645
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1646
|
+
}
|
|
1647
|
+
async function importArea(req, res, next) {
|
|
1648
|
+
if (!req.file) {
|
|
1649
|
+
next(new BadRequestError8("File is required!"));
|
|
1650
|
+
return;
|
|
1466
1651
|
}
|
|
1467
|
-
const
|
|
1468
|
-
const
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1652
|
+
const { site } = req.params;
|
|
1653
|
+
const schema = Joi4.string().hex().required();
|
|
1654
|
+
const { error, value } = schema.validate(site);
|
|
1655
|
+
if (error) {
|
|
1656
|
+
logger9.log({ level: "error", message: error.message });
|
|
1657
|
+
next(new BadRequestError8(error.message));
|
|
1658
|
+
return;
|
|
1472
1659
|
}
|
|
1473
1660
|
try {
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
const data = paginate2(items, page, limit, length);
|
|
1483
|
-
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
1484
|
-
logger8.info(`Cache set for key: ${cacheKey}`);
|
|
1485
|
-
}).catch((err) => {
|
|
1486
|
-
logger8.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
1487
|
-
});
|
|
1488
|
-
return data;
|
|
1489
|
-
} catch (error) {
|
|
1490
|
-
throw error;
|
|
1661
|
+
const xlsData = await convertBufferFile(req.file.buffer);
|
|
1662
|
+
const dataJson = JSON.stringify(xlsData);
|
|
1663
|
+
const result = await _importArea({ dataJson, site: value });
|
|
1664
|
+
return res.status(201).json(result);
|
|
1665
|
+
} catch (error2) {
|
|
1666
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1667
|
+
next(error2);
|
|
1668
|
+
return;
|
|
1491
1669
|
}
|
|
1492
1670
|
}
|
|
1493
|
-
async function
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1671
|
+
async function exportAreas(req, res, next) {
|
|
1672
|
+
const { site } = req.params;
|
|
1673
|
+
const validation = Joi4.string().hex().required();
|
|
1674
|
+
const { error, value } = validation.validate(site);
|
|
1675
|
+
if (error) {
|
|
1676
|
+
logger9.log({ level: "error", message: error.message });
|
|
1677
|
+
next(new BadRequestError8(error.message));
|
|
1678
|
+
return;
|
|
1498
1679
|
}
|
|
1499
1680
|
try {
|
|
1500
|
-
const
|
|
1501
|
-
const
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1681
|
+
const excelBuffer = await _exportAreas(value);
|
|
1682
|
+
const date = /* @__PURE__ */ new Date();
|
|
1683
|
+
const formattedDate = `${String(date.getMonth() + 1).padStart(
|
|
1684
|
+
2,
|
|
1685
|
+
"0"
|
|
1686
|
+
)}-${String(date.getDate()).padStart(2, "0")}-${date.getFullYear()}`;
|
|
1687
|
+
res.setHeader(
|
|
1688
|
+
"Content-Type",
|
|
1689
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
1505
1690
|
);
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
return res.modifiedCount;
|
|
1518
|
-
} catch (error) {
|
|
1519
|
-
const isDuplicated = error.message.includes("duplicate");
|
|
1520
|
-
if (isDuplicated) {
|
|
1521
|
-
throw new BadRequestError8("Area already exists.");
|
|
1522
|
-
}
|
|
1523
|
-
throw error;
|
|
1691
|
+
res.setHeader(
|
|
1692
|
+
"Content-Disposition",
|
|
1693
|
+
`attachment; filename="areas-${formattedDate}.xlsx"`
|
|
1694
|
+
);
|
|
1695
|
+
res.setHeader("Content-Length", excelBuffer.length);
|
|
1696
|
+
res.end(excelBuffer);
|
|
1697
|
+
return;
|
|
1698
|
+
} catch (error2) {
|
|
1699
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1700
|
+
next(error2);
|
|
1701
|
+
return;
|
|
1524
1702
|
}
|
|
1525
1703
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1704
|
+
return {
|
|
1705
|
+
createArea,
|
|
1706
|
+
getAreas,
|
|
1707
|
+
getAreaById,
|
|
1708
|
+
updateArea,
|
|
1709
|
+
deleteArea,
|
|
1710
|
+
importArea,
|
|
1711
|
+
exportAreas
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// src/services/hygiene-unit.service.ts
|
|
1716
|
+
import {
|
|
1717
|
+
BadRequestError as BadRequestError9,
|
|
1718
|
+
logger as logger11,
|
|
1719
|
+
NotFoundError as NotFoundError3,
|
|
1720
|
+
useAtlas as useAtlas4
|
|
1721
|
+
} from "@7365admin1/node-server-utils";
|
|
1722
|
+
|
|
1723
|
+
// src/services/hygiene-unit-export.service.ts
|
|
1724
|
+
import { logger as logger10 } from "@7365admin1/node-server-utils";
|
|
1725
|
+
import * as xlsx3 from "xlsx";
|
|
1726
|
+
function useUnitExportService() {
|
|
1727
|
+
async function generateUnitExcel(units) {
|
|
1728
|
+
try {
|
|
1729
|
+
const rows = [];
|
|
1730
|
+
rows.push(["UNIT"]);
|
|
1731
|
+
for (const unit of units) {
|
|
1732
|
+
const unitName = unit.name || "";
|
|
1733
|
+
rows.push([unitName]);
|
|
1545
1734
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1735
|
+
const workbook = xlsx3.utils.book_new();
|
|
1736
|
+
const worksheet = xlsx3.utils.aoa_to_sheet(rows);
|
|
1737
|
+
worksheet["!cols"] = [
|
|
1738
|
+
{ width: 25 }
|
|
1739
|
+
// UNIT
|
|
1740
|
+
];
|
|
1741
|
+
xlsx3.utils.book_append_sheet(workbook, worksheet, "Units");
|
|
1742
|
+
const excelBuffer = xlsx3.write(workbook, {
|
|
1743
|
+
type: "buffer",
|
|
1744
|
+
bookType: "xlsx"
|
|
1553
1745
|
});
|
|
1554
|
-
return
|
|
1746
|
+
return excelBuffer;
|
|
1555
1747
|
} catch (error) {
|
|
1748
|
+
logger10.log({
|
|
1749
|
+
level: "error",
|
|
1750
|
+
message: `Failed to generate unit Excel: ${error.message}`
|
|
1751
|
+
});
|
|
1556
1752
|
throw error;
|
|
1557
1753
|
}
|
|
1558
1754
|
}
|
|
1559
1755
|
return {
|
|
1560
|
-
|
|
1561
|
-
createTextIndex,
|
|
1562
|
-
createUniqueIndex,
|
|
1563
|
-
createUnit,
|
|
1564
|
-
getUnits,
|
|
1565
|
-
updateUnit,
|
|
1566
|
-
deleteUnit
|
|
1756
|
+
generateUnitExcel
|
|
1567
1757
|
};
|
|
1568
1758
|
}
|
|
1569
1759
|
|
|
@@ -1596,13 +1786,18 @@ function useUnitService() {
|
|
|
1596
1786
|
for (let i = 0; i < dataArray.length; i++) {
|
|
1597
1787
|
const row = dataArray[i];
|
|
1598
1788
|
if (!row?.UNIT) {
|
|
1599
|
-
|
|
1789
|
+
logger11.warn(`Skipping row ${i + 1} with missing UNIT:`, row);
|
|
1600
1790
|
skippedRows.push(i + 1);
|
|
1601
1791
|
continue;
|
|
1602
1792
|
}
|
|
1603
1793
|
const unitName = String(row.UNIT).trim();
|
|
1604
1794
|
if (!unitName) {
|
|
1605
|
-
|
|
1795
|
+
logger11.warn(`Skipping row ${i + 1} with empty unit name`);
|
|
1796
|
+
skippedRows.push(i + 1);
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
if (unitName.startsWith("Sample:")) {
|
|
1800
|
+
logger11.warn(`Skipping row ${i + 1} with sample unit: ${unitName}`);
|
|
1606
1801
|
skippedRows.push(i + 1);
|
|
1607
1802
|
continue;
|
|
1608
1803
|
}
|
|
@@ -1612,9 +1807,9 @@ function useUnitService() {
|
|
|
1612
1807
|
site
|
|
1613
1808
|
});
|
|
1614
1809
|
insertedUnitIds.push(insertedId);
|
|
1615
|
-
|
|
1810
|
+
logger11.info(`Successfully created unit: ${unitName}`);
|
|
1616
1811
|
} catch (error) {
|
|
1617
|
-
|
|
1812
|
+
logger11.error(`Error creating unit "${unitName}": ${error.message}`);
|
|
1618
1813
|
if (error.message.includes("Unit already exists")) {
|
|
1619
1814
|
duplicateUnits.push(unitName);
|
|
1620
1815
|
} else {
|
|
@@ -1632,7 +1827,7 @@ function useUnitService() {
|
|
|
1632
1827
|
if (skippedRows.length > 0) {
|
|
1633
1828
|
message += `, ${skippedRows.length} rows skipped (invalid data)`;
|
|
1634
1829
|
}
|
|
1635
|
-
|
|
1830
|
+
logger11.info(message);
|
|
1636
1831
|
if (insertedUnitIds.length === 0) {
|
|
1637
1832
|
if (duplicateUnits.length > 0 && failedUnits.length === 0) {
|
|
1638
1833
|
throw new BadRequestError9(
|
|
@@ -1650,7 +1845,7 @@ function useUnitService() {
|
|
|
1650
1845
|
}
|
|
1651
1846
|
return { message };
|
|
1652
1847
|
} catch (error) {
|
|
1653
|
-
|
|
1848
|
+
logger11.error("Error while uploading unit information", error);
|
|
1654
1849
|
if (error instanceof BadRequestError9) {
|
|
1655
1850
|
throw error;
|
|
1656
1851
|
} else if (error.message.includes("duplicate")) {
|
|
@@ -1680,7 +1875,7 @@ function useUnitService() {
|
|
|
1680
1875
|
await session?.commitTransaction();
|
|
1681
1876
|
return result;
|
|
1682
1877
|
} catch (error) {
|
|
1683
|
-
|
|
1878
|
+
logger11.error(`Error updating unit and associated areas:`, error);
|
|
1684
1879
|
if (session?.inTransaction()) {
|
|
1685
1880
|
await session?.abortTransaction();
|
|
1686
1881
|
}
|
|
@@ -1701,7 +1896,7 @@ function useUnitService() {
|
|
|
1701
1896
|
await session?.commitTransaction();
|
|
1702
1897
|
return result;
|
|
1703
1898
|
} catch (error) {
|
|
1704
|
-
|
|
1899
|
+
logger11.error(`Error deleting unit:`, error);
|
|
1705
1900
|
if (session?.inTransaction()) {
|
|
1706
1901
|
await session?.abortTransaction();
|
|
1707
1902
|
}
|
|
@@ -1710,24 +1905,48 @@ function useUnitService() {
|
|
|
1710
1905
|
session?.endSession();
|
|
1711
1906
|
}
|
|
1712
1907
|
}
|
|
1713
|
-
|
|
1908
|
+
async function exportUnits(site) {
|
|
1909
|
+
const { generateUnitExcel: _generateUnitExcel } = useUnitExportService();
|
|
1910
|
+
const { getUnits: _getUnits } = useUnitRepository();
|
|
1911
|
+
try {
|
|
1912
|
+
const data = await _getUnits({
|
|
1913
|
+
page: 1,
|
|
1914
|
+
limit: 999999,
|
|
1915
|
+
search: "",
|
|
1916
|
+
site
|
|
1917
|
+
});
|
|
1918
|
+
if (!data || !data.items || data.items.length === 0) {
|
|
1919
|
+
throw new BadRequestError9("No data found to export");
|
|
1920
|
+
}
|
|
1921
|
+
const excelBuffer = await _generateUnitExcel(data.items);
|
|
1922
|
+
if (!excelBuffer || excelBuffer.length === 0) {
|
|
1923
|
+
throw new Error("Generated Excel file is empty or invalid.");
|
|
1924
|
+
}
|
|
1925
|
+
return excelBuffer;
|
|
1926
|
+
} catch (error) {
|
|
1927
|
+
logger11.error(`Error downloading units: ${error.message}`);
|
|
1928
|
+
throw error;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
return { importUnit, updateUnit, deleteUnit, exportUnits };
|
|
1714
1932
|
}
|
|
1715
1933
|
|
|
1716
1934
|
// src/controllers/hygiene-unit.controller.ts
|
|
1717
1935
|
import Joi5 from "joi";
|
|
1718
|
-
import { BadRequestError as BadRequestError10, logger as
|
|
1936
|
+
import { BadRequestError as BadRequestError10, logger as logger12 } from "@7365admin1/node-server-utils";
|
|
1719
1937
|
function useUnitController() {
|
|
1720
1938
|
const { createUnit: _createUnit, getUnits: _getUnits } = useUnitRepository();
|
|
1721
1939
|
const {
|
|
1722
1940
|
updateUnit: _updateUnit,
|
|
1723
1941
|
deleteUnit: _deleteUnit,
|
|
1724
|
-
importUnit: _importUnit
|
|
1942
|
+
importUnit: _importUnit,
|
|
1943
|
+
exportUnits: _exportUnits
|
|
1725
1944
|
} = useUnitService();
|
|
1726
1945
|
async function createUnit(req, res, next) {
|
|
1727
1946
|
const payload = { ...req.body, ...req.params };
|
|
1728
1947
|
const { error } = unitSchema.validate(payload);
|
|
1729
1948
|
if (error) {
|
|
1730
|
-
|
|
1949
|
+
logger12.log({ level: "error", message: error.message });
|
|
1731
1950
|
next(new BadRequestError10(error.message));
|
|
1732
1951
|
return;
|
|
1733
1952
|
}
|
|
@@ -1736,7 +1955,7 @@ function useUnitController() {
|
|
|
1736
1955
|
res.status(201).json({ message: "Unit created successfully.", id });
|
|
1737
1956
|
return;
|
|
1738
1957
|
} catch (error2) {
|
|
1739
|
-
|
|
1958
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1740
1959
|
next(error2);
|
|
1741
1960
|
return;
|
|
1742
1961
|
}
|
|
@@ -1751,7 +1970,7 @@ function useUnitController() {
|
|
|
1751
1970
|
});
|
|
1752
1971
|
const { error } = validation.validate(query);
|
|
1753
1972
|
if (error) {
|
|
1754
|
-
|
|
1973
|
+
logger12.log({ level: "error", message: error.message });
|
|
1755
1974
|
next(new BadRequestError10(error.message));
|
|
1756
1975
|
return;
|
|
1757
1976
|
}
|
|
@@ -1769,7 +1988,7 @@ function useUnitController() {
|
|
|
1769
1988
|
res.json(data);
|
|
1770
1989
|
return;
|
|
1771
1990
|
} catch (error2) {
|
|
1772
|
-
|
|
1991
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1773
1992
|
next(error2);
|
|
1774
1993
|
return;
|
|
1775
1994
|
}
|
|
@@ -1782,7 +2001,7 @@ function useUnitController() {
|
|
|
1782
2001
|
});
|
|
1783
2002
|
const { error } = validation.validate(payload);
|
|
1784
2003
|
if (error) {
|
|
1785
|
-
|
|
2004
|
+
logger12.log({ level: "error", message: error.message });
|
|
1786
2005
|
next(new BadRequestError10(error.message));
|
|
1787
2006
|
return;
|
|
1788
2007
|
}
|
|
@@ -1792,7 +2011,7 @@ function useUnitController() {
|
|
|
1792
2011
|
res.json({ message: "Unit updated successfully." });
|
|
1793
2012
|
return;
|
|
1794
2013
|
} catch (error2) {
|
|
1795
|
-
|
|
2014
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1796
2015
|
next(error2);
|
|
1797
2016
|
return;
|
|
1798
2017
|
}
|
|
@@ -1804,7 +2023,7 @@ function useUnitController() {
|
|
|
1804
2023
|
});
|
|
1805
2024
|
const { error, value } = validation.validate({ id });
|
|
1806
2025
|
if (error) {
|
|
1807
|
-
|
|
2026
|
+
logger12.log({ level: "error", message: error.message });
|
|
1808
2027
|
next(new BadRequestError10(error.message));
|
|
1809
2028
|
return;
|
|
1810
2029
|
}
|
|
@@ -1813,7 +2032,7 @@ function useUnitController() {
|
|
|
1813
2032
|
res.json({ message: "Unit deleted successfully." });
|
|
1814
2033
|
return;
|
|
1815
2034
|
} catch (error2) {
|
|
1816
|
-
|
|
2035
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1817
2036
|
next(error2);
|
|
1818
2037
|
return;
|
|
1819
2038
|
}
|
|
@@ -1827,7 +2046,7 @@ function useUnitController() {
|
|
|
1827
2046
|
const validation = Joi5.string().hex().required();
|
|
1828
2047
|
const { error, value } = validation.validate(site);
|
|
1829
2048
|
if (error) {
|
|
1830
|
-
|
|
2049
|
+
logger12.log({ level: "error", message: error.message });
|
|
1831
2050
|
next(new BadRequestError10(error.message));
|
|
1832
2051
|
return;
|
|
1833
2052
|
}
|
|
@@ -1837,7 +2056,40 @@ function useUnitController() {
|
|
|
1837
2056
|
const result = await _importUnit({ dataJson, site: value });
|
|
1838
2057
|
return res.status(201).json(result);
|
|
1839
2058
|
} catch (error2) {
|
|
1840
|
-
|
|
2059
|
+
logger12.log({ level: "error", message: error2.message });
|
|
2060
|
+
next(error2);
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
async function exportUnits(req, res, next) {
|
|
2065
|
+
const { site } = req.params;
|
|
2066
|
+
const validation = Joi5.string().hex().required();
|
|
2067
|
+
const { error, value } = validation.validate(site);
|
|
2068
|
+
if (error) {
|
|
2069
|
+
logger12.log({ level: "error", message: error.message });
|
|
2070
|
+
next(new BadRequestError10(error.message));
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
try {
|
|
2074
|
+
const excelBuffer = await _exportUnits(value);
|
|
2075
|
+
const date = /* @__PURE__ */ new Date();
|
|
2076
|
+
const formattedDate = `${String(date.getMonth() + 1).padStart(
|
|
2077
|
+
2,
|
|
2078
|
+
"0"
|
|
2079
|
+
)}-${String(date.getDate()).padStart(2, "0")}-${date.getFullYear()}`;
|
|
2080
|
+
res.setHeader(
|
|
2081
|
+
"Content-Type",
|
|
2082
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
2083
|
+
);
|
|
2084
|
+
res.setHeader(
|
|
2085
|
+
"Content-Disposition",
|
|
2086
|
+
`attachment; filename="units-${formattedDate}.xlsx"`
|
|
2087
|
+
);
|
|
2088
|
+
res.setHeader("Content-Length", excelBuffer.length);
|
|
2089
|
+
res.end(excelBuffer);
|
|
2090
|
+
return;
|
|
2091
|
+
} catch (error2) {
|
|
2092
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1841
2093
|
next(error2);
|
|
1842
2094
|
return;
|
|
1843
2095
|
}
|
|
@@ -1847,14 +2099,15 @@ function useUnitController() {
|
|
|
1847
2099
|
getUnits,
|
|
1848
2100
|
updateUnit,
|
|
1849
2101
|
deleteUnit,
|
|
1850
|
-
importUnit
|
|
2102
|
+
importUnit,
|
|
2103
|
+
exportUnits
|
|
1851
2104
|
};
|
|
1852
2105
|
}
|
|
1853
2106
|
|
|
1854
2107
|
// src/models/hygiene-parent-checklist.model.ts
|
|
1855
2108
|
import Joi6 from "joi";
|
|
1856
2109
|
import { ObjectId as ObjectId6 } from "mongodb";
|
|
1857
|
-
import { BadRequestError as BadRequestError11, logger as
|
|
2110
|
+
import { BadRequestError as BadRequestError11, logger as logger13 } from "@7365admin1/node-server-utils";
|
|
1858
2111
|
var parentChecklistSchema = Joi6.object({
|
|
1859
2112
|
createdAt: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null),
|
|
1860
2113
|
site: Joi6.string().hex().required()
|
|
@@ -1862,7 +2115,7 @@ var parentChecklistSchema = Joi6.object({
|
|
|
1862
2115
|
function MParentChecklist(value) {
|
|
1863
2116
|
const { error } = parentChecklistSchema.validate(value);
|
|
1864
2117
|
if (error) {
|
|
1865
|
-
|
|
2118
|
+
logger13.info(`Hygiene Parent Checklist Model: ${error.message}`);
|
|
1866
2119
|
throw new BadRequestError11(error.message);
|
|
1867
2120
|
}
|
|
1868
2121
|
if (value.site) {
|
|
@@ -1887,7 +2140,7 @@ import {
|
|
|
1887
2140
|
InternalServerError as InternalServerError4,
|
|
1888
2141
|
paginate as paginate3,
|
|
1889
2142
|
useCache as useCache4,
|
|
1890
|
-
logger as
|
|
2143
|
+
logger as logger14,
|
|
1891
2144
|
makeCacheKey as makeCacheKey4,
|
|
1892
2145
|
BadRequestError as BadRequestError12
|
|
1893
2146
|
} from "@7365admin1/node-server-utils";
|
|
@@ -1930,7 +2183,7 @@ function useParentChecklistRepo() {
|
|
|
1930
2183
|
if (value.site) {
|
|
1931
2184
|
try {
|
|
1932
2185
|
existingQuery.site = new ObjectId7(value.site);
|
|
1933
|
-
|
|
2186
|
+
logger14.info(
|
|
1934
2187
|
`createParentChecklist: Looking for existing checklist with query: ${JSON.stringify(
|
|
1935
2188
|
{ ...existingQuery, site: value.site }
|
|
1936
2189
|
)}`
|
|
@@ -1939,7 +2192,7 @@ function useParentChecklistRepo() {
|
|
|
1939
2192
|
throw new BadRequestError12("Invalid site ID format.");
|
|
1940
2193
|
}
|
|
1941
2194
|
} else {
|
|
1942
|
-
|
|
2195
|
+
logger14.info(
|
|
1943
2196
|
`createParentChecklist: Looking for existing checklist with query (no site filter): ${JSON.stringify(
|
|
1944
2197
|
existingQuery
|
|
1945
2198
|
)}`
|
|
@@ -1948,7 +2201,7 @@ function useParentChecklistRepo() {
|
|
|
1948
2201
|
const existingChecklist = await collection.findOne(existingQuery);
|
|
1949
2202
|
if (existingChecklist) {
|
|
1950
2203
|
const dateStr2 = currentDate.toISOString().split("T")[0];
|
|
1951
|
-
|
|
2204
|
+
logger14.info(
|
|
1952
2205
|
`Parent checklist already exists for ${value.site ? `site ${value.site} on` : ""} today: ${dateStr2}. Found checklist: ${JSON.stringify({
|
|
1953
2206
|
_id: existingChecklist._id,
|
|
1954
2207
|
site: existingChecklist.site
|
|
@@ -1963,26 +2216,26 @@ function useParentChecklistRepo() {
|
|
|
1963
2216
|
});
|
|
1964
2217
|
const result2 = await collection.insertOne(checklistDoc, { session });
|
|
1965
2218
|
delNamespace().then(() => {
|
|
1966
|
-
|
|
2219
|
+
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1967
2220
|
}).catch((err) => {
|
|
1968
|
-
|
|
2221
|
+
logger14.error(
|
|
1969
2222
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1970
2223
|
err
|
|
1971
2224
|
);
|
|
1972
2225
|
});
|
|
1973
2226
|
const dateStr2 = currentDate.toISOString().split("T")[0];
|
|
1974
|
-
|
|
2227
|
+
logger14.info(
|
|
1975
2228
|
`Created parent checklist for site ${value.site} for today: ${dateStr2}`
|
|
1976
2229
|
);
|
|
1977
2230
|
return result2.insertedId;
|
|
1978
2231
|
}
|
|
1979
2232
|
const siteIds = await getHygieneSiteIds();
|
|
1980
2233
|
if (!Array.isArray(siteIds)) {
|
|
1981
|
-
|
|
2234
|
+
logger14.error("getHygieneSiteIds returned non-array value:", siteIds);
|
|
1982
2235
|
throw new InternalServerError4("Failed to retrieve site IDs");
|
|
1983
2236
|
}
|
|
1984
2237
|
if (siteIds.length === 0) {
|
|
1985
|
-
|
|
2238
|
+
logger14.warn("No sites found for creating parent checklist");
|
|
1986
2239
|
throw new BadRequestError12("No sites available for checklist creation");
|
|
1987
2240
|
}
|
|
1988
2241
|
const checklistDocs = [];
|
|
@@ -1996,20 +2249,20 @@ function useParentChecklistRepo() {
|
|
|
1996
2249
|
}
|
|
1997
2250
|
const result = await collection.insertMany(checklistDocs, { session });
|
|
1998
2251
|
delNamespace().then(() => {
|
|
1999
|
-
|
|
2252
|
+
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2000
2253
|
}).catch((err) => {
|
|
2001
|
-
|
|
2254
|
+
logger14.error(
|
|
2002
2255
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2003
2256
|
err
|
|
2004
2257
|
);
|
|
2005
2258
|
});
|
|
2006
2259
|
const dateStr = currentDate.toISOString().split("T")[0];
|
|
2007
|
-
|
|
2260
|
+
logger14.info(
|
|
2008
2261
|
`Created ${Object.keys(result.insertedIds).length} parent checklists for today: ${dateStr}`
|
|
2009
2262
|
);
|
|
2010
2263
|
return Object.values(result.insertedIds);
|
|
2011
2264
|
} catch (error) {
|
|
2012
|
-
|
|
2265
|
+
logger14.error("Failed to create daily parent checklist", error);
|
|
2013
2266
|
throw error;
|
|
2014
2267
|
}
|
|
2015
2268
|
}
|
|
@@ -2060,7 +2313,7 @@ function useParentChecklistRepo() {
|
|
|
2060
2313
|
const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
|
|
2061
2314
|
const cachedData = await getCache(cacheKey);
|
|
2062
2315
|
if (cachedData) {
|
|
2063
|
-
|
|
2316
|
+
logger14.info(`Cache hit for key: ${cacheKey}`);
|
|
2064
2317
|
return cachedData;
|
|
2065
2318
|
}
|
|
2066
2319
|
try {
|
|
@@ -2095,9 +2348,9 @@ function useParentChecklistRepo() {
|
|
|
2095
2348
|
const length = await collection.countDocuments(query);
|
|
2096
2349
|
const data = paginate3(items, page, limit, length);
|
|
2097
2350
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2098
|
-
|
|
2351
|
+
logger14.info(`Cache set for key: ${cacheKey}`);
|
|
2099
2352
|
}).catch((err) => {
|
|
2100
|
-
|
|
2353
|
+
logger14.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2101
2354
|
});
|
|
2102
2355
|
return data;
|
|
2103
2356
|
} catch (error) {
|
|
@@ -2129,9 +2382,9 @@ function useParentChecklistRepo() {
|
|
|
2129
2382
|
);
|
|
2130
2383
|
}
|
|
2131
2384
|
delNamespace().then(() => {
|
|
2132
|
-
|
|
2385
|
+
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2133
2386
|
}).catch((err) => {
|
|
2134
|
-
|
|
2387
|
+
logger14.error(
|
|
2135
2388
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2136
2389
|
err
|
|
2137
2390
|
);
|
|
@@ -2148,7 +2401,7 @@ function useParentChecklistRepo() {
|
|
|
2148
2401
|
const cacheKey = makeCacheKey4(site_collection, { tag: "site-ids" });
|
|
2149
2402
|
const cachedData = await getCache(cacheKey);
|
|
2150
2403
|
if (cachedData && Array.isArray(cachedData)) {
|
|
2151
|
-
|
|
2404
|
+
logger14.info(`Cache hit for key: ${cacheKey}`);
|
|
2152
2405
|
return cachedData;
|
|
2153
2406
|
}
|
|
2154
2407
|
try {
|
|
@@ -2160,14 +2413,14 @@ function useParentChecklistRepo() {
|
|
|
2160
2413
|
const siteIds = items.map((item) => item._id.toString());
|
|
2161
2414
|
if (siteIds.length > 0) {
|
|
2162
2415
|
setCache(cacheKey, siteIds, 15 * 60).then(() => {
|
|
2163
|
-
|
|
2416
|
+
logger14.info(`Cache set for key: ${cacheKey}`);
|
|
2164
2417
|
}).catch((err) => {
|
|
2165
|
-
|
|
2418
|
+
logger14.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2166
2419
|
});
|
|
2167
2420
|
}
|
|
2168
2421
|
return siteIds;
|
|
2169
2422
|
} catch (error) {
|
|
2170
|
-
|
|
2423
|
+
logger14.error("Failed to get hygiene site IDs", error);
|
|
2171
2424
|
throw error;
|
|
2172
2425
|
}
|
|
2173
2426
|
}
|
|
@@ -2181,7 +2434,7 @@ function useParentChecklistRepo() {
|
|
|
2181
2434
|
|
|
2182
2435
|
// src/controllers/hygiene-parent-checklist.controller.ts
|
|
2183
2436
|
import Joi7 from "joi";
|
|
2184
|
-
import { BadRequestError as BadRequestError13, logger as
|
|
2437
|
+
import { BadRequestError as BadRequestError13, logger as logger15 } from "@7365admin1/node-server-utils";
|
|
2185
2438
|
function useParentChecklistController() {
|
|
2186
2439
|
const {
|
|
2187
2440
|
createParentChecklist: _createParentChecklist,
|
|
@@ -2191,7 +2444,7 @@ function useParentChecklistController() {
|
|
|
2191
2444
|
const payload = req.body;
|
|
2192
2445
|
const { error } = parentChecklistSchema.validate(payload);
|
|
2193
2446
|
if (error) {
|
|
2194
|
-
|
|
2447
|
+
logger15.log({ level: "error", message: error.message });
|
|
2195
2448
|
next(new BadRequestError13(error.message));
|
|
2196
2449
|
return;
|
|
2197
2450
|
}
|
|
@@ -2200,7 +2453,7 @@ function useParentChecklistController() {
|
|
|
2200
2453
|
res.status(201).json({ message: "Parent checklist created successfully.", id });
|
|
2201
2454
|
return;
|
|
2202
2455
|
} catch (error2) {
|
|
2203
|
-
|
|
2456
|
+
logger15.log({ level: "error", message: error2.message });
|
|
2204
2457
|
next(error2);
|
|
2205
2458
|
return;
|
|
2206
2459
|
}
|
|
@@ -2218,7 +2471,7 @@ function useParentChecklistController() {
|
|
|
2218
2471
|
});
|
|
2219
2472
|
const { error } = validation.validate(query);
|
|
2220
2473
|
if (error) {
|
|
2221
|
-
|
|
2474
|
+
logger15.log({ level: "error", message: error.message });
|
|
2222
2475
|
next(new BadRequestError13(error.message));
|
|
2223
2476
|
return;
|
|
2224
2477
|
}
|
|
@@ -2242,7 +2495,7 @@ function useParentChecklistController() {
|
|
|
2242
2495
|
res.json(data);
|
|
2243
2496
|
return;
|
|
2244
2497
|
} catch (error2) {
|
|
2245
|
-
|
|
2498
|
+
logger15.log({ level: "error", message: error2.message });
|
|
2246
2499
|
next(error2);
|
|
2247
2500
|
return;
|
|
2248
2501
|
}
|
|
@@ -2256,7 +2509,7 @@ function useParentChecklistController() {
|
|
|
2256
2509
|
// src/models/hygiene-area-checklist.model.ts
|
|
2257
2510
|
import Joi8 from "joi";
|
|
2258
2511
|
import { ObjectId as ObjectId8 } from "mongodb";
|
|
2259
|
-
import { BadRequestError as BadRequestError14, logger as
|
|
2512
|
+
import { BadRequestError as BadRequestError14, logger as logger16 } from "@7365admin1/node-server-utils";
|
|
2260
2513
|
var allowedChecklistStatus = ["ready", "completed"];
|
|
2261
2514
|
var areaChecklistSchema = Joi8.object({
|
|
2262
2515
|
schedule: Joi8.string().hex().required(),
|
|
@@ -2279,7 +2532,7 @@ var areaChecklistSchema = Joi8.object({
|
|
|
2279
2532
|
function MAreaChecklist(value) {
|
|
2280
2533
|
const { error } = areaChecklistSchema.validate(value);
|
|
2281
2534
|
if (error) {
|
|
2282
|
-
|
|
2535
|
+
logger16.info(`Hygiene Checklist Area Model: ${error.message}`);
|
|
2283
2536
|
throw new BadRequestError14(error.message);
|
|
2284
2537
|
}
|
|
2285
2538
|
if (value.schedule) {
|
|
@@ -2333,14 +2586,14 @@ function MAreaChecklist(value) {
|
|
|
2333
2586
|
}
|
|
2334
2587
|
|
|
2335
2588
|
// src/services/hygiene-area-checklist.service.ts
|
|
2336
|
-
import { logger as
|
|
2589
|
+
import { logger as logger18, useAtlas as useAtlas7 } from "@7365admin1/node-server-utils";
|
|
2337
2590
|
|
|
2338
2591
|
// src/repositories/hygiene-area-checklist.repository.ts
|
|
2339
2592
|
import { ObjectId as ObjectId9 } from "mongodb";
|
|
2340
2593
|
import {
|
|
2341
2594
|
BadRequestError as BadRequestError15,
|
|
2342
2595
|
InternalServerError as InternalServerError5,
|
|
2343
|
-
logger as
|
|
2596
|
+
logger as logger17,
|
|
2344
2597
|
makeCacheKey as makeCacheKey5,
|
|
2345
2598
|
paginate as paginate4,
|
|
2346
2599
|
useAtlas as useAtlas6,
|
|
@@ -2401,7 +2654,7 @@ function useAreaChecklistRepo() {
|
|
|
2401
2654
|
}
|
|
2402
2655
|
});
|
|
2403
2656
|
if (existingChecklist) {
|
|
2404
|
-
|
|
2657
|
+
logger17.info(
|
|
2405
2658
|
`Area checklist already exists for area ${value.name} on ${currentDate.toISOString().split("T")[0]}`
|
|
2406
2659
|
);
|
|
2407
2660
|
return existingChecklist._id;
|
|
@@ -2409,9 +2662,9 @@ function useAreaChecklistRepo() {
|
|
|
2409
2662
|
const processedValue = MAreaChecklist(value);
|
|
2410
2663
|
const result = await collection.insertOne(processedValue, { session });
|
|
2411
2664
|
delNamespace().then(() => {
|
|
2412
|
-
|
|
2665
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2413
2666
|
}).catch((err) => {
|
|
2414
|
-
|
|
2667
|
+
logger17.error(
|
|
2415
2668
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2416
2669
|
err
|
|
2417
2670
|
);
|
|
@@ -2457,7 +2710,7 @@ function useAreaChecklistRepo() {
|
|
|
2457
2710
|
if (!session) {
|
|
2458
2711
|
const cachedData = await getCache(cacheKey);
|
|
2459
2712
|
if (cachedData) {
|
|
2460
|
-
|
|
2713
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2461
2714
|
return cachedData;
|
|
2462
2715
|
}
|
|
2463
2716
|
}
|
|
@@ -2533,9 +2786,9 @@ function useAreaChecklistRepo() {
|
|
|
2533
2786
|
const length = await collection.countDocuments(query, { session });
|
|
2534
2787
|
const data = paginate4(items, page, limit, length);
|
|
2535
2788
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2536
|
-
|
|
2789
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2537
2790
|
}).catch((err) => {
|
|
2538
|
-
|
|
2791
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2539
2792
|
});
|
|
2540
2793
|
return data;
|
|
2541
2794
|
} catch (error) {
|
|
@@ -2587,7 +2840,7 @@ function useAreaChecklistRepo() {
|
|
|
2587
2840
|
const cacheKey = makeCacheKey5(namespace_collection, cacheOptions);
|
|
2588
2841
|
const cachedData = await getCache(cacheKey);
|
|
2589
2842
|
if (cachedData) {
|
|
2590
|
-
|
|
2843
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2591
2844
|
return cachedData;
|
|
2592
2845
|
}
|
|
2593
2846
|
try {
|
|
@@ -2637,9 +2890,9 @@ function useAreaChecklistRepo() {
|
|
|
2637
2890
|
const length = await collection.countDocuments(query);
|
|
2638
2891
|
const data = paginate4(items, page, limit, length);
|
|
2639
2892
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2640
|
-
|
|
2893
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2641
2894
|
}).catch((err) => {
|
|
2642
|
-
|
|
2895
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2643
2896
|
});
|
|
2644
2897
|
return data;
|
|
2645
2898
|
} catch (error) {
|
|
@@ -2657,7 +2910,7 @@ function useAreaChecklistRepo() {
|
|
|
2657
2910
|
});
|
|
2658
2911
|
const cachedData = await getCache(cacheKey);
|
|
2659
2912
|
if (cachedData) {
|
|
2660
|
-
|
|
2913
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2661
2914
|
return cachedData;
|
|
2662
2915
|
}
|
|
2663
2916
|
try {
|
|
@@ -2787,9 +3040,9 @@ function useAreaChecklistRepo() {
|
|
|
2787
3040
|
units
|
|
2788
3041
|
};
|
|
2789
3042
|
setCache(cacheKey, items, 15 * 60).then(() => {
|
|
2790
|
-
|
|
3043
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2791
3044
|
}).catch((err) => {
|
|
2792
|
-
|
|
3045
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2793
3046
|
});
|
|
2794
3047
|
return items;
|
|
2795
3048
|
} catch (error) {
|
|
@@ -2822,7 +3075,7 @@ function useAreaChecklistRepo() {
|
|
|
2822
3075
|
if (!session) {
|
|
2823
3076
|
const cachedData = await getCache(cacheKey);
|
|
2824
3077
|
if (cachedData) {
|
|
2825
|
-
|
|
3078
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2826
3079
|
return cachedData;
|
|
2827
3080
|
}
|
|
2828
3081
|
}
|
|
@@ -2912,9 +3165,9 @@ function useAreaChecklistRepo() {
|
|
|
2912
3165
|
const length = countResult.length > 0 ? countResult[0].total : 0;
|
|
2913
3166
|
const data = paginate4(items, page, limit, length);
|
|
2914
3167
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2915
|
-
|
|
3168
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2916
3169
|
}).catch((err) => {
|
|
2917
|
-
|
|
3170
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2918
3171
|
});
|
|
2919
3172
|
return data;
|
|
2920
3173
|
} catch (error) {
|
|
@@ -2994,16 +3247,16 @@ function useAreaChecklistRepo() {
|
|
|
2994
3247
|
throw new InternalServerError5("Unable to update area checklist unit.");
|
|
2995
3248
|
}
|
|
2996
3249
|
delNamespace().then(() => {
|
|
2997
|
-
|
|
3250
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2998
3251
|
}).catch((err) => {
|
|
2999
|
-
|
|
3252
|
+
logger17.error(
|
|
3000
3253
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3001
3254
|
err
|
|
3002
3255
|
);
|
|
3003
3256
|
});
|
|
3004
3257
|
return res.modifiedCount;
|
|
3005
3258
|
} catch (error) {
|
|
3006
|
-
|
|
3259
|
+
logger17.error("Error updating area checklist unit:", error.message);
|
|
3007
3260
|
throw error;
|
|
3008
3261
|
}
|
|
3009
3262
|
}
|
|
@@ -3027,9 +3280,9 @@ function useAreaChecklistRepo() {
|
|
|
3027
3280
|
throw new InternalServerError5("Unable to update area checklist.");
|
|
3028
3281
|
}
|
|
3029
3282
|
delNamespace().then(() => {
|
|
3030
|
-
|
|
3283
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3031
3284
|
}).catch((err) => {
|
|
3032
|
-
|
|
3285
|
+
logger17.error(
|
|
3033
3286
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3034
3287
|
err
|
|
3035
3288
|
);
|
|
@@ -3064,9 +3317,9 @@ function useAreaChecklistRepo() {
|
|
|
3064
3317
|
);
|
|
3065
3318
|
}
|
|
3066
3319
|
delNamespace().then(() => {
|
|
3067
|
-
|
|
3320
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3068
3321
|
}).catch((err) => {
|
|
3069
|
-
|
|
3322
|
+
logger17.error(
|
|
3070
3323
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3071
3324
|
err
|
|
3072
3325
|
);
|
|
@@ -3089,7 +3342,7 @@ function useAreaChecklistRepo() {
|
|
|
3089
3342
|
).toArray();
|
|
3090
3343
|
return result.length > 0 && result[0].maxSet ? result[0].maxSet : 0;
|
|
3091
3344
|
} catch (error) {
|
|
3092
|
-
|
|
3345
|
+
logger17.error(`Error getting max set number for area ${areaId}:`, error);
|
|
3093
3346
|
return 0;
|
|
3094
3347
|
}
|
|
3095
3348
|
}
|
|
@@ -3137,7 +3390,7 @@ function useAreaChecklistService() {
|
|
|
3137
3390
|
const batch = areas.slice(i, i + BATCH_SIZE);
|
|
3138
3391
|
const batchPromises = batch.map(async (area) => {
|
|
3139
3392
|
if (!area.units || area.units.length === 0) {
|
|
3140
|
-
|
|
3393
|
+
logger18.warn(
|
|
3141
3394
|
`Skipping area ${area.name} (${area._id}): No units defined`
|
|
3142
3395
|
);
|
|
3143
3396
|
return null;
|
|
@@ -3168,19 +3421,19 @@ function useAreaChecklistService() {
|
|
|
3168
3421
|
const batchResults = await Promise.all(batchPromises);
|
|
3169
3422
|
results.push(...batchResults.filter((id) => id !== null));
|
|
3170
3423
|
}
|
|
3171
|
-
|
|
3424
|
+
logger18.info(
|
|
3172
3425
|
`Created ${totalChecklistsCreated} area checklists out of ${areas.length} areas for site: ${value.site}`
|
|
3173
3426
|
);
|
|
3174
3427
|
} else {
|
|
3175
|
-
|
|
3428
|
+
logger18.warn(`No common areas found for site: ${value.site}`);
|
|
3176
3429
|
}
|
|
3177
3430
|
await session?.commitTransaction();
|
|
3178
|
-
|
|
3431
|
+
logger18.info(
|
|
3179
3432
|
`Successfully created ${totalChecklistsCreated} area checklists for site: ${value.site}`
|
|
3180
3433
|
);
|
|
3181
3434
|
return results;
|
|
3182
3435
|
} catch (error) {
|
|
3183
|
-
|
|
3436
|
+
logger18.error(`Error generating area checklists:`, error);
|
|
3184
3437
|
if (session?.inTransaction()) {
|
|
3185
3438
|
await session?.abortTransaction();
|
|
3186
3439
|
}
|
|
@@ -3256,7 +3509,7 @@ function useAreaChecklistService() {
|
|
|
3256
3509
|
session
|
|
3257
3510
|
);
|
|
3258
3511
|
} else {
|
|
3259
|
-
|
|
3512
|
+
logger18.info(
|
|
3260
3513
|
"No area checklists found, keeping parent status as ready"
|
|
3261
3514
|
);
|
|
3262
3515
|
}
|
|
@@ -3264,7 +3517,7 @@ function useAreaChecklistService() {
|
|
|
3264
3517
|
await session?.commitTransaction();
|
|
3265
3518
|
return;
|
|
3266
3519
|
} catch (error) {
|
|
3267
|
-
|
|
3520
|
+
logger18.error(`Error updating area checklist unit and statuses:`, error);
|
|
3268
3521
|
if (session?.inTransaction()) {
|
|
3269
3522
|
await session?.abortTransaction();
|
|
3270
3523
|
}
|
|
@@ -3278,7 +3531,7 @@ function useAreaChecklistService() {
|
|
|
3278
3531
|
|
|
3279
3532
|
// src/controllers/hygiene-area-checklist.controller.ts
|
|
3280
3533
|
import Joi9 from "joi";
|
|
3281
|
-
import { BadRequestError as BadRequestError16, logger as
|
|
3534
|
+
import { BadRequestError as BadRequestError16, logger as logger19 } from "@7365admin1/node-server-utils";
|
|
3282
3535
|
function useAreaChecklistController() {
|
|
3283
3536
|
const {
|
|
3284
3537
|
getAllAreaChecklist: _getAllAreaChecklist,
|
|
@@ -3308,7 +3561,7 @@ function useAreaChecklistController() {
|
|
|
3308
3561
|
});
|
|
3309
3562
|
const { error, value } = validation.validate(payload);
|
|
3310
3563
|
if (error) {
|
|
3311
|
-
|
|
3564
|
+
logger19.log({ level: "error", message: error.message });
|
|
3312
3565
|
next(new BadRequestError16(error.message));
|
|
3313
3566
|
return;
|
|
3314
3567
|
}
|
|
@@ -3317,7 +3570,7 @@ function useAreaChecklistController() {
|
|
|
3317
3570
|
res.status(201).json({ message: "Area checklist generated successfully." });
|
|
3318
3571
|
return;
|
|
3319
3572
|
} catch (error2) {
|
|
3320
|
-
|
|
3573
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3321
3574
|
next(error2);
|
|
3322
3575
|
return;
|
|
3323
3576
|
}
|
|
@@ -3334,7 +3587,7 @@ function useAreaChecklistController() {
|
|
|
3334
3587
|
});
|
|
3335
3588
|
const { error } = validation.validate(query);
|
|
3336
3589
|
if (error) {
|
|
3337
|
-
|
|
3590
|
+
logger19.log({ level: "error", message: error.message });
|
|
3338
3591
|
next(new BadRequestError16(error.message));
|
|
3339
3592
|
return;
|
|
3340
3593
|
}
|
|
@@ -3356,7 +3609,7 @@ function useAreaChecklistController() {
|
|
|
3356
3609
|
res.json(data);
|
|
3357
3610
|
return;
|
|
3358
3611
|
} catch (error2) {
|
|
3359
|
-
|
|
3612
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3360
3613
|
next(error2);
|
|
3361
3614
|
return;
|
|
3362
3615
|
}
|
|
@@ -3374,7 +3627,7 @@ function useAreaChecklistController() {
|
|
|
3374
3627
|
});
|
|
3375
3628
|
const { error } = validation.validate(query);
|
|
3376
3629
|
if (error) {
|
|
3377
|
-
|
|
3630
|
+
logger19.log({ level: "error", message: error.message });
|
|
3378
3631
|
next(new BadRequestError16(error.message));
|
|
3379
3632
|
return;
|
|
3380
3633
|
}
|
|
@@ -3398,7 +3651,7 @@ function useAreaChecklistController() {
|
|
|
3398
3651
|
res.json(data);
|
|
3399
3652
|
return;
|
|
3400
3653
|
} catch (error2) {
|
|
3401
|
-
|
|
3654
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3402
3655
|
next(error2);
|
|
3403
3656
|
return;
|
|
3404
3657
|
}
|
|
@@ -3408,7 +3661,7 @@ function useAreaChecklistController() {
|
|
|
3408
3661
|
const _id = req.params.id;
|
|
3409
3662
|
const { error, value } = validation.validate(_id);
|
|
3410
3663
|
if (error) {
|
|
3411
|
-
|
|
3664
|
+
logger19.log({ level: "error", message: error.message });
|
|
3412
3665
|
next(new BadRequestError16(error.message));
|
|
3413
3666
|
return;
|
|
3414
3667
|
}
|
|
@@ -3417,7 +3670,7 @@ function useAreaChecklistController() {
|
|
|
3417
3670
|
res.json(data);
|
|
3418
3671
|
return;
|
|
3419
3672
|
} catch (error2) {
|
|
3420
|
-
|
|
3673
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3421
3674
|
next(error2);
|
|
3422
3675
|
return;
|
|
3423
3676
|
}
|
|
@@ -3432,7 +3685,7 @@ function useAreaChecklistController() {
|
|
|
3432
3685
|
});
|
|
3433
3686
|
const { error } = validation.validate(query);
|
|
3434
3687
|
if (error) {
|
|
3435
|
-
|
|
3688
|
+
logger19.log({ level: "error", message: error.message });
|
|
3436
3689
|
next(new BadRequestError16(error.message));
|
|
3437
3690
|
return;
|
|
3438
3691
|
}
|
|
@@ -3450,7 +3703,7 @@ function useAreaChecklistController() {
|
|
|
3450
3703
|
res.json(data);
|
|
3451
3704
|
return;
|
|
3452
3705
|
} catch (error2) {
|
|
3453
|
-
|
|
3706
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3454
3707
|
next(error2);
|
|
3455
3708
|
return;
|
|
3456
3709
|
}
|
|
@@ -3475,7 +3728,7 @@ function useAreaChecklistController() {
|
|
|
3475
3728
|
});
|
|
3476
3729
|
const { error } = validation.validate(payload);
|
|
3477
3730
|
if (error) {
|
|
3478
|
-
|
|
3731
|
+
logger19.log({ level: "error", message: error.message });
|
|
3479
3732
|
next(new BadRequestError16(error.message));
|
|
3480
3733
|
return;
|
|
3481
3734
|
}
|
|
@@ -3490,7 +3743,7 @@ function useAreaChecklistController() {
|
|
|
3490
3743
|
res.json({ message: "Area checklist updated successfully." });
|
|
3491
3744
|
return;
|
|
3492
3745
|
} catch (error2) {
|
|
3493
|
-
|
|
3746
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3494
3747
|
next(error2);
|
|
3495
3748
|
return;
|
|
3496
3749
|
}
|
|
@@ -3508,7 +3761,7 @@ function useAreaChecklistController() {
|
|
|
3508
3761
|
// src/models/hygiene-supply.model.ts
|
|
3509
3762
|
import Joi10 from "joi";
|
|
3510
3763
|
import { ObjectId as ObjectId10 } from "mongodb";
|
|
3511
|
-
import { BadRequestError as BadRequestError17, logger as
|
|
3764
|
+
import { BadRequestError as BadRequestError17, logger as logger20 } from "@7365admin1/node-server-utils";
|
|
3512
3765
|
var supplySchema = Joi10.object({
|
|
3513
3766
|
site: Joi10.string().hex().required(),
|
|
3514
3767
|
name: Joi10.string().required(),
|
|
@@ -3517,7 +3770,7 @@ var supplySchema = Joi10.object({
|
|
|
3517
3770
|
function MSupply(value) {
|
|
3518
3771
|
const { error } = supplySchema.validate(value);
|
|
3519
3772
|
if (error) {
|
|
3520
|
-
|
|
3773
|
+
logger20.info(`Hygiene Supply Model: ${error.message}`);
|
|
3521
3774
|
throw new BadRequestError17(error.message);
|
|
3522
3775
|
}
|
|
3523
3776
|
if (value.site) {
|
|
@@ -3547,7 +3800,7 @@ import {
|
|
|
3547
3800
|
paginate as paginate5,
|
|
3548
3801
|
BadRequestError as BadRequestError18,
|
|
3549
3802
|
useCache as useCache6,
|
|
3550
|
-
logger as
|
|
3803
|
+
logger as logger21,
|
|
3551
3804
|
makeCacheKey as makeCacheKey6,
|
|
3552
3805
|
NotFoundError as NotFoundError4
|
|
3553
3806
|
} from "@7365admin1/node-server-utils";
|
|
@@ -3594,9 +3847,9 @@ function useSupplyRepository() {
|
|
|
3594
3847
|
value = MSupply(value);
|
|
3595
3848
|
const res = await collection.insertOne(value, { session });
|
|
3596
3849
|
delNamespace().then(() => {
|
|
3597
|
-
|
|
3850
|
+
logger21.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3598
3851
|
}).catch((err) => {
|
|
3599
|
-
|
|
3852
|
+
logger21.error(
|
|
3600
3853
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3601
3854
|
err
|
|
3602
3855
|
);
|
|
@@ -3638,7 +3891,7 @@ function useSupplyRepository() {
|
|
|
3638
3891
|
const cacheKey = makeCacheKey6(namespace_collection, cacheOptions);
|
|
3639
3892
|
const cachedData = await getCache(cacheKey);
|
|
3640
3893
|
if (cachedData) {
|
|
3641
|
-
|
|
3894
|
+
logger21.info(`Cache hit for key: ${cacheKey}`);
|
|
3642
3895
|
return cachedData;
|
|
3643
3896
|
}
|
|
3644
3897
|
try {
|
|
@@ -3658,9 +3911,9 @@ function useSupplyRepository() {
|
|
|
3658
3911
|
const length = await collection.countDocuments(query);
|
|
3659
3912
|
const data = paginate5(items, page, limit, length);
|
|
3660
3913
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3661
|
-
|
|
3914
|
+
logger21.info(`Cache set for key: ${cacheKey}`);
|
|
3662
3915
|
}).catch((err) => {
|
|
3663
|
-
|
|
3916
|
+
logger21.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3664
3917
|
});
|
|
3665
3918
|
return data;
|
|
3666
3919
|
} catch (error) {
|
|
@@ -3683,11 +3936,11 @@ function useSupplyRepository() {
|
|
|
3683
3936
|
if (!session) {
|
|
3684
3937
|
const cachedData = await getCache(cacheKey);
|
|
3685
3938
|
if (cachedData) {
|
|
3686
|
-
|
|
3939
|
+
logger21.info(`Cache hit for key: ${cacheKey}`);
|
|
3687
3940
|
return cachedData;
|
|
3688
3941
|
}
|
|
3689
3942
|
} else {
|
|
3690
|
-
|
|
3943
|
+
logger21.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
3691
3944
|
}
|
|
3692
3945
|
try {
|
|
3693
3946
|
const data = await collection.aggregate([
|
|
@@ -3704,9 +3957,9 @@ function useSupplyRepository() {
|
|
|
3704
3957
|
throw new NotFoundError4("Supply not found.");
|
|
3705
3958
|
}
|
|
3706
3959
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
3707
|
-
|
|
3960
|
+
logger21.info(`Cache set for key: ${cacheKey}`);
|
|
3708
3961
|
}).catch((err) => {
|
|
3709
|
-
|
|
3962
|
+
logger21.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3710
3963
|
});
|
|
3711
3964
|
return data[0];
|
|
3712
3965
|
} catch (error) {
|
|
@@ -3730,9 +3983,9 @@ function useSupplyRepository() {
|
|
|
3730
3983
|
throw new InternalServerError6("Unable to update cleaning supply.");
|
|
3731
3984
|
}
|
|
3732
3985
|
delNamespace().then(() => {
|
|
3733
|
-
|
|
3986
|
+
logger21.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3734
3987
|
}).catch((err) => {
|
|
3735
|
-
|
|
3988
|
+
logger21.error(
|
|
3736
3989
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3737
3990
|
err
|
|
3738
3991
|
);
|
|
@@ -3767,9 +4020,9 @@ function useSupplyRepository() {
|
|
|
3767
4020
|
throw new InternalServerError6("Unable to delete supply.");
|
|
3768
4021
|
}
|
|
3769
4022
|
delNamespace().then(() => {
|
|
3770
|
-
|
|
4023
|
+
logger21.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3771
4024
|
}).catch((err) => {
|
|
3772
|
-
|
|
4025
|
+
logger21.error(
|
|
3773
4026
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3774
4027
|
err
|
|
3775
4028
|
);
|
|
@@ -3793,7 +4046,7 @@ function useSupplyRepository() {
|
|
|
3793
4046
|
|
|
3794
4047
|
// src/controllers/hygiene-supply.controller.ts
|
|
3795
4048
|
import Joi11 from "joi";
|
|
3796
|
-
import { BadRequestError as BadRequestError19, logger as
|
|
4049
|
+
import { BadRequestError as BadRequestError19, logger as logger22 } from "@7365admin1/node-server-utils";
|
|
3797
4050
|
function useSupplyController() {
|
|
3798
4051
|
const {
|
|
3799
4052
|
createSupply: _createSupply,
|
|
@@ -3806,7 +4059,7 @@ function useSupplyController() {
|
|
|
3806
4059
|
const payload = { ...req.body, ...req.params };
|
|
3807
4060
|
const { error } = supplySchema.validate(payload);
|
|
3808
4061
|
if (error) {
|
|
3809
|
-
|
|
4062
|
+
logger22.log({ level: "error", message: error.message });
|
|
3810
4063
|
next(new BadRequestError19(error.message));
|
|
3811
4064
|
return;
|
|
3812
4065
|
}
|
|
@@ -3815,7 +4068,7 @@ function useSupplyController() {
|
|
|
3815
4068
|
res.status(201).json({ message: "Supply created successfully.", id });
|
|
3816
4069
|
return;
|
|
3817
4070
|
} catch (error2) {
|
|
3818
|
-
|
|
4071
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3819
4072
|
next(error2);
|
|
3820
4073
|
return;
|
|
3821
4074
|
}
|
|
@@ -3830,7 +4083,7 @@ function useSupplyController() {
|
|
|
3830
4083
|
});
|
|
3831
4084
|
const { error } = validation.validate(query);
|
|
3832
4085
|
if (error) {
|
|
3833
|
-
|
|
4086
|
+
logger22.log({ level: "error", message: error.message });
|
|
3834
4087
|
next(new BadRequestError19(error.message));
|
|
3835
4088
|
return;
|
|
3836
4089
|
}
|
|
@@ -3848,7 +4101,7 @@ function useSupplyController() {
|
|
|
3848
4101
|
res.json(data);
|
|
3849
4102
|
return;
|
|
3850
4103
|
} catch (error2) {
|
|
3851
|
-
|
|
4104
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3852
4105
|
next(error2);
|
|
3853
4106
|
return;
|
|
3854
4107
|
}
|
|
@@ -3858,7 +4111,7 @@ function useSupplyController() {
|
|
|
3858
4111
|
const _id = req.params.id;
|
|
3859
4112
|
const { error, value } = validation.validate(_id);
|
|
3860
4113
|
if (error) {
|
|
3861
|
-
|
|
4114
|
+
logger22.log({ level: "error", message: error.message });
|
|
3862
4115
|
next(new BadRequestError19(error.message));
|
|
3863
4116
|
return;
|
|
3864
4117
|
}
|
|
@@ -3867,7 +4120,7 @@ function useSupplyController() {
|
|
|
3867
4120
|
res.json(data);
|
|
3868
4121
|
return;
|
|
3869
4122
|
} catch (error2) {
|
|
3870
|
-
|
|
4123
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3871
4124
|
next(error2);
|
|
3872
4125
|
return;
|
|
3873
4126
|
}
|
|
@@ -3882,7 +4135,7 @@ function useSupplyController() {
|
|
|
3882
4135
|
});
|
|
3883
4136
|
const { error } = validation.validate(payload);
|
|
3884
4137
|
if (error) {
|
|
3885
|
-
|
|
4138
|
+
logger22.log({ level: "error", message: error.message });
|
|
3886
4139
|
next(new BadRequestError19(error.message));
|
|
3887
4140
|
return;
|
|
3888
4141
|
}
|
|
@@ -3892,7 +4145,7 @@ function useSupplyController() {
|
|
|
3892
4145
|
res.json({ message: "Supply updated successfully." });
|
|
3893
4146
|
return;
|
|
3894
4147
|
} catch (error2) {
|
|
3895
|
-
|
|
4148
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3896
4149
|
next(error2);
|
|
3897
4150
|
return;
|
|
3898
4151
|
}
|
|
@@ -3904,7 +4157,7 @@ function useSupplyController() {
|
|
|
3904
4157
|
});
|
|
3905
4158
|
const { error, value } = validation.validate({ id });
|
|
3906
4159
|
if (error) {
|
|
3907
|
-
|
|
4160
|
+
logger22.log({ level: "error", message: error.message });
|
|
3908
4161
|
next(new BadRequestError19(error.message));
|
|
3909
4162
|
return;
|
|
3910
4163
|
}
|
|
@@ -3913,7 +4166,7 @@ function useSupplyController() {
|
|
|
3913
4166
|
res.json({ message: "Supply deleted successfully." });
|
|
3914
4167
|
return;
|
|
3915
4168
|
} catch (error2) {
|
|
3916
|
-
|
|
4169
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3917
4170
|
next(error2);
|
|
3918
4171
|
return;
|
|
3919
4172
|
}
|
|
@@ -3930,7 +4183,7 @@ function useSupplyController() {
|
|
|
3930
4183
|
// src/models/hygiene-stock.model.ts
|
|
3931
4184
|
import Joi12 from "joi";
|
|
3932
4185
|
import { ObjectId as ObjectId12 } from "mongodb";
|
|
3933
|
-
import { BadRequestError as BadRequestError20, logger as
|
|
4186
|
+
import { BadRequestError as BadRequestError20, logger as logger23 } from "@7365admin1/node-server-utils";
|
|
3934
4187
|
var stockSchema = Joi12.object({
|
|
3935
4188
|
site: Joi12.string().hex().required(),
|
|
3936
4189
|
supply: Joi12.string().hex().required(),
|
|
@@ -3942,7 +4195,7 @@ var stockSchema = Joi12.object({
|
|
|
3942
4195
|
function MStock(value) {
|
|
3943
4196
|
const { error } = stockSchema.validate(value);
|
|
3944
4197
|
if (error) {
|
|
3945
|
-
|
|
4198
|
+
logger23.info(`Hygiene Stock Model: ${error.message}`);
|
|
3946
4199
|
throw new BadRequestError20(error.message);
|
|
3947
4200
|
}
|
|
3948
4201
|
if (value.site) {
|
|
@@ -3980,7 +4233,7 @@ import {
|
|
|
3980
4233
|
InternalServerError as InternalServerError7,
|
|
3981
4234
|
BadRequestError as BadRequestError21,
|
|
3982
4235
|
useCache as useCache7,
|
|
3983
|
-
logger as
|
|
4236
|
+
logger as logger24,
|
|
3984
4237
|
makeCacheKey as makeCacheKey7,
|
|
3985
4238
|
paginate as paginate6
|
|
3986
4239
|
} from "@7365admin1/node-server-utils";
|
|
@@ -4011,17 +4264,17 @@ function useStockRepository() {
|
|
|
4011
4264
|
value = MStock(value);
|
|
4012
4265
|
const res = await collection.insertOne(value, { session });
|
|
4013
4266
|
delNamespace().then(() => {
|
|
4014
|
-
|
|
4267
|
+
logger24.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4015
4268
|
}).catch((err) => {
|
|
4016
|
-
|
|
4269
|
+
logger24.error(
|
|
4017
4270
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4018
4271
|
err
|
|
4019
4272
|
);
|
|
4020
4273
|
});
|
|
4021
4274
|
delSupplyNamespace().then(() => {
|
|
4022
|
-
|
|
4275
|
+
logger24.info(`Cache cleared for namespace: ${supply_collection}`);
|
|
4023
4276
|
}).catch((err) => {
|
|
4024
|
-
|
|
4277
|
+
logger24.error(
|
|
4025
4278
|
`Failed to clear cache for namespace: ${supply_collection}`,
|
|
4026
4279
|
err
|
|
4027
4280
|
);
|
|
@@ -4067,7 +4320,7 @@ function useStockRepository() {
|
|
|
4067
4320
|
const cacheKey = makeCacheKey7(namespace_collection, cacheOptions);
|
|
4068
4321
|
const cachedData = await getCache(cacheKey);
|
|
4069
4322
|
if (cachedData) {
|
|
4070
|
-
|
|
4323
|
+
logger24.info(`Cache hit for key: ${cacheKey}`);
|
|
4071
4324
|
return cachedData;
|
|
4072
4325
|
}
|
|
4073
4326
|
try {
|
|
@@ -4088,9 +4341,9 @@ function useStockRepository() {
|
|
|
4088
4341
|
const length = await collection.countDocuments(query);
|
|
4089
4342
|
const data = paginate6(items, page, limit, length);
|
|
4090
4343
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4091
|
-
|
|
4344
|
+
logger24.info(`Cache set for key: ${cacheKey}`);
|
|
4092
4345
|
}).catch((err) => {
|
|
4093
|
-
|
|
4346
|
+
logger24.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4094
4347
|
});
|
|
4095
4348
|
return data;
|
|
4096
4349
|
} catch (error) {
|
|
@@ -4160,7 +4413,7 @@ function useStockService() {
|
|
|
4160
4413
|
|
|
4161
4414
|
// src/controllers/hygiene-stock.controller.ts
|
|
4162
4415
|
import Joi13 from "joi";
|
|
4163
|
-
import { BadRequestError as BadRequestError23, logger as
|
|
4416
|
+
import { BadRequestError as BadRequestError23, logger as logger25 } from "@7365admin1/node-server-utils";
|
|
4164
4417
|
function useStockController() {
|
|
4165
4418
|
const { getStocksBySupplyId: _getStocksBySupplyId } = useStockRepository();
|
|
4166
4419
|
const { createStock: _createStock } = useStockService();
|
|
@@ -4174,7 +4427,7 @@ function useStockController() {
|
|
|
4174
4427
|
});
|
|
4175
4428
|
const { error } = validation.validate(payload);
|
|
4176
4429
|
if (error) {
|
|
4177
|
-
|
|
4430
|
+
logger25.log({ level: "error", message: error.message });
|
|
4178
4431
|
next(new BadRequestError23(error.message));
|
|
4179
4432
|
return;
|
|
4180
4433
|
}
|
|
@@ -4183,7 +4436,7 @@ function useStockController() {
|
|
|
4183
4436
|
res.status(201).json({ message: "Stock created successfully.", id });
|
|
4184
4437
|
return;
|
|
4185
4438
|
} catch (error2) {
|
|
4186
|
-
|
|
4439
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4187
4440
|
next(error2);
|
|
4188
4441
|
return;
|
|
4189
4442
|
}
|
|
@@ -4199,7 +4452,7 @@ function useStockController() {
|
|
|
4199
4452
|
});
|
|
4200
4453
|
const { error } = validation.validate(query);
|
|
4201
4454
|
if (error) {
|
|
4202
|
-
|
|
4455
|
+
logger25.log({ level: "error", message: error.message });
|
|
4203
4456
|
next(new BadRequestError23(error.message));
|
|
4204
4457
|
return;
|
|
4205
4458
|
}
|
|
@@ -4219,7 +4472,7 @@ function useStockController() {
|
|
|
4219
4472
|
res.json(data);
|
|
4220
4473
|
return;
|
|
4221
4474
|
} catch (error2) {
|
|
4222
|
-
|
|
4475
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4223
4476
|
next(error2);
|
|
4224
4477
|
return;
|
|
4225
4478
|
}
|
|
@@ -4233,7 +4486,7 @@ function useStockController() {
|
|
|
4233
4486
|
// src/models/hygiene-checkout-item.model.ts
|
|
4234
4487
|
import Joi14 from "joi";
|
|
4235
4488
|
import { ObjectId as ObjectId14 } from "mongodb";
|
|
4236
|
-
import { BadRequestError as BadRequestError24, logger as
|
|
4489
|
+
import { BadRequestError as BadRequestError24, logger as logger26 } from "@7365admin1/node-server-utils";
|
|
4237
4490
|
var allowedCheckOutItemStatus = ["pending", "completed"];
|
|
4238
4491
|
var checkOutItemSchema = Joi14.object({
|
|
4239
4492
|
site: Joi14.string().hex().required(),
|
|
@@ -4247,7 +4500,7 @@ var checkOutItemSchema = Joi14.object({
|
|
|
4247
4500
|
function MCheckOutItem(value) {
|
|
4248
4501
|
const { error } = checkOutItemSchema.validate(value);
|
|
4249
4502
|
if (error) {
|
|
4250
|
-
|
|
4503
|
+
logger26.info(`Hygiene Check Out Item Model: ${error.message}`);
|
|
4251
4504
|
throw new BadRequestError24(error.message);
|
|
4252
4505
|
}
|
|
4253
4506
|
if (value.site) {
|
|
@@ -4285,7 +4538,7 @@ import {
|
|
|
4285
4538
|
useAtlas as useAtlas11,
|
|
4286
4539
|
InternalServerError as InternalServerError8,
|
|
4287
4540
|
useCache as useCache8,
|
|
4288
|
-
logger as
|
|
4541
|
+
logger as logger27,
|
|
4289
4542
|
makeCacheKey as makeCacheKey8,
|
|
4290
4543
|
paginate as paginate7,
|
|
4291
4544
|
BadRequestError as BadRequestError25,
|
|
@@ -4326,9 +4579,9 @@ function useCheckOutItemRepository() {
|
|
|
4326
4579
|
value = MCheckOutItem(value);
|
|
4327
4580
|
const res = await collection.insertOne(value, { session });
|
|
4328
4581
|
delNamespace().then(() => {
|
|
4329
|
-
|
|
4582
|
+
logger27.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4330
4583
|
}).catch((err) => {
|
|
4331
|
-
|
|
4584
|
+
logger27.error(
|
|
4332
4585
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4333
4586
|
err
|
|
4334
4587
|
);
|
|
@@ -4366,7 +4619,7 @@ function useCheckOutItemRepository() {
|
|
|
4366
4619
|
const cacheKey = makeCacheKey8(namespace_collection, cacheOptions);
|
|
4367
4620
|
const cachedData = await getCache(cacheKey);
|
|
4368
4621
|
if (cachedData) {
|
|
4369
|
-
|
|
4622
|
+
logger27.info(`Cache hit for key: ${cacheKey}`);
|
|
4370
4623
|
return cachedData;
|
|
4371
4624
|
}
|
|
4372
4625
|
try {
|
|
@@ -4415,9 +4668,9 @@ function useCheckOutItemRepository() {
|
|
|
4415
4668
|
const length = await collection.countDocuments(query);
|
|
4416
4669
|
const data = paginate7(items, page, limit, length);
|
|
4417
4670
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4418
|
-
|
|
4671
|
+
logger27.info(`Cache set for key: ${cacheKey}`);
|
|
4419
4672
|
}).catch((err) => {
|
|
4420
|
-
|
|
4673
|
+
logger27.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4421
4674
|
});
|
|
4422
4675
|
return data;
|
|
4423
4676
|
} catch (error) {
|
|
@@ -4437,11 +4690,11 @@ function useCheckOutItemRepository() {
|
|
|
4437
4690
|
if (!session) {
|
|
4438
4691
|
const cachedData = await getCache(cacheKey);
|
|
4439
4692
|
if (cachedData) {
|
|
4440
|
-
|
|
4693
|
+
logger27.info(`Cache hit for key: ${cacheKey}`);
|
|
4441
4694
|
return cachedData;
|
|
4442
4695
|
}
|
|
4443
4696
|
} else {
|
|
4444
|
-
|
|
4697
|
+
logger27.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
4445
4698
|
}
|
|
4446
4699
|
try {
|
|
4447
4700
|
const data = await collection.aggregate(
|
|
@@ -4479,9 +4732,9 @@ function useCheckOutItemRepository() {
|
|
|
4479
4732
|
throw new NotFoundError6("Check out item not found.");
|
|
4480
4733
|
}
|
|
4481
4734
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
4482
|
-
|
|
4735
|
+
logger27.info(`Cache set for key: ${cacheKey}`);
|
|
4483
4736
|
}).catch((err) => {
|
|
4484
|
-
|
|
4737
|
+
logger27.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4485
4738
|
});
|
|
4486
4739
|
return data[0];
|
|
4487
4740
|
} catch (error) {
|
|
@@ -4508,9 +4761,9 @@ function useCheckOutItemRepository() {
|
|
|
4508
4761
|
throw new InternalServerError8("Unable to complete check out item.");
|
|
4509
4762
|
}
|
|
4510
4763
|
delNamespace().then(() => {
|
|
4511
|
-
|
|
4764
|
+
logger27.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4512
4765
|
}).catch((err) => {
|
|
4513
|
-
|
|
4766
|
+
logger27.error(
|
|
4514
4767
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4515
4768
|
err
|
|
4516
4769
|
);
|
|
@@ -4631,7 +4884,7 @@ function useCheckOutItemService() {
|
|
|
4631
4884
|
|
|
4632
4885
|
// src/controllers/hygiene-checkout-item.controller.ts
|
|
4633
4886
|
import Joi15 from "joi";
|
|
4634
|
-
import { BadRequestError as BadRequestError27, logger as
|
|
4887
|
+
import { BadRequestError as BadRequestError27, logger as logger28 } from "@7365admin1/node-server-utils";
|
|
4635
4888
|
function useCheckOutItemController() {
|
|
4636
4889
|
const {
|
|
4637
4890
|
getCheckOutItems: _getCheckOutItems,
|
|
@@ -4661,7 +4914,7 @@ function useCheckOutItemController() {
|
|
|
4661
4914
|
});
|
|
4662
4915
|
const { error } = validation.validate(payload);
|
|
4663
4916
|
if (error) {
|
|
4664
|
-
|
|
4917
|
+
logger28.log({ level: "error", message: error.message });
|
|
4665
4918
|
next(new BadRequestError27(error.message));
|
|
4666
4919
|
return;
|
|
4667
4920
|
}
|
|
@@ -4670,7 +4923,7 @@ function useCheckOutItemController() {
|
|
|
4670
4923
|
res.status(201).json({ message: "Check out item created successfully.", id });
|
|
4671
4924
|
return;
|
|
4672
4925
|
} catch (error2) {
|
|
4673
|
-
|
|
4926
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4674
4927
|
next(error2);
|
|
4675
4928
|
return;
|
|
4676
4929
|
}
|
|
@@ -4699,7 +4952,7 @@ function useCheckOutItemController() {
|
|
|
4699
4952
|
});
|
|
4700
4953
|
const { error } = validation.validate(payload);
|
|
4701
4954
|
if (error) {
|
|
4702
|
-
|
|
4955
|
+
logger28.log({ level: "error", message: error.message });
|
|
4703
4956
|
next(new BadRequestError27(error.message));
|
|
4704
4957
|
return;
|
|
4705
4958
|
}
|
|
@@ -4708,7 +4961,7 @@ function useCheckOutItemController() {
|
|
|
4708
4961
|
res.status(201).json({ message: "Check out items created successfully." });
|
|
4709
4962
|
return;
|
|
4710
4963
|
} catch (error2) {
|
|
4711
|
-
|
|
4964
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4712
4965
|
next(error2);
|
|
4713
4966
|
return;
|
|
4714
4967
|
}
|
|
@@ -4723,7 +4976,7 @@ function useCheckOutItemController() {
|
|
|
4723
4976
|
});
|
|
4724
4977
|
const { error } = validation.validate(query);
|
|
4725
4978
|
if (error) {
|
|
4726
|
-
|
|
4979
|
+
logger28.log({ level: "error", message: error.message });
|
|
4727
4980
|
next(new BadRequestError27(error.message));
|
|
4728
4981
|
return;
|
|
4729
4982
|
}
|
|
@@ -4741,7 +4994,7 @@ function useCheckOutItemController() {
|
|
|
4741
4994
|
res.json(data);
|
|
4742
4995
|
return;
|
|
4743
4996
|
} catch (error2) {
|
|
4744
|
-
|
|
4997
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4745
4998
|
next(error2);
|
|
4746
4999
|
return;
|
|
4747
5000
|
}
|
|
@@ -4751,7 +5004,7 @@ function useCheckOutItemController() {
|
|
|
4751
5004
|
const _id = req.params.id;
|
|
4752
5005
|
const { error, value } = validation.validate(_id);
|
|
4753
5006
|
if (error) {
|
|
4754
|
-
|
|
5007
|
+
logger28.log({ level: "error", message: error.message });
|
|
4755
5008
|
next(new BadRequestError27(error.message));
|
|
4756
5009
|
return;
|
|
4757
5010
|
}
|
|
@@ -4760,7 +5013,7 @@ function useCheckOutItemController() {
|
|
|
4760
5013
|
res.json(data);
|
|
4761
5014
|
return;
|
|
4762
5015
|
} catch (error2) {
|
|
4763
|
-
|
|
5016
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4764
5017
|
next(error2);
|
|
4765
5018
|
return;
|
|
4766
5019
|
}
|
|
@@ -4774,7 +5027,7 @@ function useCheckOutItemController() {
|
|
|
4774
5027
|
}
|
|
4775
5028
|
|
|
4776
5029
|
// src/models/hygiene-schedule-task.model.ts
|
|
4777
|
-
import { BadRequestError as BadRequestError28, logger as
|
|
5030
|
+
import { BadRequestError as BadRequestError28, logger as logger29 } from "@7365admin1/node-server-utils";
|
|
4778
5031
|
import Joi16 from "joi";
|
|
4779
5032
|
import { ObjectId as ObjectId16 } from "mongodb";
|
|
4780
5033
|
var scheduleTaskSchema = Joi16.object({
|
|
@@ -4795,7 +5048,7 @@ var scheduleTaskSchema = Joi16.object({
|
|
|
4795
5048
|
function MScheduleTask(value) {
|
|
4796
5049
|
const { error } = scheduleTaskSchema.validate(value);
|
|
4797
5050
|
if (error) {
|
|
4798
|
-
|
|
5051
|
+
logger29.info(`Hygiene Schedule Task Model: ${error.message}`);
|
|
4799
5052
|
throw new BadRequestError28(error.message);
|
|
4800
5053
|
}
|
|
4801
5054
|
if (value.site) {
|
|
@@ -4848,7 +5101,7 @@ import {
|
|
|
4848
5101
|
paginate as paginate8,
|
|
4849
5102
|
BadRequestError as BadRequestError29,
|
|
4850
5103
|
useCache as useCache9,
|
|
4851
|
-
logger as
|
|
5104
|
+
logger as logger30,
|
|
4852
5105
|
makeCacheKey as makeCacheKey9,
|
|
4853
5106
|
NotFoundError as NotFoundError7
|
|
4854
5107
|
} from "@7365admin1/node-server-utils";
|
|
@@ -4886,9 +5139,9 @@ function useScheduleTaskRepository() {
|
|
|
4886
5139
|
value = MScheduleTask(value);
|
|
4887
5140
|
const res = await collection.insertOne(value, { session });
|
|
4888
5141
|
delNamespace().then(() => {
|
|
4889
|
-
|
|
5142
|
+
logger30.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4890
5143
|
}).catch((err) => {
|
|
4891
|
-
|
|
5144
|
+
logger30.error(
|
|
4892
5145
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4893
5146
|
err
|
|
4894
5147
|
);
|
|
@@ -4926,7 +5179,7 @@ function useScheduleTaskRepository() {
|
|
|
4926
5179
|
const cacheKey = makeCacheKey9(namespace_collection, cacheOptions);
|
|
4927
5180
|
const cachedData = await getCache(cacheKey);
|
|
4928
5181
|
if (cachedData) {
|
|
4929
|
-
|
|
5182
|
+
logger30.info(`Cache hit for key: ${cacheKey}`);
|
|
4930
5183
|
return cachedData;
|
|
4931
5184
|
}
|
|
4932
5185
|
try {
|
|
@@ -4946,9 +5199,9 @@ function useScheduleTaskRepository() {
|
|
|
4946
5199
|
const length = await collection.countDocuments(query);
|
|
4947
5200
|
const data = paginate8(items, page, limit, length);
|
|
4948
5201
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4949
|
-
|
|
5202
|
+
logger30.info(`Cache set for key: ${cacheKey}`);
|
|
4950
5203
|
}).catch((err) => {
|
|
4951
|
-
|
|
5204
|
+
logger30.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4952
5205
|
});
|
|
4953
5206
|
return data;
|
|
4954
5207
|
} catch (error) {
|
|
@@ -4994,7 +5247,7 @@ function useScheduleTaskRepository() {
|
|
|
4994
5247
|
const cacheKey = makeCacheKey9(namespace_collection, cacheOptions);
|
|
4995
5248
|
const cachedData = await getCache(cacheKey);
|
|
4996
5249
|
if (cachedData) {
|
|
4997
|
-
|
|
5250
|
+
logger30.info(`Cache hit for key: ${cacheKey}`);
|
|
4998
5251
|
return cachedData;
|
|
4999
5252
|
}
|
|
5000
5253
|
try {
|
|
@@ -5013,9 +5266,9 @@ function useScheduleTaskRepository() {
|
|
|
5013
5266
|
const length = await collection.countDocuments(query);
|
|
5014
5267
|
const data = paginate8(items, page, limit, length);
|
|
5015
5268
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
5016
|
-
|
|
5269
|
+
logger30.info(`Cache set for key: ${cacheKey}`);
|
|
5017
5270
|
}).catch((err) => {
|
|
5018
|
-
|
|
5271
|
+
logger30.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
5019
5272
|
});
|
|
5020
5273
|
return data;
|
|
5021
5274
|
} catch (error) {
|
|
@@ -5038,11 +5291,11 @@ function useScheduleTaskRepository() {
|
|
|
5038
5291
|
if (!session) {
|
|
5039
5292
|
const cachedData = await getCache(cacheKey);
|
|
5040
5293
|
if (cachedData) {
|
|
5041
|
-
|
|
5294
|
+
logger30.info(`Cache hit for key: ${cacheKey}`);
|
|
5042
5295
|
return cachedData;
|
|
5043
5296
|
}
|
|
5044
5297
|
} else {
|
|
5045
|
-
|
|
5298
|
+
logger30.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
5046
5299
|
}
|
|
5047
5300
|
try {
|
|
5048
5301
|
const data = await collection.aggregate([
|
|
@@ -5064,9 +5317,9 @@ function useScheduleTaskRepository() {
|
|
|
5064
5317
|
throw new NotFoundError7("Schedule task not found.");
|
|
5065
5318
|
}
|
|
5066
5319
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
5067
|
-
|
|
5320
|
+
logger30.info(`Cache set for key: ${cacheKey}`);
|
|
5068
5321
|
}).catch((err) => {
|
|
5069
|
-
|
|
5322
|
+
logger30.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
5070
5323
|
});
|
|
5071
5324
|
return data[0];
|
|
5072
5325
|
} catch (error) {
|
|
@@ -5104,9 +5357,9 @@ function useScheduleTaskRepository() {
|
|
|
5104
5357
|
);
|
|
5105
5358
|
}
|
|
5106
5359
|
delNamespace().then(() => {
|
|
5107
|
-
|
|
5360
|
+
logger30.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
5108
5361
|
}).catch((err) => {
|
|
5109
|
-
|
|
5362
|
+
logger30.error(
|
|
5110
5363
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
5111
5364
|
err
|
|
5112
5365
|
);
|
|
@@ -5129,7 +5382,7 @@ function useScheduleTaskRepository() {
|
|
|
5129
5382
|
}
|
|
5130
5383
|
|
|
5131
5384
|
// src/services/hygiene-schedule-task.service.ts
|
|
5132
|
-
import { logger as
|
|
5385
|
+
import { logger as logger31 } from "@7365admin1/node-server-utils";
|
|
5133
5386
|
function useScheduleTaskService() {
|
|
5134
5387
|
const { createParentChecklist } = useParentChecklistRepo();
|
|
5135
5388
|
const { getAllScheduleTask } = useScheduleTaskRepository();
|
|
@@ -5152,13 +5405,13 @@ function useScheduleTaskService() {
|
|
|
5152
5405
|
const currentDateString = now.toLocaleDateString("en-US", {
|
|
5153
5406
|
timeZone: "Asia/Singapore"
|
|
5154
5407
|
});
|
|
5155
|
-
|
|
5408
|
+
logger31.info(
|
|
5156
5409
|
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Current date ${currentDateString}, Schedule time ${schedule.time}, Start date ${schedule.startDate}, End date ${schedule.endDate}`
|
|
5157
5410
|
);
|
|
5158
5411
|
const startDate = /* @__PURE__ */ new Date(schedule.startDate + "T00:00:00");
|
|
5159
5412
|
const currentDateOnly = /* @__PURE__ */ new Date(currentDateString + "T00:00:00");
|
|
5160
5413
|
if (currentDateOnly < startDate) {
|
|
5161
|
-
|
|
5414
|
+
logger31.info(
|
|
5162
5415
|
`Schedule ${schedule._id}: Current date ${currentDateString} is before start date ${schedule.startDate}`
|
|
5163
5416
|
);
|
|
5164
5417
|
return false;
|
|
@@ -5166,7 +5419,7 @@ function useScheduleTaskService() {
|
|
|
5166
5419
|
if (schedule.endDate) {
|
|
5167
5420
|
const endDate = /* @__PURE__ */ new Date(schedule.endDate + "T00:00:00");
|
|
5168
5421
|
if (currentDateOnly > endDate) {
|
|
5169
|
-
|
|
5422
|
+
logger31.info(
|
|
5170
5423
|
`Schedule ${schedule._id}: Current date ${currentDateString} is after end date ${schedule.endDate}`
|
|
5171
5424
|
);
|
|
5172
5425
|
return false;
|
|
@@ -5175,17 +5428,17 @@ function useScheduleTaskService() {
|
|
|
5175
5428
|
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
5176
5429
|
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
5177
5430
|
if (!timeMatches) {
|
|
5178
|
-
|
|
5431
|
+
logger31.info(
|
|
5179
5432
|
`Schedule ${schedule._id}: Time does not match. Current: ${currentHour}:${currentMinute}, Expected: ${scheduleHour}:${scheduleMinute}`
|
|
5180
5433
|
);
|
|
5181
5434
|
return false;
|
|
5182
5435
|
}
|
|
5183
|
-
|
|
5436
|
+
logger31.info(
|
|
5184
5437
|
`Schedule ${schedule._id}: All conditions matched - Date is within range and time matches`
|
|
5185
5438
|
);
|
|
5186
5439
|
return true;
|
|
5187
5440
|
} catch (error) {
|
|
5188
|
-
|
|
5441
|
+
logger31.error(
|
|
5189
5442
|
`Error checking schedule conditions for ${schedule._id}:`,
|
|
5190
5443
|
error
|
|
5191
5444
|
);
|
|
@@ -5194,40 +5447,40 @@ function useScheduleTaskService() {
|
|
|
5194
5447
|
}
|
|
5195
5448
|
async function processScheduledTasks(currentDate) {
|
|
5196
5449
|
try {
|
|
5197
|
-
|
|
5450
|
+
logger31.info("Starting scheduled task processing...");
|
|
5198
5451
|
const scheduleTasks = await getAllScheduleTask();
|
|
5199
5452
|
if (!scheduleTasks || scheduleTasks.length === 0) {
|
|
5200
|
-
|
|
5453
|
+
logger31.info("No schedule tasks found to process");
|
|
5201
5454
|
return { processed: 0, validated: 0 };
|
|
5202
5455
|
}
|
|
5203
|
-
|
|
5456
|
+
logger31.info(`Found ${scheduleTasks.length} schedule tasks to check`);
|
|
5204
5457
|
let processedCount = 0;
|
|
5205
5458
|
let validatedCount = 0;
|
|
5206
5459
|
const validatedTasks = [];
|
|
5207
5460
|
for (const scheduleTask of scheduleTasks) {
|
|
5208
5461
|
try {
|
|
5209
|
-
|
|
5462
|
+
logger31.info(
|
|
5210
5463
|
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, startDate=${scheduleTask.startDate}, endDate=${scheduleTask.endDate}`
|
|
5211
5464
|
);
|
|
5212
5465
|
const shouldRun = checkScheduleConditions(scheduleTask, currentDate);
|
|
5213
5466
|
if (!shouldRun) {
|
|
5214
|
-
|
|
5467
|
+
logger31.info(
|
|
5215
5468
|
`Schedule ${scheduleTask._id} conditions not met, skipping`
|
|
5216
5469
|
);
|
|
5217
5470
|
continue;
|
|
5218
5471
|
}
|
|
5219
|
-
|
|
5472
|
+
logger31.info(
|
|
5220
5473
|
`Schedule ${scheduleTask._id} conditions validated, creating area checklists`
|
|
5221
5474
|
);
|
|
5222
5475
|
if (!scheduleTask._id) {
|
|
5223
|
-
|
|
5476
|
+
logger31.warn(`Schedule ${scheduleTask.title} has no _id, skipping`);
|
|
5224
5477
|
continue;
|
|
5225
5478
|
}
|
|
5226
5479
|
if (!scheduleTask.site) {
|
|
5227
|
-
|
|
5480
|
+
logger31.warn(`Schedule ${scheduleTask._id} has no site, skipping`);
|
|
5228
5481
|
continue;
|
|
5229
5482
|
}
|
|
5230
|
-
|
|
5483
|
+
logger31.info(
|
|
5231
5484
|
`Getting or creating parent checklist for schedule ${scheduleTask._id} in site ${scheduleTask.site}`
|
|
5232
5485
|
);
|
|
5233
5486
|
const parentChecklistIds = await createParentChecklist({
|
|
@@ -5235,7 +5488,7 @@ function useScheduleTaskService() {
|
|
|
5235
5488
|
createdAt: /* @__PURE__ */ new Date()
|
|
5236
5489
|
});
|
|
5237
5490
|
const parentChecklistId = Array.isArray(parentChecklistIds) ? parentChecklistIds[0] : parentChecklistIds;
|
|
5238
|
-
|
|
5491
|
+
logger31.info(
|
|
5239
5492
|
`Using parent checklist ${parentChecklistId}, now creating/updating area checklists`
|
|
5240
5493
|
);
|
|
5241
5494
|
for (const area of scheduleTask.areas) {
|
|
@@ -5248,14 +5501,14 @@ function useScheduleTaskService() {
|
|
|
5248
5501
|
unit: unit.unit.toString(),
|
|
5249
5502
|
name: unit.name
|
|
5250
5503
|
}));
|
|
5251
|
-
|
|
5504
|
+
logger31.info(
|
|
5252
5505
|
`Area ${area.name} (${areaId}): Using units from area details: ${JSON.stringify(
|
|
5253
5506
|
units
|
|
5254
5507
|
)}`
|
|
5255
5508
|
);
|
|
5256
5509
|
}
|
|
5257
5510
|
if (units.length === 0) {
|
|
5258
|
-
|
|
5511
|
+
logger31.warn(
|
|
5259
5512
|
`Area ${area.name} (${areaId}): No units found, skipping area.`
|
|
5260
5513
|
);
|
|
5261
5514
|
continue;
|
|
@@ -5266,11 +5519,11 @@ function useScheduleTaskService() {
|
|
|
5266
5519
|
parentChecklistId.toString(),
|
|
5267
5520
|
areaId
|
|
5268
5521
|
);
|
|
5269
|
-
|
|
5522
|
+
logger31.info(
|
|
5270
5523
|
`Area ${area.name} (${areaId}): Existing area checklist found: ${existingAreaChecklist ? "Yes" : "No"}`
|
|
5271
5524
|
);
|
|
5272
5525
|
if (existingAreaChecklist) {
|
|
5273
|
-
|
|
5526
|
+
logger31.info(
|
|
5274
5527
|
`Area ${area.name} (${areaId}): Existing checklist content: ${JSON.stringify(
|
|
5275
5528
|
existingAreaChecklist.checklist
|
|
5276
5529
|
)}`
|
|
@@ -5278,7 +5531,7 @@ function useScheduleTaskService() {
|
|
|
5278
5531
|
}
|
|
5279
5532
|
} catch (error) {
|
|
5280
5533
|
existingAreaChecklist = null;
|
|
5281
|
-
|
|
5534
|
+
logger31.info(
|
|
5282
5535
|
`Area ${area.name} (${areaId}): No existing area checklist found (exception).`
|
|
5283
5536
|
);
|
|
5284
5537
|
}
|
|
@@ -5292,7 +5545,7 @@ function useScheduleTaskService() {
|
|
|
5292
5545
|
...existingAreaChecklist.checklist || [],
|
|
5293
5546
|
newSet
|
|
5294
5547
|
];
|
|
5295
|
-
|
|
5548
|
+
logger31.info(
|
|
5296
5549
|
`Area ${area.name} (${areaId}): Appending new set ${newSet.set} to checklist. Updated checklist: ${JSON.stringify(
|
|
5297
5550
|
updatedChecklist
|
|
5298
5551
|
)}`
|
|
@@ -5300,7 +5553,7 @@ function useScheduleTaskService() {
|
|
|
5300
5553
|
await updateAreaChecklist(existingAreaChecklist._id, {
|
|
5301
5554
|
checklist: updatedChecklist
|
|
5302
5555
|
});
|
|
5303
|
-
|
|
5556
|
+
logger31.info(
|
|
5304
5557
|
`Appended set ${newSet.set} to area checklist for area ${area.name}`
|
|
5305
5558
|
);
|
|
5306
5559
|
try {
|
|
@@ -5308,13 +5561,13 @@ function useScheduleTaskService() {
|
|
|
5308
5561
|
parentChecklistId.toString(),
|
|
5309
5562
|
areaId
|
|
5310
5563
|
);
|
|
5311
|
-
|
|
5564
|
+
logger31.info(
|
|
5312
5565
|
`Area ${area.name} (${areaId}): Checklist after update: ${JSON.stringify(
|
|
5313
5566
|
verifyChecklist.checklist
|
|
5314
5567
|
)}`
|
|
5315
5568
|
);
|
|
5316
5569
|
} catch (verifyError) {
|
|
5317
|
-
|
|
5570
|
+
logger31.warn(
|
|
5318
5571
|
`Area ${area.name} (${areaId}): Error verifying checklist after update:`,
|
|
5319
5572
|
verifyError
|
|
5320
5573
|
);
|
|
@@ -5333,50 +5586,50 @@ function useScheduleTaskService() {
|
|
|
5333
5586
|
],
|
|
5334
5587
|
createdBy: scheduleTask.createdBy
|
|
5335
5588
|
};
|
|
5336
|
-
|
|
5589
|
+
logger31.info(
|
|
5337
5590
|
`Area ${area.name} (${areaId}): Creating new area checklist with data: ${JSON.stringify(
|
|
5338
5591
|
checklistData
|
|
5339
5592
|
)}`
|
|
5340
5593
|
);
|
|
5341
5594
|
await createAreaChecklist(checklistData);
|
|
5342
|
-
|
|
5595
|
+
logger31.info(`Created new area checklist for area ${area.name}`);
|
|
5343
5596
|
try {
|
|
5344
5597
|
const verifyChecklist = await getAreaChecklistByAreaAndSchedule(
|
|
5345
5598
|
parentChecklistId.toString(),
|
|
5346
5599
|
areaId
|
|
5347
5600
|
);
|
|
5348
|
-
|
|
5601
|
+
logger31.info(
|
|
5349
5602
|
`Area ${area.name} (${areaId}): Checklist after creation: ${JSON.stringify(
|
|
5350
5603
|
verifyChecklist.checklist
|
|
5351
5604
|
)}`
|
|
5352
5605
|
);
|
|
5353
5606
|
} catch (verifyError) {
|
|
5354
|
-
|
|
5607
|
+
logger31.warn(
|
|
5355
5608
|
`Area ${area.name} (${areaId}): Error verifying checklist after creation:`,
|
|
5356
5609
|
verifyError
|
|
5357
5610
|
);
|
|
5358
5611
|
}
|
|
5359
5612
|
}
|
|
5360
5613
|
} catch (error) {
|
|
5361
|
-
|
|
5614
|
+
logger31.error(`Error processing area ${area.name}:`, error);
|
|
5362
5615
|
continue;
|
|
5363
5616
|
}
|
|
5364
5617
|
}
|
|
5365
5618
|
processedCount++;
|
|
5366
5619
|
validatedCount++;
|
|
5367
5620
|
validatedTasks.push(scheduleTask);
|
|
5368
|
-
|
|
5621
|
+
logger31.info(
|
|
5369
5622
|
`Successfully processed schedule ${scheduleTask._id}, created/updated area checklists for all areas.`
|
|
5370
5623
|
);
|
|
5371
5624
|
} catch (error) {
|
|
5372
|
-
|
|
5625
|
+
logger31.error(
|
|
5373
5626
|
`Error processing schedule task ${scheduleTask._id}:`,
|
|
5374
5627
|
error
|
|
5375
5628
|
);
|
|
5376
5629
|
continue;
|
|
5377
5630
|
}
|
|
5378
5631
|
}
|
|
5379
|
-
|
|
5632
|
+
logger31.info(
|
|
5380
5633
|
`Scheduled task processing completed. Processed: ${processedCount}, Validated: ${validatedCount} tasks`
|
|
5381
5634
|
);
|
|
5382
5635
|
return {
|
|
@@ -5385,7 +5638,7 @@ function useScheduleTaskService() {
|
|
|
5385
5638
|
tasks: validatedTasks
|
|
5386
5639
|
};
|
|
5387
5640
|
} catch (error) {
|
|
5388
|
-
|
|
5641
|
+
logger31.error("Error processing scheduled tasks:", error);
|
|
5389
5642
|
throw error;
|
|
5390
5643
|
}
|
|
5391
5644
|
}
|
|
@@ -5394,7 +5647,7 @@ function useScheduleTaskService() {
|
|
|
5394
5647
|
|
|
5395
5648
|
// src/controllers/hygiene-schedule-task.controller.ts
|
|
5396
5649
|
import Joi17 from "joi";
|
|
5397
|
-
import { BadRequestError as BadRequestError30, logger as
|
|
5650
|
+
import { BadRequestError as BadRequestError30, logger as logger32 } from "@7365admin1/node-server-utils";
|
|
5398
5651
|
function useScheduleTaskController() {
|
|
5399
5652
|
const {
|
|
5400
5653
|
createScheduleTask: _createScheduleTask,
|
|
@@ -5412,7 +5665,7 @@ function useScheduleTaskController() {
|
|
|
5412
5665
|
const payload = { ...req.body, ...req.params, createdBy };
|
|
5413
5666
|
const { error } = scheduleTaskSchema.validate(payload);
|
|
5414
5667
|
if (error) {
|
|
5415
|
-
|
|
5668
|
+
logger32.log({ level: "error", message: error.message });
|
|
5416
5669
|
next(new BadRequestError30(error.message));
|
|
5417
5670
|
return;
|
|
5418
5671
|
}
|
|
@@ -5421,7 +5674,7 @@ function useScheduleTaskController() {
|
|
|
5421
5674
|
res.status(201).json({ message: "Schedule task created successfully.", id });
|
|
5422
5675
|
return;
|
|
5423
5676
|
} catch (error2) {
|
|
5424
|
-
|
|
5677
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5425
5678
|
next(error2);
|
|
5426
5679
|
return;
|
|
5427
5680
|
}
|
|
@@ -5436,7 +5689,7 @@ function useScheduleTaskController() {
|
|
|
5436
5689
|
});
|
|
5437
5690
|
const { error } = validation.validate(query);
|
|
5438
5691
|
if (error) {
|
|
5439
|
-
|
|
5692
|
+
logger32.log({ level: "error", message: error.message });
|
|
5440
5693
|
next(new BadRequestError30(error.message));
|
|
5441
5694
|
return;
|
|
5442
5695
|
}
|
|
@@ -5454,7 +5707,7 @@ function useScheduleTaskController() {
|
|
|
5454
5707
|
res.json(data);
|
|
5455
5708
|
return;
|
|
5456
5709
|
} catch (error2) {
|
|
5457
|
-
|
|
5710
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5458
5711
|
next(error2);
|
|
5459
5712
|
return;
|
|
5460
5713
|
}
|
|
@@ -5469,7 +5722,7 @@ function useScheduleTaskController() {
|
|
|
5469
5722
|
});
|
|
5470
5723
|
const { error } = validation.validate(query);
|
|
5471
5724
|
if (error) {
|
|
5472
|
-
|
|
5725
|
+
logger32.log({ level: "error", message: error.message });
|
|
5473
5726
|
next(new BadRequestError30(error.message));
|
|
5474
5727
|
return;
|
|
5475
5728
|
}
|
|
@@ -5487,7 +5740,7 @@ function useScheduleTaskController() {
|
|
|
5487
5740
|
res.json(data);
|
|
5488
5741
|
return;
|
|
5489
5742
|
} catch (error2) {
|
|
5490
|
-
|
|
5743
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5491
5744
|
next(error2);
|
|
5492
5745
|
return;
|
|
5493
5746
|
}
|
|
@@ -5497,7 +5750,7 @@ function useScheduleTaskController() {
|
|
|
5497
5750
|
const _id = req.params.id;
|
|
5498
5751
|
const { error, value } = validation.validate(_id);
|
|
5499
5752
|
if (error) {
|
|
5500
|
-
|
|
5753
|
+
logger32.log({ level: "error", message: error.message });
|
|
5501
5754
|
next(new BadRequestError30(error.message));
|
|
5502
5755
|
return;
|
|
5503
5756
|
}
|
|
@@ -5506,7 +5759,7 @@ function useScheduleTaskController() {
|
|
|
5506
5759
|
res.json(data);
|
|
5507
5760
|
return;
|
|
5508
5761
|
} catch (error2) {
|
|
5509
|
-
|
|
5762
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5510
5763
|
next(error2);
|
|
5511
5764
|
return;
|
|
5512
5765
|
}
|
|
@@ -5529,7 +5782,7 @@ function useScheduleTaskController() {
|
|
|
5529
5782
|
});
|
|
5530
5783
|
const { error } = validation.validate(payload);
|
|
5531
5784
|
if (error) {
|
|
5532
|
-
|
|
5785
|
+
logger32.log({ level: "error", message: error.message });
|
|
5533
5786
|
next(new BadRequestError30(error.message));
|
|
5534
5787
|
return;
|
|
5535
5788
|
}
|
|
@@ -5539,7 +5792,7 @@ function useScheduleTaskController() {
|
|
|
5539
5792
|
res.json({ message: "Schedule task updated successfully." });
|
|
5540
5793
|
return;
|
|
5541
5794
|
} catch (error2) {
|
|
5542
|
-
|
|
5795
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5543
5796
|
next(error2);
|
|
5544
5797
|
return;
|
|
5545
5798
|
}
|
|
@@ -5552,6 +5805,275 @@ function useScheduleTaskController() {
|
|
|
5552
5805
|
updateScheduleTask
|
|
5553
5806
|
};
|
|
5554
5807
|
}
|
|
5808
|
+
|
|
5809
|
+
// src/services/hygiene-qr.service.ts
|
|
5810
|
+
import { logger as logger33 } from "@7365admin1/node-server-utils";
|
|
5811
|
+
import { launch } from "puppeteer";
|
|
5812
|
+
import QRCode from "qrcode";
|
|
5813
|
+
function useQRService() {
|
|
5814
|
+
async function generateQRDataUrl(qrUrl) {
|
|
5815
|
+
return QRCode.toDataURL(qrUrl, {
|
|
5816
|
+
width: 350,
|
|
5817
|
+
margin: 2,
|
|
5818
|
+
errorCorrectionLevel: "M",
|
|
5819
|
+
color: {
|
|
5820
|
+
dark: "#000000",
|
|
5821
|
+
light: "#FFFFFF"
|
|
5822
|
+
}
|
|
5823
|
+
});
|
|
5824
|
+
}
|
|
5825
|
+
async function generateQRImage(qrUrl) {
|
|
5826
|
+
try {
|
|
5827
|
+
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
5828
|
+
const browser = await launch({
|
|
5829
|
+
headless: true,
|
|
5830
|
+
executablePath: process.env.CHROME_BINARY,
|
|
5831
|
+
args: [`--no-sandbox`, `--disable-gpu`, `--disable-dev-shm-usage`]
|
|
5832
|
+
});
|
|
5833
|
+
const page = await browser.newPage();
|
|
5834
|
+
await page.setViewport({
|
|
5835
|
+
width: 400,
|
|
5836
|
+
height: 400
|
|
5837
|
+
});
|
|
5838
|
+
const html = `
|
|
5839
|
+
<!DOCTYPE html>
|
|
5840
|
+
<html>
|
|
5841
|
+
<head>
|
|
5842
|
+
<meta charset="UTF-8">
|
|
5843
|
+
<style>
|
|
5844
|
+
body {
|
|
5845
|
+
margin: 0;
|
|
5846
|
+
padding: 20px;
|
|
5847
|
+
display: flex;
|
|
5848
|
+
justify-content: center;
|
|
5849
|
+
align-items: center;
|
|
5850
|
+
background: white;
|
|
5851
|
+
min-height: 100vh;
|
|
5852
|
+
}
|
|
5853
|
+
#qr-image {
|
|
5854
|
+
display: block;
|
|
5855
|
+
width: 350px;
|
|
5856
|
+
height: 350px;
|
|
5857
|
+
}
|
|
5858
|
+
</style>
|
|
5859
|
+
</head>
|
|
5860
|
+
<body>
|
|
5861
|
+
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
5862
|
+
</body>
|
|
5863
|
+
</html>
|
|
5864
|
+
`;
|
|
5865
|
+
await page.setContent(html, {
|
|
5866
|
+
waitUntil: ["load", "networkidle0"]
|
|
5867
|
+
});
|
|
5868
|
+
await page.waitForSelector("#qr-image", { timeout: 1e4 });
|
|
5869
|
+
const imageBuffer = await page.screenshot({
|
|
5870
|
+
type: "png",
|
|
5871
|
+
clip: {
|
|
5872
|
+
x: 0,
|
|
5873
|
+
y: 0,
|
|
5874
|
+
width: 400,
|
|
5875
|
+
height: 400
|
|
5876
|
+
}
|
|
5877
|
+
});
|
|
5878
|
+
await browser.close();
|
|
5879
|
+
return imageBuffer;
|
|
5880
|
+
} catch (error) {
|
|
5881
|
+
logger33.log({
|
|
5882
|
+
level: "error",
|
|
5883
|
+
message: `Failed to generate QR image: ${error.message}`
|
|
5884
|
+
});
|
|
5885
|
+
throw error;
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5888
|
+
async function generateQRPDF(qrUrl, title) {
|
|
5889
|
+
try {
|
|
5890
|
+
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
5891
|
+
const browser = await launch({
|
|
5892
|
+
headless: true,
|
|
5893
|
+
executablePath: process.env.CHROME_BINARY,
|
|
5894
|
+
args: [`--no-sandbox`, `--disable-gpu`, `--disable-dev-shm-usage`]
|
|
5895
|
+
});
|
|
5896
|
+
const page = await browser.newPage();
|
|
5897
|
+
await page.setViewport({
|
|
5898
|
+
width: 800,
|
|
5899
|
+
height: 1100
|
|
5900
|
+
});
|
|
5901
|
+
const escapedTitle = (title || "Cleaning Schedule QR Code").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5902
|
+
const html = `
|
|
5903
|
+
<!DOCTYPE html>
|
|
5904
|
+
<html>
|
|
5905
|
+
<head>
|
|
5906
|
+
<meta charset="UTF-8">
|
|
5907
|
+
<style>
|
|
5908
|
+
* {
|
|
5909
|
+
margin: 0;
|
|
5910
|
+
padding: 0;
|
|
5911
|
+
box-sizing: border-box;
|
|
5912
|
+
}
|
|
5913
|
+
body {
|
|
5914
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
5915
|
+
background: white;
|
|
5916
|
+
padding: 60px 40px;
|
|
5917
|
+
display: flex;
|
|
5918
|
+
flex-direction: column;
|
|
5919
|
+
align-items: center;
|
|
5920
|
+
justify-content: center;
|
|
5921
|
+
min-height: 100vh;
|
|
5922
|
+
}
|
|
5923
|
+
.qr-container {
|
|
5924
|
+
text-align: center;
|
|
5925
|
+
background: transparent;
|
|
5926
|
+
padding: 0;
|
|
5927
|
+
}
|
|
5928
|
+
h1 {
|
|
5929
|
+
font-size: 28px;
|
|
5930
|
+
color: #333;
|
|
5931
|
+
margin-bottom: 20px;
|
|
5932
|
+
font-weight: 600;
|
|
5933
|
+
}
|
|
5934
|
+
.qr-wrapper {
|
|
5935
|
+
display: inline-block;
|
|
5936
|
+
padding: 0;
|
|
5937
|
+
background: transparent;
|
|
5938
|
+
border: none;
|
|
5939
|
+
border-radius: 0;
|
|
5940
|
+
margin: 20px 0;
|
|
5941
|
+
}
|
|
5942
|
+
#qr-image {
|
|
5943
|
+
display: block;
|
|
5944
|
+
width: 350px;
|
|
5945
|
+
height: 350px;
|
|
5946
|
+
}
|
|
5947
|
+
.instructions {
|
|
5948
|
+
margin-top: 30px;
|
|
5949
|
+
font-size: 16px;
|
|
5950
|
+
color: #666;
|
|
5951
|
+
line-height: 1.6;
|
|
5952
|
+
}
|
|
5953
|
+
.url-info {
|
|
5954
|
+
margin-top: 20px;
|
|
5955
|
+
padding: 15px;
|
|
5956
|
+
background: #f5f5f5;
|
|
5957
|
+
border-radius: 6px;
|
|
5958
|
+
font-size: 12px;
|
|
5959
|
+
color: #888;
|
|
5960
|
+
word-break: break-all;
|
|
5961
|
+
}
|
|
5962
|
+
</style>
|
|
5963
|
+
</head>
|
|
5964
|
+
<body>
|
|
5965
|
+
<div class="qr-container">
|
|
5966
|
+
<h1>${escapedTitle}</h1>
|
|
5967
|
+
<div class="qr-wrapper">
|
|
5968
|
+
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
5969
|
+
</div>
|
|
5970
|
+
<div class="instructions">
|
|
5971
|
+
<p>Scan this QR code to access the cleaning schedule.</p>
|
|
5972
|
+
</div>
|
|
5973
|
+
<div class="url-info">
|
|
5974
|
+
${qrUrl}
|
|
5975
|
+
</div>
|
|
5976
|
+
</div>
|
|
5977
|
+
</body>
|
|
5978
|
+
</html>
|
|
5979
|
+
`;
|
|
5980
|
+
await page.setContent(html, {
|
|
5981
|
+
waitUntil: ["load", "networkidle0"]
|
|
5982
|
+
});
|
|
5983
|
+
await page.waitForSelector("#qr-image", { timeout: 1e4 });
|
|
5984
|
+
await page.waitForFunction(
|
|
5985
|
+
() => {
|
|
5986
|
+
const img = document.getElementById("qr-image");
|
|
5987
|
+
return img && img.complete && img.naturalWidth > 0;
|
|
5988
|
+
},
|
|
5989
|
+
{ timeout: 1e4 }
|
|
5990
|
+
);
|
|
5991
|
+
const pdfBuffer = await page.pdf({
|
|
5992
|
+
format: "A4",
|
|
5993
|
+
printBackground: true,
|
|
5994
|
+
margin: {
|
|
5995
|
+
top: "20mm",
|
|
5996
|
+
right: "20mm",
|
|
5997
|
+
bottom: "20mm",
|
|
5998
|
+
left: "20mm"
|
|
5999
|
+
}
|
|
6000
|
+
});
|
|
6001
|
+
await browser.close();
|
|
6002
|
+
return pdfBuffer;
|
|
6003
|
+
} catch (error) {
|
|
6004
|
+
logger33.log({
|
|
6005
|
+
level: "error",
|
|
6006
|
+
message: `Failed to generate QR PDF: ${error.message}`
|
|
6007
|
+
});
|
|
6008
|
+
throw error;
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
return {
|
|
6012
|
+
generateQRImage,
|
|
6013
|
+
generateQRPDF
|
|
6014
|
+
};
|
|
6015
|
+
}
|
|
6016
|
+
|
|
6017
|
+
// src/controllers/hygiene-qr.controller.ts
|
|
6018
|
+
import Joi18 from "joi";
|
|
6019
|
+
import { BadRequestError as BadRequestError31, logger as logger34 } from "@7365admin1/node-server-utils";
|
|
6020
|
+
function useQRController() {
|
|
6021
|
+
const { generateQRImage: _generateQRImage, generateQRPDF: _generateQRPDF } = useQRService();
|
|
6022
|
+
async function generateQR(req, res, next) {
|
|
6023
|
+
const validation = Joi18.object({
|
|
6024
|
+
url: Joi18.string().uri().required(),
|
|
6025
|
+
filename: Joi18.string().optional().allow("", null),
|
|
6026
|
+
title: Joi18.string().optional().allow("", null),
|
|
6027
|
+
download: Joi18.boolean().optional().default(false)
|
|
6028
|
+
});
|
|
6029
|
+
const query = { ...req.query };
|
|
6030
|
+
const { error, value } = validation.validate(query);
|
|
6031
|
+
if (error) {
|
|
6032
|
+
logger34.log({ level: "error", message: error.message });
|
|
6033
|
+
next(new BadRequestError31(error.message));
|
|
6034
|
+
return;
|
|
6035
|
+
}
|
|
6036
|
+
try {
|
|
6037
|
+
const { url, filename, title, download } = value;
|
|
6038
|
+
if (download) {
|
|
6039
|
+
const pdfBuffer = await _generateQRPDF(url, title);
|
|
6040
|
+
if (!pdfBuffer || pdfBuffer.length === 0) {
|
|
6041
|
+
throw new Error("Generated QR PDF is empty or invalid.");
|
|
6042
|
+
}
|
|
6043
|
+
const sanitizedFilename = (filename || "qrcode").replace(/['"]/g, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6044
|
+
const date = /* @__PURE__ */ new Date();
|
|
6045
|
+
const formattedDate = `${String(date.getMonth() + 1).padStart(
|
|
6046
|
+
2,
|
|
6047
|
+
"0"
|
|
6048
|
+
)}-${String(date.getDate()).padStart(2, "0")}-${date.getFullYear()}`;
|
|
6049
|
+
res.setHeader("Content-Type", "application/pdf");
|
|
6050
|
+
res.setHeader(
|
|
6051
|
+
"Content-Disposition",
|
|
6052
|
+
`attachment; filename="${sanitizedFilename}-${formattedDate}.pdf"`
|
|
6053
|
+
);
|
|
6054
|
+
res.setHeader("Content-Length", pdfBuffer.length);
|
|
6055
|
+
res.end(pdfBuffer);
|
|
6056
|
+
} else {
|
|
6057
|
+
const imageBuffer = await _generateQRImage(url);
|
|
6058
|
+
if (!imageBuffer || imageBuffer.length === 0) {
|
|
6059
|
+
throw new Error("Generated QR image is empty or invalid.");
|
|
6060
|
+
}
|
|
6061
|
+
res.setHeader("Content-Type", "image/png");
|
|
6062
|
+
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
6063
|
+
res.setHeader("Content-Length", imageBuffer.length);
|
|
6064
|
+
res.end(imageBuffer);
|
|
6065
|
+
}
|
|
6066
|
+
return;
|
|
6067
|
+
} catch (error2) {
|
|
6068
|
+
logger34.log({ level: "error", message: error2.message });
|
|
6069
|
+
next(error2);
|
|
6070
|
+
return;
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
return {
|
|
6074
|
+
generateQR
|
|
6075
|
+
};
|
|
6076
|
+
}
|
|
5555
6077
|
export {
|
|
5556
6078
|
MArea,
|
|
5557
6079
|
MAreaChecklist,
|
|
@@ -5578,6 +6100,7 @@ export {
|
|
|
5578
6100
|
useAreaChecklistRepo,
|
|
5579
6101
|
useAreaChecklistService,
|
|
5580
6102
|
useAreaController,
|
|
6103
|
+
useAreaExportService,
|
|
5581
6104
|
useAreaRepo,
|
|
5582
6105
|
useAreaService,
|
|
5583
6106
|
useCheckOutItemController,
|
|
@@ -5587,6 +6110,8 @@ export {
|
|
|
5587
6110
|
useHygieneDashboardRepository,
|
|
5588
6111
|
useParentChecklistController,
|
|
5589
6112
|
useParentChecklistRepo,
|
|
6113
|
+
useQRController,
|
|
6114
|
+
useQRService,
|
|
5590
6115
|
useScheduleTaskController,
|
|
5591
6116
|
useScheduleTaskRepository,
|
|
5592
6117
|
useScheduleTaskService,
|
|
@@ -5596,6 +6121,7 @@ export {
|
|
|
5596
6121
|
useSupplyController,
|
|
5597
6122
|
useSupplyRepository,
|
|
5598
6123
|
useUnitController,
|
|
6124
|
+
useUnitExportService,
|
|
5599
6125
|
useUnitRepository,
|
|
5600
6126
|
useUnitService
|
|
5601
6127
|
};
|