@7365admin1/module-hygiene 4.4.0 → 4.6.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 +49 -5
- package/dist/index.js +1261 -723
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1102 -568
- 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,42 +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`);
|
|
1062
1359
|
skippedRows.push(i + 1);
|
|
1063
1360
|
continue;
|
|
1064
1361
|
}
|
|
1065
1362
|
if (areaName.startsWith("Sample:")) {
|
|
1066
|
-
|
|
1363
|
+
logger8.warn(`Skipping row ${i + 1} with sample area: ${areaName}`);
|
|
1067
1364
|
skippedRows.push(i + 1);
|
|
1068
1365
|
continue;
|
|
1069
1366
|
}
|
|
1070
1367
|
try {
|
|
1071
|
-
const
|
|
1368
|
+
const areaData = {
|
|
1072
1369
|
type: areaType,
|
|
1073
1370
|
name: areaName,
|
|
1074
1371
|
site
|
|
1075
|
-
}
|
|
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);
|
|
1076
1404
|
insertedAreaIds.push(insertedId);
|
|
1077
|
-
|
|
1405
|
+
logger8.info(`Successfully created ${areaType} area: ${areaName}`);
|
|
1078
1406
|
} catch (error) {
|
|
1079
|
-
|
|
1407
|
+
logger8.error(
|
|
1080
1408
|
`Error creating ${areaType} area "${areaName}": ${error.message}`
|
|
1081
1409
|
);
|
|
1082
1410
|
if (error.message.includes("duplicate")) {
|
|
@@ -1096,18 +1424,18 @@ function useAreaService() {
|
|
|
1096
1424
|
if (skippedRows.length > 0) {
|
|
1097
1425
|
message += `, ${skippedRows.length} rows skipped (invalid data)`;
|
|
1098
1426
|
}
|
|
1099
|
-
|
|
1427
|
+
logger8.info(message);
|
|
1100
1428
|
if (insertedAreaIds.length === 0) {
|
|
1101
1429
|
if (duplicateAreas.length > 0 && failedAreas.length === 0 && skippedRows.length === 0) {
|
|
1102
1430
|
return {
|
|
1103
1431
|
message: `No new areas were created. All ${duplicateAreas.length} areas already exist in the system: ${duplicateAreas.join(", ")}`
|
|
1104
1432
|
};
|
|
1105
1433
|
} else if (failedAreas.length > 0) {
|
|
1106
|
-
throw new
|
|
1434
|
+
throw new BadRequestError7(
|
|
1107
1435
|
`No areas were created. ${failedAreas.length} areas failed due to errors. Please check your data format and ensure area names are valid.`
|
|
1108
1436
|
);
|
|
1109
1437
|
} else if (skippedRows.length > 0 && duplicateAreas.length === 0) {
|
|
1110
|
-
throw new
|
|
1438
|
+
throw new BadRequestError7(
|
|
1111
1439
|
`No areas were created. All ${skippedRows.length} rows contained invalid or missing data.`
|
|
1112
1440
|
);
|
|
1113
1441
|
} else {
|
|
@@ -1118,30 +1446,46 @@ function useAreaService() {
|
|
|
1118
1446
|
}
|
|
1119
1447
|
return { message };
|
|
1120
1448
|
} catch (error) {
|
|
1121
|
-
|
|
1122
|
-
if (error instanceof
|
|
1449
|
+
logger8.error("Error while uploading area information", error);
|
|
1450
|
+
if (error instanceof BadRequestError7) {
|
|
1123
1451
|
throw error;
|
|
1124
1452
|
} else if (error.message.includes("validation")) {
|
|
1125
|
-
throw new
|
|
1453
|
+
throw new BadRequestError7(
|
|
1126
1454
|
"Upload failed due to invalid data format. Please check that all required fields are properly filled."
|
|
1127
1455
|
);
|
|
1128
1456
|
} else {
|
|
1129
|
-
throw new
|
|
1457
|
+
throw new BadRequestError7(
|
|
1130
1458
|
`Upload failed: ${error.message || "Please check your data format and try again."}`
|
|
1131
1459
|
);
|
|
1132
1460
|
}
|
|
1133
1461
|
}
|
|
1134
1462
|
}
|
|
1135
|
-
|
|
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 };
|
|
1136
1480
|
}
|
|
1137
1481
|
|
|
1138
1482
|
// src/controllers/hygiene-area.controller.ts
|
|
1139
|
-
import
|
|
1140
|
-
import { BadRequestError as
|
|
1483
|
+
import Joi4 from "joi";
|
|
1484
|
+
import { BadRequestError as BadRequestError8, logger as logger9 } from "@7365admin1/node-server-utils";
|
|
1141
1485
|
|
|
1142
1486
|
// src/utils/convert-excel.util.ts
|
|
1143
1487
|
import { Readable } from "stream";
|
|
1144
|
-
import * as
|
|
1488
|
+
import * as xlsx2 from "xlsx";
|
|
1145
1489
|
function convertBufferFile(bufferFile) {
|
|
1146
1490
|
return new Promise((resolve, reject) => {
|
|
1147
1491
|
const fileStream = Readable.from(bufferFile);
|
|
@@ -1151,10 +1495,10 @@ function convertBufferFile(bufferFile) {
|
|
|
1151
1495
|
});
|
|
1152
1496
|
fileStream.on("end", () => {
|
|
1153
1497
|
try {
|
|
1154
|
-
const workbook =
|
|
1498
|
+
const workbook = xlsx2.read(fileBuffer, { type: "buffer" });
|
|
1155
1499
|
const sheetName = workbook.SheetNames[0];
|
|
1156
1500
|
const sheet = workbook.Sheets[sheetName];
|
|
1157
|
-
const jsonData =
|
|
1501
|
+
const jsonData = xlsx2.utils.sheet_to_json(sheet);
|
|
1158
1502
|
resolve(jsonData);
|
|
1159
1503
|
} catch (error) {
|
|
1160
1504
|
reject("Error parsing file");
|
|
@@ -1175,13 +1519,13 @@ function useAreaController() {
|
|
|
1175
1519
|
updateArea: _updateArea,
|
|
1176
1520
|
deleteArea: _deleteById
|
|
1177
1521
|
} = useAreaRepo();
|
|
1178
|
-
const { importArea: _importArea } = useAreaService();
|
|
1522
|
+
const { importArea: _importArea, exportAreas: _exportAreas } = useAreaService();
|
|
1179
1523
|
async function createArea(req, res, next) {
|
|
1180
1524
|
const payload = { ...req.body, ...req.params };
|
|
1181
1525
|
const { error } = areaSchema.validate(payload);
|
|
1182
1526
|
if (error) {
|
|
1183
|
-
|
|
1184
|
-
next(new
|
|
1527
|
+
logger9.log({ level: "error", message: error.message });
|
|
1528
|
+
next(new BadRequestError8(error.message));
|
|
1185
1529
|
return;
|
|
1186
1530
|
}
|
|
1187
1531
|
try {
|
|
@@ -1189,24 +1533,24 @@ function useAreaController() {
|
|
|
1189
1533
|
res.status(201).json({ message: "Area created successfully.", id });
|
|
1190
1534
|
return;
|
|
1191
1535
|
} catch (error2) {
|
|
1192
|
-
|
|
1536
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1193
1537
|
next(error2);
|
|
1194
1538
|
return;
|
|
1195
1539
|
}
|
|
1196
1540
|
}
|
|
1197
1541
|
async function getAreas(req, res, next) {
|
|
1198
1542
|
const query = { ...req.query, ...req.params };
|
|
1199
|
-
const validation =
|
|
1200
|
-
page:
|
|
1201
|
-
limit:
|
|
1202
|
-
search:
|
|
1203
|
-
type:
|
|
1204
|
-
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()
|
|
1205
1549
|
});
|
|
1206
1550
|
const { error } = validation.validate(query);
|
|
1207
1551
|
if (error) {
|
|
1208
|
-
|
|
1209
|
-
next(new
|
|
1552
|
+
logger9.log({ level: "error", message: error.message });
|
|
1553
|
+
next(new BadRequestError8(error.message));
|
|
1210
1554
|
return;
|
|
1211
1555
|
}
|
|
1212
1556
|
const page = parseInt(req.query.page) ?? 1;
|
|
@@ -1230,12 +1574,12 @@ function useAreaController() {
|
|
|
1230
1574
|
}
|
|
1231
1575
|
}
|
|
1232
1576
|
async function getAreaById(req, res, next) {
|
|
1233
|
-
const validation =
|
|
1577
|
+
const validation = Joi4.string().hex().required();
|
|
1234
1578
|
const _id = req.params.id;
|
|
1235
1579
|
const { error, value } = validation.validate(_id);
|
|
1236
1580
|
if (error) {
|
|
1237
|
-
|
|
1238
|
-
next(new
|
|
1581
|
+
logger9.log({ level: "error", message: error.message });
|
|
1582
|
+
next(new BadRequestError8(error.message));
|
|
1239
1583
|
return;
|
|
1240
1584
|
}
|
|
1241
1585
|
try {
|
|
@@ -1243,22 +1587,22 @@ function useAreaController() {
|
|
|
1243
1587
|
res.json(data);
|
|
1244
1588
|
return;
|
|
1245
1589
|
} catch (error2) {
|
|
1246
|
-
|
|
1590
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1247
1591
|
next(error2);
|
|
1248
1592
|
return;
|
|
1249
1593
|
}
|
|
1250
1594
|
}
|
|
1251
1595
|
async function updateArea(req, res, next) {
|
|
1252
1596
|
const payload = { id: req.params.id, ...req.body };
|
|
1253
|
-
const schema =
|
|
1254
|
-
id:
|
|
1255
|
-
name:
|
|
1256
|
-
type:
|
|
1257
|
-
set:
|
|
1258
|
-
units:
|
|
1259
|
-
|
|
1260
|
-
unit:
|
|
1261
|
-
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()
|
|
1262
1606
|
}).required()
|
|
1263
1607
|
).min(1).unique("unit", { ignoreUndefined: true }).optional().messages({
|
|
1264
1608
|
"array.unique": "Duplicate area units are not allowed"
|
|
@@ -1266,8 +1610,8 @@ function useAreaController() {
|
|
|
1266
1610
|
});
|
|
1267
1611
|
const { error } = schema.validate(payload);
|
|
1268
1612
|
if (error) {
|
|
1269
|
-
|
|
1270
|
-
next(new
|
|
1613
|
+
logger9.log({ level: "error", message: error.message });
|
|
1614
|
+
next(new BadRequestError8(error.message));
|
|
1271
1615
|
return;
|
|
1272
1616
|
}
|
|
1273
1617
|
try {
|
|
@@ -1276,299 +1620,140 @@ function useAreaController() {
|
|
|
1276
1620
|
res.json({ message: "Area updated successfully." });
|
|
1277
1621
|
return;
|
|
1278
1622
|
} catch (error2) {
|
|
1279
|
-
|
|
1623
|
+
logger9.log({ level: "error", message: error2.message });
|
|
1280
1624
|
next(error2);
|
|
1281
1625
|
return;
|
|
1282
1626
|
}
|
|
1283
1627
|
}
|
|
1284
|
-
async function deleteArea(req, res, next) {
|
|
1285
|
-
const id = req.params.id;
|
|
1286
|
-
const validation =
|
|
1287
|
-
const { error, value } = validation.validate(id);
|
|
1288
|
-
if (error) {
|
|
1289
|
-
|
|
1290
|
-
next(new
|
|
1291
|
-
return;
|
|
1292
|
-
}
|
|
1293
|
-
try {
|
|
1294
|
-
await _deleteById(value);
|
|
1295
|
-
res.json({ message: "Area deleted successfully." });
|
|
1296
|
-
return;
|
|
1297
|
-
} catch (error2) {
|
|
1298
|
-
logger6.log({ level: "error", message: error2.message });
|
|
1299
|
-
next(error2);
|
|
1300
|
-
return;
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
async function importArea(req, res, next) {
|
|
1304
|
-
if (!req.file) {
|
|
1305
|
-
next(new BadRequestError6("File is required!"));
|
|
1306
|
-
return;
|
|
1307
|
-
}
|
|
1308
|
-
const { site } = req.params;
|
|
1309
|
-
const schema = Joi3.string().hex().required();
|
|
1310
|
-
const { error, value } = schema.validate(site);
|
|
1311
|
-
if (error) {
|
|
1312
|
-
logger6.log({ level: "error", message: error.message });
|
|
1313
|
-
next(new BadRequestError6(error.message));
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
try {
|
|
1317
|
-
const xlsData = await convertBufferFile(req.file.buffer);
|
|
1318
|
-
const dataJson = JSON.stringify(xlsData);
|
|
1319
|
-
const result = await _importArea({ dataJson, site: value });
|
|
1320
|
-
return res.status(201).json(result);
|
|
1321
|
-
} catch (error2) {
|
|
1322
|
-
logger6.log({ level: "error", message: error2.message });
|
|
1323
|
-
next(error2);
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
return {
|
|
1328
|
-
createArea,
|
|
1329
|
-
getAreas,
|
|
1330
|
-
getAreaById,
|
|
1331
|
-
updateArea,
|
|
1332
|
-
deleteArea,
|
|
1333
|
-
importArea
|
|
1334
|
-
};
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
// src/models/hygiene-unit.model.ts
|
|
1338
|
-
import Joi4 from "joi";
|
|
1339
|
-
import { ObjectId as ObjectId4 } from "mongodb";
|
|
1340
|
-
import { BadRequestError as BadRequestError7, logger as logger7 } from "@7365admin1/node-server-utils";
|
|
1341
|
-
var unitSchema = Joi4.object({
|
|
1342
|
-
site: Joi4.string().hex().required(),
|
|
1343
|
-
name: Joi4.string().required()
|
|
1344
|
-
});
|
|
1345
|
-
function MUnit(value) {
|
|
1346
|
-
const { error } = unitSchema.validate(value);
|
|
1347
|
-
if (error) {
|
|
1348
|
-
logger7.info(`Hygiene Unit Model: ${error.message}`);
|
|
1349
|
-
throw new BadRequestError7(error.message);
|
|
1350
|
-
}
|
|
1351
|
-
if (value.site) {
|
|
1352
|
-
try {
|
|
1353
|
-
value.site = new ObjectId4(value.site);
|
|
1354
|
-
} catch (error2) {
|
|
1355
|
-
throw new BadRequestError7("Invalid site ID format.");
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
return {
|
|
1359
|
-
site: value.site,
|
|
1360
|
-
name: value.name,
|
|
1361
|
-
status: "active",
|
|
1362
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
1363
|
-
updatedAt: "",
|
|
1364
|
-
deletedAt: ""
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
// src/services/hygiene-unit.service.ts
|
|
1369
|
-
import {
|
|
1370
|
-
BadRequestError as BadRequestError9,
|
|
1371
|
-
logger as logger9,
|
|
1372
|
-
NotFoundError as NotFoundError3,
|
|
1373
|
-
useAtlas as useAtlas4
|
|
1374
|
-
} from "@7365admin1/node-server-utils";
|
|
1375
|
-
|
|
1376
|
-
// src/repositories/hygiene-unit.repository.ts
|
|
1377
|
-
import { ObjectId as ObjectId5 } from "mongodb";
|
|
1378
|
-
import {
|
|
1379
|
-
useAtlas as useAtlas3,
|
|
1380
|
-
InternalServerError as InternalServerError3,
|
|
1381
|
-
paginate as paginate2,
|
|
1382
|
-
BadRequestError as BadRequestError8,
|
|
1383
|
-
useCache as useCache3,
|
|
1384
|
-
logger as logger8,
|
|
1385
|
-
makeCacheKey as makeCacheKey3
|
|
1386
|
-
} from "@7365admin1/node-server-utils";
|
|
1387
|
-
function useUnitRepository() {
|
|
1388
|
-
const db = useAtlas3.getDb();
|
|
1389
|
-
if (!db) {
|
|
1390
|
-
throw new InternalServerError3("Unable to connect to server.");
|
|
1391
|
-
}
|
|
1392
|
-
const namespace_collection = "site.cleaning.area.unit";
|
|
1393
|
-
const collection = db.collection(namespace_collection);
|
|
1394
|
-
const { delNamespace, setCache, getCache } = useCache3(namespace_collection);
|
|
1395
|
-
async function createIndex() {
|
|
1396
|
-
try {
|
|
1397
|
-
await collection.createIndexes([
|
|
1398
|
-
{ key: { site: 1 } },
|
|
1399
|
-
{ key: { status: 1 } }
|
|
1400
|
-
]);
|
|
1401
|
-
} catch (error) {
|
|
1402
|
-
throw new InternalServerError3("Failed to create index on hygiene unit.");
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
async function createTextIndex() {
|
|
1406
|
-
try {
|
|
1407
|
-
await collection.createIndex({ name: "text" });
|
|
1408
|
-
} catch (error) {
|
|
1409
|
-
throw new InternalServerError3(
|
|
1410
|
-
"Failed to create text index on hygiene unit."
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
async function createUniqueIndex() {
|
|
1415
|
-
try {
|
|
1416
|
-
await collection.createIndex(
|
|
1417
|
-
{ site: 1, name: 1, deletedAt: 1 },
|
|
1418
|
-
{ unique: true }
|
|
1419
|
-
);
|
|
1420
|
-
} catch (error) {
|
|
1421
|
-
throw new InternalServerError3(
|
|
1422
|
-
"Failed to create unique index on hygiene unit."
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
async function createUnit(value, session) {
|
|
1427
|
-
try {
|
|
1428
|
-
value = MUnit(value);
|
|
1429
|
-
const res = await collection.insertOne(value, { session });
|
|
1430
|
-
delNamespace().then(() => {
|
|
1431
|
-
logger8.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1432
|
-
}).catch((err) => {
|
|
1433
|
-
logger8.error(
|
|
1434
|
-
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1435
|
-
err
|
|
1436
|
-
);
|
|
1437
|
-
});
|
|
1438
|
-
return res.insertedId;
|
|
1439
|
-
} catch (error) {
|
|
1440
|
-
const isDuplicated = error.message.includes("duplicate");
|
|
1441
|
-
if (isDuplicated) {
|
|
1442
|
-
throw new BadRequestError8("Unit already exists.");
|
|
1443
|
-
}
|
|
1444
|
-
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;
|
|
1445
1636
|
}
|
|
1446
|
-
}
|
|
1447
|
-
async function getUnits({
|
|
1448
|
-
page = 1,
|
|
1449
|
-
limit = 10,
|
|
1450
|
-
search = "",
|
|
1451
|
-
site
|
|
1452
|
-
}) {
|
|
1453
|
-
page = page > 0 ? page - 1 : 0;
|
|
1454
|
-
const query = {
|
|
1455
|
-
status: { $ne: "deleted" }
|
|
1456
|
-
};
|
|
1457
|
-
const cacheOptions = {
|
|
1458
|
-
page,
|
|
1459
|
-
limit
|
|
1460
|
-
};
|
|
1461
1637
|
try {
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
} catch (
|
|
1466
|
-
|
|
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;
|
|
1467
1645
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1646
|
+
}
|
|
1647
|
+
async function importArea(req, res, next) {
|
|
1648
|
+
if (!req.file) {
|
|
1649
|
+
next(new BadRequestError8("File is required!"));
|
|
1650
|
+
return;
|
|
1471
1651
|
}
|
|
1472
|
-
const
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
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;
|
|
1477
1659
|
}
|
|
1478
1660
|
try {
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
const data = paginate2(items, page, limit, length);
|
|
1488
|
-
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
1489
|
-
logger8.info(`Cache set for key: ${cacheKey}`);
|
|
1490
|
-
}).catch((err) => {
|
|
1491
|
-
logger8.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
1492
|
-
});
|
|
1493
|
-
return data;
|
|
1494
|
-
} catch (error) {
|
|
1495
|
-
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;
|
|
1496
1669
|
}
|
|
1497
1670
|
}
|
|
1498
|
-
async function
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
}
|
|
1502
|
-
|
|
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;
|
|
1503
1679
|
}
|
|
1504
1680
|
try {
|
|
1505
|
-
const
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
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"
|
|
1510
1690
|
);
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
return res.modifiedCount;
|
|
1523
|
-
} catch (error) {
|
|
1524
|
-
const isDuplicated = error.message.includes("duplicate");
|
|
1525
|
-
if (isDuplicated) {
|
|
1526
|
-
throw new BadRequestError8("Area already exists.");
|
|
1527
|
-
}
|
|
1528
|
-
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;
|
|
1529
1702
|
}
|
|
1530
1703
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
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]);
|
|
1550
1734
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
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"
|
|
1558
1745
|
});
|
|
1559
|
-
return
|
|
1746
|
+
return excelBuffer;
|
|
1560
1747
|
} catch (error) {
|
|
1748
|
+
logger10.log({
|
|
1749
|
+
level: "error",
|
|
1750
|
+
message: `Failed to generate unit Excel: ${error.message}`
|
|
1751
|
+
});
|
|
1561
1752
|
throw error;
|
|
1562
1753
|
}
|
|
1563
1754
|
}
|
|
1564
1755
|
return {
|
|
1565
|
-
|
|
1566
|
-
createTextIndex,
|
|
1567
|
-
createUniqueIndex,
|
|
1568
|
-
createUnit,
|
|
1569
|
-
getUnits,
|
|
1570
|
-
updateUnit,
|
|
1571
|
-
deleteUnit
|
|
1756
|
+
generateUnitExcel
|
|
1572
1757
|
};
|
|
1573
1758
|
}
|
|
1574
1759
|
|
|
@@ -1601,18 +1786,18 @@ function useUnitService() {
|
|
|
1601
1786
|
for (let i = 0; i < dataArray.length; i++) {
|
|
1602
1787
|
const row = dataArray[i];
|
|
1603
1788
|
if (!row?.UNIT) {
|
|
1604
|
-
|
|
1789
|
+
logger11.warn(`Skipping row ${i + 1} with missing UNIT:`, row);
|
|
1605
1790
|
skippedRows.push(i + 1);
|
|
1606
1791
|
continue;
|
|
1607
1792
|
}
|
|
1608
1793
|
const unitName = String(row.UNIT).trim();
|
|
1609
1794
|
if (!unitName) {
|
|
1610
|
-
|
|
1795
|
+
logger11.warn(`Skipping row ${i + 1} with empty unit name`);
|
|
1611
1796
|
skippedRows.push(i + 1);
|
|
1612
1797
|
continue;
|
|
1613
1798
|
}
|
|
1614
1799
|
if (unitName.startsWith("Sample:")) {
|
|
1615
|
-
|
|
1800
|
+
logger11.warn(`Skipping row ${i + 1} with sample unit: ${unitName}`);
|
|
1616
1801
|
skippedRows.push(i + 1);
|
|
1617
1802
|
continue;
|
|
1618
1803
|
}
|
|
@@ -1622,9 +1807,9 @@ function useUnitService() {
|
|
|
1622
1807
|
site
|
|
1623
1808
|
});
|
|
1624
1809
|
insertedUnitIds.push(insertedId);
|
|
1625
|
-
|
|
1810
|
+
logger11.info(`Successfully created unit: ${unitName}`);
|
|
1626
1811
|
} catch (error) {
|
|
1627
|
-
|
|
1812
|
+
logger11.error(`Error creating unit "${unitName}": ${error.message}`);
|
|
1628
1813
|
if (error.message.includes("Unit already exists")) {
|
|
1629
1814
|
duplicateUnits.push(unitName);
|
|
1630
1815
|
} else {
|
|
@@ -1642,7 +1827,7 @@ function useUnitService() {
|
|
|
1642
1827
|
if (skippedRows.length > 0) {
|
|
1643
1828
|
message += `, ${skippedRows.length} rows skipped (invalid data)`;
|
|
1644
1829
|
}
|
|
1645
|
-
|
|
1830
|
+
logger11.info(message);
|
|
1646
1831
|
if (insertedUnitIds.length === 0) {
|
|
1647
1832
|
if (duplicateUnits.length > 0 && failedUnits.length === 0) {
|
|
1648
1833
|
throw new BadRequestError9(
|
|
@@ -1660,7 +1845,7 @@ function useUnitService() {
|
|
|
1660
1845
|
}
|
|
1661
1846
|
return { message };
|
|
1662
1847
|
} catch (error) {
|
|
1663
|
-
|
|
1848
|
+
logger11.error("Error while uploading unit information", error);
|
|
1664
1849
|
if (error instanceof BadRequestError9) {
|
|
1665
1850
|
throw error;
|
|
1666
1851
|
} else if (error.message.includes("duplicate")) {
|
|
@@ -1690,7 +1875,7 @@ function useUnitService() {
|
|
|
1690
1875
|
await session?.commitTransaction();
|
|
1691
1876
|
return result;
|
|
1692
1877
|
} catch (error) {
|
|
1693
|
-
|
|
1878
|
+
logger11.error(`Error updating unit and associated areas:`, error);
|
|
1694
1879
|
if (session?.inTransaction()) {
|
|
1695
1880
|
await session?.abortTransaction();
|
|
1696
1881
|
}
|
|
@@ -1711,7 +1896,7 @@ function useUnitService() {
|
|
|
1711
1896
|
await session?.commitTransaction();
|
|
1712
1897
|
return result;
|
|
1713
1898
|
} catch (error) {
|
|
1714
|
-
|
|
1899
|
+
logger11.error(`Error deleting unit:`, error);
|
|
1715
1900
|
if (session?.inTransaction()) {
|
|
1716
1901
|
await session?.abortTransaction();
|
|
1717
1902
|
}
|
|
@@ -1720,24 +1905,48 @@ function useUnitService() {
|
|
|
1720
1905
|
session?.endSession();
|
|
1721
1906
|
}
|
|
1722
1907
|
}
|
|
1723
|
-
|
|
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 };
|
|
1724
1932
|
}
|
|
1725
1933
|
|
|
1726
1934
|
// src/controllers/hygiene-unit.controller.ts
|
|
1727
1935
|
import Joi5 from "joi";
|
|
1728
|
-
import { BadRequestError as BadRequestError10, logger as
|
|
1936
|
+
import { BadRequestError as BadRequestError10, logger as logger12 } from "@7365admin1/node-server-utils";
|
|
1729
1937
|
function useUnitController() {
|
|
1730
1938
|
const { createUnit: _createUnit, getUnits: _getUnits } = useUnitRepository();
|
|
1731
1939
|
const {
|
|
1732
1940
|
updateUnit: _updateUnit,
|
|
1733
1941
|
deleteUnit: _deleteUnit,
|
|
1734
|
-
importUnit: _importUnit
|
|
1942
|
+
importUnit: _importUnit,
|
|
1943
|
+
exportUnits: _exportUnits
|
|
1735
1944
|
} = useUnitService();
|
|
1736
1945
|
async function createUnit(req, res, next) {
|
|
1737
1946
|
const payload = { ...req.body, ...req.params };
|
|
1738
1947
|
const { error } = unitSchema.validate(payload);
|
|
1739
1948
|
if (error) {
|
|
1740
|
-
|
|
1949
|
+
logger12.log({ level: "error", message: error.message });
|
|
1741
1950
|
next(new BadRequestError10(error.message));
|
|
1742
1951
|
return;
|
|
1743
1952
|
}
|
|
@@ -1746,7 +1955,7 @@ function useUnitController() {
|
|
|
1746
1955
|
res.status(201).json({ message: "Unit created successfully.", id });
|
|
1747
1956
|
return;
|
|
1748
1957
|
} catch (error2) {
|
|
1749
|
-
|
|
1958
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1750
1959
|
next(error2);
|
|
1751
1960
|
return;
|
|
1752
1961
|
}
|
|
@@ -1761,7 +1970,7 @@ function useUnitController() {
|
|
|
1761
1970
|
});
|
|
1762
1971
|
const { error } = validation.validate(query);
|
|
1763
1972
|
if (error) {
|
|
1764
|
-
|
|
1973
|
+
logger12.log({ level: "error", message: error.message });
|
|
1765
1974
|
next(new BadRequestError10(error.message));
|
|
1766
1975
|
return;
|
|
1767
1976
|
}
|
|
@@ -1779,7 +1988,7 @@ function useUnitController() {
|
|
|
1779
1988
|
res.json(data);
|
|
1780
1989
|
return;
|
|
1781
1990
|
} catch (error2) {
|
|
1782
|
-
|
|
1991
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1783
1992
|
next(error2);
|
|
1784
1993
|
return;
|
|
1785
1994
|
}
|
|
@@ -1792,7 +2001,7 @@ function useUnitController() {
|
|
|
1792
2001
|
});
|
|
1793
2002
|
const { error } = validation.validate(payload);
|
|
1794
2003
|
if (error) {
|
|
1795
|
-
|
|
2004
|
+
logger12.log({ level: "error", message: error.message });
|
|
1796
2005
|
next(new BadRequestError10(error.message));
|
|
1797
2006
|
return;
|
|
1798
2007
|
}
|
|
@@ -1802,7 +2011,7 @@ function useUnitController() {
|
|
|
1802
2011
|
res.json({ message: "Unit updated successfully." });
|
|
1803
2012
|
return;
|
|
1804
2013
|
} catch (error2) {
|
|
1805
|
-
|
|
2014
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1806
2015
|
next(error2);
|
|
1807
2016
|
return;
|
|
1808
2017
|
}
|
|
@@ -1814,7 +2023,7 @@ function useUnitController() {
|
|
|
1814
2023
|
});
|
|
1815
2024
|
const { error, value } = validation.validate({ id });
|
|
1816
2025
|
if (error) {
|
|
1817
|
-
|
|
2026
|
+
logger12.log({ level: "error", message: error.message });
|
|
1818
2027
|
next(new BadRequestError10(error.message));
|
|
1819
2028
|
return;
|
|
1820
2029
|
}
|
|
@@ -1823,7 +2032,7 @@ function useUnitController() {
|
|
|
1823
2032
|
res.json({ message: "Unit deleted successfully." });
|
|
1824
2033
|
return;
|
|
1825
2034
|
} catch (error2) {
|
|
1826
|
-
|
|
2035
|
+
logger12.log({ level: "error", message: error2.message });
|
|
1827
2036
|
next(error2);
|
|
1828
2037
|
return;
|
|
1829
2038
|
}
|
|
@@ -1837,7 +2046,7 @@ function useUnitController() {
|
|
|
1837
2046
|
const validation = Joi5.string().hex().required();
|
|
1838
2047
|
const { error, value } = validation.validate(site);
|
|
1839
2048
|
if (error) {
|
|
1840
|
-
|
|
2049
|
+
logger12.log({ level: "error", message: error.message });
|
|
1841
2050
|
next(new BadRequestError10(error.message));
|
|
1842
2051
|
return;
|
|
1843
2052
|
}
|
|
@@ -1847,7 +2056,40 @@ function useUnitController() {
|
|
|
1847
2056
|
const result = await _importUnit({ dataJson, site: value });
|
|
1848
2057
|
return res.status(201).json(result);
|
|
1849
2058
|
} catch (error2) {
|
|
1850
|
-
|
|
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 });
|
|
1851
2093
|
next(error2);
|
|
1852
2094
|
return;
|
|
1853
2095
|
}
|
|
@@ -1857,14 +2099,15 @@ function useUnitController() {
|
|
|
1857
2099
|
getUnits,
|
|
1858
2100
|
updateUnit,
|
|
1859
2101
|
deleteUnit,
|
|
1860
|
-
importUnit
|
|
2102
|
+
importUnit,
|
|
2103
|
+
exportUnits
|
|
1861
2104
|
};
|
|
1862
2105
|
}
|
|
1863
2106
|
|
|
1864
2107
|
// src/models/hygiene-parent-checklist.model.ts
|
|
1865
2108
|
import Joi6 from "joi";
|
|
1866
2109
|
import { ObjectId as ObjectId6 } from "mongodb";
|
|
1867
|
-
import { BadRequestError as BadRequestError11, logger as
|
|
2110
|
+
import { BadRequestError as BadRequestError11, logger as logger13 } from "@7365admin1/node-server-utils";
|
|
1868
2111
|
var parentChecklistSchema = Joi6.object({
|
|
1869
2112
|
createdAt: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null),
|
|
1870
2113
|
site: Joi6.string().hex().required()
|
|
@@ -1872,7 +2115,7 @@ var parentChecklistSchema = Joi6.object({
|
|
|
1872
2115
|
function MParentChecklist(value) {
|
|
1873
2116
|
const { error } = parentChecklistSchema.validate(value);
|
|
1874
2117
|
if (error) {
|
|
1875
|
-
|
|
2118
|
+
logger13.info(`Hygiene Parent Checklist Model: ${error.message}`);
|
|
1876
2119
|
throw new BadRequestError11(error.message);
|
|
1877
2120
|
}
|
|
1878
2121
|
if (value.site) {
|
|
@@ -1897,7 +2140,7 @@ import {
|
|
|
1897
2140
|
InternalServerError as InternalServerError4,
|
|
1898
2141
|
paginate as paginate3,
|
|
1899
2142
|
useCache as useCache4,
|
|
1900
|
-
logger as
|
|
2143
|
+
logger as logger14,
|
|
1901
2144
|
makeCacheKey as makeCacheKey4,
|
|
1902
2145
|
BadRequestError as BadRequestError12
|
|
1903
2146
|
} from "@7365admin1/node-server-utils";
|
|
@@ -1940,7 +2183,7 @@ function useParentChecklistRepo() {
|
|
|
1940
2183
|
if (value.site) {
|
|
1941
2184
|
try {
|
|
1942
2185
|
existingQuery.site = new ObjectId7(value.site);
|
|
1943
|
-
|
|
2186
|
+
logger14.info(
|
|
1944
2187
|
`createParentChecklist: Looking for existing checklist with query: ${JSON.stringify(
|
|
1945
2188
|
{ ...existingQuery, site: value.site }
|
|
1946
2189
|
)}`
|
|
@@ -1949,7 +2192,7 @@ function useParentChecklistRepo() {
|
|
|
1949
2192
|
throw new BadRequestError12("Invalid site ID format.");
|
|
1950
2193
|
}
|
|
1951
2194
|
} else {
|
|
1952
|
-
|
|
2195
|
+
logger14.info(
|
|
1953
2196
|
`createParentChecklist: Looking for existing checklist with query (no site filter): ${JSON.stringify(
|
|
1954
2197
|
existingQuery
|
|
1955
2198
|
)}`
|
|
@@ -1958,7 +2201,7 @@ function useParentChecklistRepo() {
|
|
|
1958
2201
|
const existingChecklist = await collection.findOne(existingQuery);
|
|
1959
2202
|
if (existingChecklist) {
|
|
1960
2203
|
const dateStr2 = currentDate.toISOString().split("T")[0];
|
|
1961
|
-
|
|
2204
|
+
logger14.info(
|
|
1962
2205
|
`Parent checklist already exists for ${value.site ? `site ${value.site} on` : ""} today: ${dateStr2}. Found checklist: ${JSON.stringify({
|
|
1963
2206
|
_id: existingChecklist._id,
|
|
1964
2207
|
site: existingChecklist.site
|
|
@@ -1973,26 +2216,26 @@ function useParentChecklistRepo() {
|
|
|
1973
2216
|
});
|
|
1974
2217
|
const result2 = await collection.insertOne(checklistDoc, { session });
|
|
1975
2218
|
delNamespace().then(() => {
|
|
1976
|
-
|
|
2219
|
+
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1977
2220
|
}).catch((err) => {
|
|
1978
|
-
|
|
2221
|
+
logger14.error(
|
|
1979
2222
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1980
2223
|
err
|
|
1981
2224
|
);
|
|
1982
2225
|
});
|
|
1983
2226
|
const dateStr2 = currentDate.toISOString().split("T")[0];
|
|
1984
|
-
|
|
2227
|
+
logger14.info(
|
|
1985
2228
|
`Created parent checklist for site ${value.site} for today: ${dateStr2}`
|
|
1986
2229
|
);
|
|
1987
2230
|
return result2.insertedId;
|
|
1988
2231
|
}
|
|
1989
2232
|
const siteIds = await getHygieneSiteIds();
|
|
1990
2233
|
if (!Array.isArray(siteIds)) {
|
|
1991
|
-
|
|
2234
|
+
logger14.error("getHygieneSiteIds returned non-array value:", siteIds);
|
|
1992
2235
|
throw new InternalServerError4("Failed to retrieve site IDs");
|
|
1993
2236
|
}
|
|
1994
2237
|
if (siteIds.length === 0) {
|
|
1995
|
-
|
|
2238
|
+
logger14.warn("No sites found for creating parent checklist");
|
|
1996
2239
|
throw new BadRequestError12("No sites available for checklist creation");
|
|
1997
2240
|
}
|
|
1998
2241
|
const checklistDocs = [];
|
|
@@ -2006,20 +2249,20 @@ function useParentChecklistRepo() {
|
|
|
2006
2249
|
}
|
|
2007
2250
|
const result = await collection.insertMany(checklistDocs, { session });
|
|
2008
2251
|
delNamespace().then(() => {
|
|
2009
|
-
|
|
2252
|
+
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2010
2253
|
}).catch((err) => {
|
|
2011
|
-
|
|
2254
|
+
logger14.error(
|
|
2012
2255
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2013
2256
|
err
|
|
2014
2257
|
);
|
|
2015
2258
|
});
|
|
2016
2259
|
const dateStr = currentDate.toISOString().split("T")[0];
|
|
2017
|
-
|
|
2260
|
+
logger14.info(
|
|
2018
2261
|
`Created ${Object.keys(result.insertedIds).length} parent checklists for today: ${dateStr}`
|
|
2019
2262
|
);
|
|
2020
2263
|
return Object.values(result.insertedIds);
|
|
2021
2264
|
} catch (error) {
|
|
2022
|
-
|
|
2265
|
+
logger14.error("Failed to create daily parent checklist", error);
|
|
2023
2266
|
throw error;
|
|
2024
2267
|
}
|
|
2025
2268
|
}
|
|
@@ -2070,7 +2313,7 @@ function useParentChecklistRepo() {
|
|
|
2070
2313
|
const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
|
|
2071
2314
|
const cachedData = await getCache(cacheKey);
|
|
2072
2315
|
if (cachedData) {
|
|
2073
|
-
|
|
2316
|
+
logger14.info(`Cache hit for key: ${cacheKey}`);
|
|
2074
2317
|
return cachedData;
|
|
2075
2318
|
}
|
|
2076
2319
|
try {
|
|
@@ -2105,9 +2348,9 @@ function useParentChecklistRepo() {
|
|
|
2105
2348
|
const length = await collection.countDocuments(query);
|
|
2106
2349
|
const data = paginate3(items, page, limit, length);
|
|
2107
2350
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2108
|
-
|
|
2351
|
+
logger14.info(`Cache set for key: ${cacheKey}`);
|
|
2109
2352
|
}).catch((err) => {
|
|
2110
|
-
|
|
2353
|
+
logger14.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2111
2354
|
});
|
|
2112
2355
|
return data;
|
|
2113
2356
|
} catch (error) {
|
|
@@ -2139,9 +2382,9 @@ function useParentChecklistRepo() {
|
|
|
2139
2382
|
);
|
|
2140
2383
|
}
|
|
2141
2384
|
delNamespace().then(() => {
|
|
2142
|
-
|
|
2385
|
+
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2143
2386
|
}).catch((err) => {
|
|
2144
|
-
|
|
2387
|
+
logger14.error(
|
|
2145
2388
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2146
2389
|
err
|
|
2147
2390
|
);
|
|
@@ -2158,7 +2401,7 @@ function useParentChecklistRepo() {
|
|
|
2158
2401
|
const cacheKey = makeCacheKey4(site_collection, { tag: "site-ids" });
|
|
2159
2402
|
const cachedData = await getCache(cacheKey);
|
|
2160
2403
|
if (cachedData && Array.isArray(cachedData)) {
|
|
2161
|
-
|
|
2404
|
+
logger14.info(`Cache hit for key: ${cacheKey}`);
|
|
2162
2405
|
return cachedData;
|
|
2163
2406
|
}
|
|
2164
2407
|
try {
|
|
@@ -2170,14 +2413,14 @@ function useParentChecklistRepo() {
|
|
|
2170
2413
|
const siteIds = items.map((item) => item._id.toString());
|
|
2171
2414
|
if (siteIds.length > 0) {
|
|
2172
2415
|
setCache(cacheKey, siteIds, 15 * 60).then(() => {
|
|
2173
|
-
|
|
2416
|
+
logger14.info(`Cache set for key: ${cacheKey}`);
|
|
2174
2417
|
}).catch((err) => {
|
|
2175
|
-
|
|
2418
|
+
logger14.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2176
2419
|
});
|
|
2177
2420
|
}
|
|
2178
2421
|
return siteIds;
|
|
2179
2422
|
} catch (error) {
|
|
2180
|
-
|
|
2423
|
+
logger14.error("Failed to get hygiene site IDs", error);
|
|
2181
2424
|
throw error;
|
|
2182
2425
|
}
|
|
2183
2426
|
}
|
|
@@ -2191,7 +2434,7 @@ function useParentChecklistRepo() {
|
|
|
2191
2434
|
|
|
2192
2435
|
// src/controllers/hygiene-parent-checklist.controller.ts
|
|
2193
2436
|
import Joi7 from "joi";
|
|
2194
|
-
import { BadRequestError as BadRequestError13, logger as
|
|
2437
|
+
import { BadRequestError as BadRequestError13, logger as logger15 } from "@7365admin1/node-server-utils";
|
|
2195
2438
|
function useParentChecklistController() {
|
|
2196
2439
|
const {
|
|
2197
2440
|
createParentChecklist: _createParentChecklist,
|
|
@@ -2201,7 +2444,7 @@ function useParentChecklistController() {
|
|
|
2201
2444
|
const payload = req.body;
|
|
2202
2445
|
const { error } = parentChecklistSchema.validate(payload);
|
|
2203
2446
|
if (error) {
|
|
2204
|
-
|
|
2447
|
+
logger15.log({ level: "error", message: error.message });
|
|
2205
2448
|
next(new BadRequestError13(error.message));
|
|
2206
2449
|
return;
|
|
2207
2450
|
}
|
|
@@ -2210,7 +2453,7 @@ function useParentChecklistController() {
|
|
|
2210
2453
|
res.status(201).json({ message: "Parent checklist created successfully.", id });
|
|
2211
2454
|
return;
|
|
2212
2455
|
} catch (error2) {
|
|
2213
|
-
|
|
2456
|
+
logger15.log({ level: "error", message: error2.message });
|
|
2214
2457
|
next(error2);
|
|
2215
2458
|
return;
|
|
2216
2459
|
}
|
|
@@ -2228,7 +2471,7 @@ function useParentChecklistController() {
|
|
|
2228
2471
|
});
|
|
2229
2472
|
const { error } = validation.validate(query);
|
|
2230
2473
|
if (error) {
|
|
2231
|
-
|
|
2474
|
+
logger15.log({ level: "error", message: error.message });
|
|
2232
2475
|
next(new BadRequestError13(error.message));
|
|
2233
2476
|
return;
|
|
2234
2477
|
}
|
|
@@ -2252,7 +2495,7 @@ function useParentChecklistController() {
|
|
|
2252
2495
|
res.json(data);
|
|
2253
2496
|
return;
|
|
2254
2497
|
} catch (error2) {
|
|
2255
|
-
|
|
2498
|
+
logger15.log({ level: "error", message: error2.message });
|
|
2256
2499
|
next(error2);
|
|
2257
2500
|
return;
|
|
2258
2501
|
}
|
|
@@ -2266,7 +2509,7 @@ function useParentChecklistController() {
|
|
|
2266
2509
|
// src/models/hygiene-area-checklist.model.ts
|
|
2267
2510
|
import Joi8 from "joi";
|
|
2268
2511
|
import { ObjectId as ObjectId8 } from "mongodb";
|
|
2269
|
-
import { BadRequestError as BadRequestError14, logger as
|
|
2512
|
+
import { BadRequestError as BadRequestError14, logger as logger16 } from "@7365admin1/node-server-utils";
|
|
2270
2513
|
var allowedChecklistStatus = ["ready", "completed"];
|
|
2271
2514
|
var areaChecklistSchema = Joi8.object({
|
|
2272
2515
|
schedule: Joi8.string().hex().required(),
|
|
@@ -2289,7 +2532,7 @@ var areaChecklistSchema = Joi8.object({
|
|
|
2289
2532
|
function MAreaChecklist(value) {
|
|
2290
2533
|
const { error } = areaChecklistSchema.validate(value);
|
|
2291
2534
|
if (error) {
|
|
2292
|
-
|
|
2535
|
+
logger16.info(`Hygiene Checklist Area Model: ${error.message}`);
|
|
2293
2536
|
throw new BadRequestError14(error.message);
|
|
2294
2537
|
}
|
|
2295
2538
|
if (value.schedule) {
|
|
@@ -2313,6 +2556,8 @@ function MAreaChecklist(value) {
|
|
|
2313
2556
|
units: checklistItem.units.map((unit) => ({
|
|
2314
2557
|
unit: new ObjectId8(unit.unit),
|
|
2315
2558
|
name: unit.name,
|
|
2559
|
+
approve: false,
|
|
2560
|
+
reject: false,
|
|
2316
2561
|
status: "ready",
|
|
2317
2562
|
remarks: "",
|
|
2318
2563
|
completedBy: "",
|
|
@@ -2343,14 +2588,14 @@ function MAreaChecklist(value) {
|
|
|
2343
2588
|
}
|
|
2344
2589
|
|
|
2345
2590
|
// src/services/hygiene-area-checklist.service.ts
|
|
2346
|
-
import { logger as
|
|
2591
|
+
import { logger as logger18, useAtlas as useAtlas7 } from "@7365admin1/node-server-utils";
|
|
2347
2592
|
|
|
2348
2593
|
// src/repositories/hygiene-area-checklist.repository.ts
|
|
2349
2594
|
import { ObjectId as ObjectId9 } from "mongodb";
|
|
2350
2595
|
import {
|
|
2351
2596
|
BadRequestError as BadRequestError15,
|
|
2352
2597
|
InternalServerError as InternalServerError5,
|
|
2353
|
-
logger as
|
|
2598
|
+
logger as logger17,
|
|
2354
2599
|
makeCacheKey as makeCacheKey5,
|
|
2355
2600
|
paginate as paginate4,
|
|
2356
2601
|
useAtlas as useAtlas6,
|
|
@@ -2411,7 +2656,7 @@ function useAreaChecklistRepo() {
|
|
|
2411
2656
|
}
|
|
2412
2657
|
});
|
|
2413
2658
|
if (existingChecklist) {
|
|
2414
|
-
|
|
2659
|
+
logger17.info(
|
|
2415
2660
|
`Area checklist already exists for area ${value.name} on ${currentDate.toISOString().split("T")[0]}`
|
|
2416
2661
|
);
|
|
2417
2662
|
return existingChecklist._id;
|
|
@@ -2419,9 +2664,9 @@ function useAreaChecklistRepo() {
|
|
|
2419
2664
|
const processedValue = MAreaChecklist(value);
|
|
2420
2665
|
const result = await collection.insertOne(processedValue, { session });
|
|
2421
2666
|
delNamespace().then(() => {
|
|
2422
|
-
|
|
2667
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2423
2668
|
}).catch((err) => {
|
|
2424
|
-
|
|
2669
|
+
logger17.error(
|
|
2425
2670
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2426
2671
|
err
|
|
2427
2672
|
);
|
|
@@ -2467,7 +2712,7 @@ function useAreaChecklistRepo() {
|
|
|
2467
2712
|
if (!session) {
|
|
2468
2713
|
const cachedData = await getCache(cacheKey);
|
|
2469
2714
|
if (cachedData) {
|
|
2470
|
-
|
|
2715
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2471
2716
|
return cachedData;
|
|
2472
2717
|
}
|
|
2473
2718
|
}
|
|
@@ -2543,9 +2788,9 @@ function useAreaChecklistRepo() {
|
|
|
2543
2788
|
const length = await collection.countDocuments(query, { session });
|
|
2544
2789
|
const data = paginate4(items, page, limit, length);
|
|
2545
2790
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2546
|
-
|
|
2791
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2547
2792
|
}).catch((err) => {
|
|
2548
|
-
|
|
2793
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2549
2794
|
});
|
|
2550
2795
|
return data;
|
|
2551
2796
|
} catch (error) {
|
|
@@ -2597,7 +2842,7 @@ function useAreaChecklistRepo() {
|
|
|
2597
2842
|
const cacheKey = makeCacheKey5(namespace_collection, cacheOptions);
|
|
2598
2843
|
const cachedData = await getCache(cacheKey);
|
|
2599
2844
|
if (cachedData) {
|
|
2600
|
-
|
|
2845
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2601
2846
|
return cachedData;
|
|
2602
2847
|
}
|
|
2603
2848
|
try {
|
|
@@ -2647,9 +2892,9 @@ function useAreaChecklistRepo() {
|
|
|
2647
2892
|
const length = await collection.countDocuments(query);
|
|
2648
2893
|
const data = paginate4(items, page, limit, length);
|
|
2649
2894
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2650
|
-
|
|
2895
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2651
2896
|
}).catch((err) => {
|
|
2652
|
-
|
|
2897
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2653
2898
|
});
|
|
2654
2899
|
return data;
|
|
2655
2900
|
} catch (error) {
|
|
@@ -2667,7 +2912,7 @@ function useAreaChecklistRepo() {
|
|
|
2667
2912
|
});
|
|
2668
2913
|
const cachedData = await getCache(cacheKey);
|
|
2669
2914
|
if (cachedData) {
|
|
2670
|
-
|
|
2915
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2671
2916
|
return cachedData;
|
|
2672
2917
|
}
|
|
2673
2918
|
try {
|
|
@@ -2797,9 +3042,9 @@ function useAreaChecklistRepo() {
|
|
|
2797
3042
|
units
|
|
2798
3043
|
};
|
|
2799
3044
|
setCache(cacheKey, items, 15 * 60).then(() => {
|
|
2800
|
-
|
|
3045
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2801
3046
|
}).catch((err) => {
|
|
2802
|
-
|
|
3047
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2803
3048
|
});
|
|
2804
3049
|
return items;
|
|
2805
3050
|
} catch (error) {
|
|
@@ -2832,7 +3077,7 @@ function useAreaChecklistRepo() {
|
|
|
2832
3077
|
if (!session) {
|
|
2833
3078
|
const cachedData = await getCache(cacheKey);
|
|
2834
3079
|
if (cachedData) {
|
|
2835
|
-
|
|
3080
|
+
logger17.info(`Cache hit for key: ${cacheKey}`);
|
|
2836
3081
|
return cachedData;
|
|
2837
3082
|
}
|
|
2838
3083
|
}
|
|
@@ -2922,9 +3167,9 @@ function useAreaChecklistRepo() {
|
|
|
2922
3167
|
const length = countResult.length > 0 ? countResult[0].total : 0;
|
|
2923
3168
|
const data = paginate4(items, page, limit, length);
|
|
2924
3169
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2925
|
-
|
|
3170
|
+
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
2926
3171
|
}).catch((err) => {
|
|
2927
|
-
|
|
3172
|
+
logger17.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2928
3173
|
});
|
|
2929
3174
|
return data;
|
|
2930
3175
|
} catch (error) {
|
|
@@ -2967,7 +3212,7 @@ function useAreaChecklistRepo() {
|
|
|
2967
3212
|
throw error;
|
|
2968
3213
|
}
|
|
2969
3214
|
}
|
|
2970
|
-
async function
|
|
3215
|
+
async function updateAreaChecklistUnits(_id, set, unitId, value, session) {
|
|
2971
3216
|
try {
|
|
2972
3217
|
_id = new ObjectId9(_id);
|
|
2973
3218
|
} catch (error) {
|
|
@@ -2982,9 +3227,17 @@ function useAreaChecklistRepo() {
|
|
|
2982
3227
|
const now = /* @__PURE__ */ new Date();
|
|
2983
3228
|
const updateValue = {
|
|
2984
3229
|
"checklist.$[checklist].units.$[unit].timestamp": now,
|
|
2985
|
-
"checklist.$[checklist].units.$[unit].status": "completed",
|
|
2986
3230
|
updatedAt: now
|
|
2987
3231
|
};
|
|
3232
|
+
if (value.approve === true) {
|
|
3233
|
+
updateValue["checklist.$[checklist].units.$[unit].approve"] = true;
|
|
3234
|
+
updateValue["checklist.$[checklist].units.$[unit].reject"] = false;
|
|
3235
|
+
updateValue["checklist.$[checklist].units.$[unit].status"] = "completed";
|
|
3236
|
+
} else if (value.reject === true) {
|
|
3237
|
+
updateValue["checklist.$[checklist].units.$[unit].approve"] = false;
|
|
3238
|
+
updateValue["checklist.$[checklist].units.$[unit].reject"] = true;
|
|
3239
|
+
updateValue["checklist.$[checklist].units.$[unit].status"] = "ready";
|
|
3240
|
+
}
|
|
2988
3241
|
if (value.remarks) {
|
|
2989
3242
|
updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
|
|
2990
3243
|
}
|
|
@@ -3004,16 +3257,16 @@ function useAreaChecklistRepo() {
|
|
|
3004
3257
|
throw new InternalServerError5("Unable to update area checklist unit.");
|
|
3005
3258
|
}
|
|
3006
3259
|
delNamespace().then(() => {
|
|
3007
|
-
|
|
3260
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3008
3261
|
}).catch((err) => {
|
|
3009
|
-
|
|
3262
|
+
logger17.error(
|
|
3010
3263
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3011
3264
|
err
|
|
3012
3265
|
);
|
|
3013
3266
|
});
|
|
3014
3267
|
return res.modifiedCount;
|
|
3015
3268
|
} catch (error) {
|
|
3016
|
-
|
|
3269
|
+
logger17.error("Error updating area checklist unit:", error.message);
|
|
3017
3270
|
throw error;
|
|
3018
3271
|
}
|
|
3019
3272
|
}
|
|
@@ -3037,9 +3290,9 @@ function useAreaChecklistRepo() {
|
|
|
3037
3290
|
throw new InternalServerError5("Unable to update area checklist.");
|
|
3038
3291
|
}
|
|
3039
3292
|
delNamespace().then(() => {
|
|
3040
|
-
|
|
3293
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3041
3294
|
}).catch((err) => {
|
|
3042
|
-
|
|
3295
|
+
logger17.error(
|
|
3043
3296
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3044
3297
|
err
|
|
3045
3298
|
);
|
|
@@ -3074,9 +3327,9 @@ function useAreaChecklistRepo() {
|
|
|
3074
3327
|
);
|
|
3075
3328
|
}
|
|
3076
3329
|
delNamespace().then(() => {
|
|
3077
|
-
|
|
3330
|
+
logger17.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3078
3331
|
}).catch((err) => {
|
|
3079
|
-
|
|
3332
|
+
logger17.error(
|
|
3080
3333
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3081
3334
|
err
|
|
3082
3335
|
);
|
|
@@ -3099,7 +3352,7 @@ function useAreaChecklistRepo() {
|
|
|
3099
3352
|
).toArray();
|
|
3100
3353
|
return result.length > 0 && result[0].maxSet ? result[0].maxSet : 0;
|
|
3101
3354
|
} catch (error) {
|
|
3102
|
-
|
|
3355
|
+
logger17.error(`Error getting max set number for area ${areaId}:`, error);
|
|
3103
3356
|
return 0;
|
|
3104
3357
|
}
|
|
3105
3358
|
}
|
|
@@ -3115,7 +3368,7 @@ function useAreaChecklistRepo() {
|
|
|
3115
3368
|
getAreaChecklistByAreaAndSchedule,
|
|
3116
3369
|
updateAreaChecklist,
|
|
3117
3370
|
updateAreaChecklistStatus,
|
|
3118
|
-
|
|
3371
|
+
updateAreaChecklistUnits,
|
|
3119
3372
|
getMaxSetNumberForArea
|
|
3120
3373
|
};
|
|
3121
3374
|
}
|
|
@@ -3128,7 +3381,7 @@ function useAreaChecklistService() {
|
|
|
3128
3381
|
getAllAreaChecklist,
|
|
3129
3382
|
getAreaChecklistUnits,
|
|
3130
3383
|
getAreaChecklistById,
|
|
3131
|
-
|
|
3384
|
+
updateAreaChecklistUnits: _updateAreaChecklistUnits,
|
|
3132
3385
|
updateAreaChecklistStatus,
|
|
3133
3386
|
getMaxSetNumberForArea
|
|
3134
3387
|
} = useAreaChecklistRepo();
|
|
@@ -3147,7 +3400,7 @@ function useAreaChecklistService() {
|
|
|
3147
3400
|
const batch = areas.slice(i, i + BATCH_SIZE);
|
|
3148
3401
|
const batchPromises = batch.map(async (area) => {
|
|
3149
3402
|
if (!area.units || area.units.length === 0) {
|
|
3150
|
-
|
|
3403
|
+
logger18.warn(
|
|
3151
3404
|
`Skipping area ${area.name} (${area._id}): No units defined`
|
|
3152
3405
|
);
|
|
3153
3406
|
return null;
|
|
@@ -3178,19 +3431,19 @@ function useAreaChecklistService() {
|
|
|
3178
3431
|
const batchResults = await Promise.all(batchPromises);
|
|
3179
3432
|
results.push(...batchResults.filter((id) => id !== null));
|
|
3180
3433
|
}
|
|
3181
|
-
|
|
3434
|
+
logger18.info(
|
|
3182
3435
|
`Created ${totalChecklistsCreated} area checklists out of ${areas.length} areas for site: ${value.site}`
|
|
3183
3436
|
);
|
|
3184
3437
|
} else {
|
|
3185
|
-
|
|
3438
|
+
logger18.warn(`No common areas found for site: ${value.site}`);
|
|
3186
3439
|
}
|
|
3187
3440
|
await session?.commitTransaction();
|
|
3188
|
-
|
|
3441
|
+
logger18.info(
|
|
3189
3442
|
`Successfully created ${totalChecklistsCreated} area checklists for site: ${value.site}`
|
|
3190
3443
|
);
|
|
3191
3444
|
return results;
|
|
3192
3445
|
} catch (error) {
|
|
3193
|
-
|
|
3446
|
+
logger18.error(`Error generating area checklists:`, error);
|
|
3194
3447
|
if (session?.inTransaction()) {
|
|
3195
3448
|
await session?.abortTransaction();
|
|
3196
3449
|
}
|
|
@@ -3199,11 +3452,11 @@ function useAreaChecklistService() {
|
|
|
3199
3452
|
session?.endSession();
|
|
3200
3453
|
}
|
|
3201
3454
|
}
|
|
3202
|
-
async function
|
|
3455
|
+
async function updateAreaChecklistUnits(_id, set, unitId, value) {
|
|
3203
3456
|
const session = useAtlas7.getClient()?.startSession();
|
|
3204
3457
|
try {
|
|
3205
3458
|
session?.startTransaction();
|
|
3206
|
-
await
|
|
3459
|
+
await _updateAreaChecklistUnits(_id, set, unitId, value, session);
|
|
3207
3460
|
const allUnitsResult = await getAreaChecklistUnits(
|
|
3208
3461
|
{
|
|
3209
3462
|
page: 1,
|
|
@@ -3266,7 +3519,7 @@ function useAreaChecklistService() {
|
|
|
3266
3519
|
session
|
|
3267
3520
|
);
|
|
3268
3521
|
} else {
|
|
3269
|
-
|
|
3522
|
+
logger18.info(
|
|
3270
3523
|
"No area checklists found, keeping parent status as ready"
|
|
3271
3524
|
);
|
|
3272
3525
|
}
|
|
@@ -3274,7 +3527,7 @@ function useAreaChecklistService() {
|
|
|
3274
3527
|
await session?.commitTransaction();
|
|
3275
3528
|
return;
|
|
3276
3529
|
} catch (error) {
|
|
3277
|
-
|
|
3530
|
+
logger18.error(`Error updating area checklist unit and statuses:`, error);
|
|
3278
3531
|
if (session?.inTransaction()) {
|
|
3279
3532
|
await session?.abortTransaction();
|
|
3280
3533
|
}
|
|
@@ -3283,12 +3536,12 @@ function useAreaChecklistService() {
|
|
|
3283
3536
|
session?.endSession();
|
|
3284
3537
|
}
|
|
3285
3538
|
}
|
|
3286
|
-
return { createAreaChecklist,
|
|
3539
|
+
return { createAreaChecklist, updateAreaChecklistUnits };
|
|
3287
3540
|
}
|
|
3288
3541
|
|
|
3289
3542
|
// src/controllers/hygiene-area-checklist.controller.ts
|
|
3290
3543
|
import Joi9 from "joi";
|
|
3291
|
-
import { BadRequestError as BadRequestError16, logger as
|
|
3544
|
+
import { BadRequestError as BadRequestError16, logger as logger19 } from "@7365admin1/node-server-utils";
|
|
3292
3545
|
function useAreaChecklistController() {
|
|
3293
3546
|
const {
|
|
3294
3547
|
getAllAreaChecklist: _getAllAreaChecklist,
|
|
@@ -3298,7 +3551,7 @@ function useAreaChecklistController() {
|
|
|
3298
3551
|
} = useAreaChecklistRepo();
|
|
3299
3552
|
const {
|
|
3300
3553
|
createAreaChecklist: _createAreaChecklist,
|
|
3301
|
-
|
|
3554
|
+
updateAreaChecklistUnits: _updateAreaChecklistUnits
|
|
3302
3555
|
} = useAreaChecklistService();
|
|
3303
3556
|
async function createAreaChecklist(req, res, next) {
|
|
3304
3557
|
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
@@ -3318,7 +3571,7 @@ function useAreaChecklistController() {
|
|
|
3318
3571
|
});
|
|
3319
3572
|
const { error, value } = validation.validate(payload);
|
|
3320
3573
|
if (error) {
|
|
3321
|
-
|
|
3574
|
+
logger19.log({ level: "error", message: error.message });
|
|
3322
3575
|
next(new BadRequestError16(error.message));
|
|
3323
3576
|
return;
|
|
3324
3577
|
}
|
|
@@ -3327,7 +3580,7 @@ function useAreaChecklistController() {
|
|
|
3327
3580
|
res.status(201).json({ message: "Area checklist generated successfully." });
|
|
3328
3581
|
return;
|
|
3329
3582
|
} catch (error2) {
|
|
3330
|
-
|
|
3583
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3331
3584
|
next(error2);
|
|
3332
3585
|
return;
|
|
3333
3586
|
}
|
|
@@ -3344,7 +3597,7 @@ function useAreaChecklistController() {
|
|
|
3344
3597
|
});
|
|
3345
3598
|
const { error } = validation.validate(query);
|
|
3346
3599
|
if (error) {
|
|
3347
|
-
|
|
3600
|
+
logger19.log({ level: "error", message: error.message });
|
|
3348
3601
|
next(new BadRequestError16(error.message));
|
|
3349
3602
|
return;
|
|
3350
3603
|
}
|
|
@@ -3366,7 +3619,7 @@ function useAreaChecklistController() {
|
|
|
3366
3619
|
res.json(data);
|
|
3367
3620
|
return;
|
|
3368
3621
|
} catch (error2) {
|
|
3369
|
-
|
|
3622
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3370
3623
|
next(error2);
|
|
3371
3624
|
return;
|
|
3372
3625
|
}
|
|
@@ -3384,7 +3637,7 @@ function useAreaChecklistController() {
|
|
|
3384
3637
|
});
|
|
3385
3638
|
const { error } = validation.validate(query);
|
|
3386
3639
|
if (error) {
|
|
3387
|
-
|
|
3640
|
+
logger19.log({ level: "error", message: error.message });
|
|
3388
3641
|
next(new BadRequestError16(error.message));
|
|
3389
3642
|
return;
|
|
3390
3643
|
}
|
|
@@ -3408,7 +3661,7 @@ function useAreaChecklistController() {
|
|
|
3408
3661
|
res.json(data);
|
|
3409
3662
|
return;
|
|
3410
3663
|
} catch (error2) {
|
|
3411
|
-
|
|
3664
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3412
3665
|
next(error2);
|
|
3413
3666
|
return;
|
|
3414
3667
|
}
|
|
@@ -3418,7 +3671,7 @@ function useAreaChecklistController() {
|
|
|
3418
3671
|
const _id = req.params.id;
|
|
3419
3672
|
const { error, value } = validation.validate(_id);
|
|
3420
3673
|
if (error) {
|
|
3421
|
-
|
|
3674
|
+
logger19.log({ level: "error", message: error.message });
|
|
3422
3675
|
next(new BadRequestError16(error.message));
|
|
3423
3676
|
return;
|
|
3424
3677
|
}
|
|
@@ -3427,7 +3680,7 @@ function useAreaChecklistController() {
|
|
|
3427
3680
|
res.json(data);
|
|
3428
3681
|
return;
|
|
3429
3682
|
} catch (error2) {
|
|
3430
|
-
|
|
3683
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3431
3684
|
next(error2);
|
|
3432
3685
|
return;
|
|
3433
3686
|
}
|
|
@@ -3442,7 +3695,7 @@ function useAreaChecklistController() {
|
|
|
3442
3695
|
});
|
|
3443
3696
|
const { error } = validation.validate(query);
|
|
3444
3697
|
if (error) {
|
|
3445
|
-
|
|
3698
|
+
logger19.log({ level: "error", message: error.message });
|
|
3446
3699
|
next(new BadRequestError16(error.message));
|
|
3447
3700
|
return;
|
|
3448
3701
|
}
|
|
@@ -3460,47 +3713,55 @@ function useAreaChecklistController() {
|
|
|
3460
3713
|
res.json(data);
|
|
3461
3714
|
return;
|
|
3462
3715
|
} catch (error2) {
|
|
3463
|
-
|
|
3716
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3464
3717
|
next(error2);
|
|
3465
3718
|
return;
|
|
3466
3719
|
}
|
|
3467
3720
|
}
|
|
3468
|
-
async function
|
|
3721
|
+
async function updateAreaChecklistUnits(req, res, next) {
|
|
3469
3722
|
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3470
3723
|
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3471
3724
|
{}
|
|
3472
3725
|
) : {};
|
|
3473
3726
|
const completedBy = cookies["user"] || "";
|
|
3727
|
+
const decisionMap = {
|
|
3728
|
+
approve: { approve: true, reject: false },
|
|
3729
|
+
reject: { approve: false, reject: true }
|
|
3730
|
+
};
|
|
3731
|
+
const decision = req.params.decision;
|
|
3732
|
+
const decisionValues = decisionMap[decision] || {
|
|
3733
|
+
approve: void 0,
|
|
3734
|
+
reject: void 0
|
|
3735
|
+
};
|
|
3474
3736
|
const payload = {
|
|
3475
3737
|
...req.params,
|
|
3476
3738
|
...req.body,
|
|
3739
|
+
...decisionValues,
|
|
3477
3740
|
completedBy
|
|
3478
3741
|
};
|
|
3479
3742
|
const validation = Joi9.object({
|
|
3480
3743
|
id: Joi9.string().hex().required(),
|
|
3481
3744
|
set: Joi9.number().integer().min(1).required(),
|
|
3482
3745
|
unit: Joi9.string().hex().required(),
|
|
3746
|
+
decision: Joi9.string().valid("approve", "reject").required(),
|
|
3747
|
+
approve: Joi9.boolean().optional(),
|
|
3748
|
+
reject: Joi9.boolean().optional(),
|
|
3483
3749
|
remarks: Joi9.string().optional().allow("", null),
|
|
3484
3750
|
completedBy: Joi9.string().hex().required()
|
|
3485
3751
|
});
|
|
3486
3752
|
const { error } = validation.validate(payload);
|
|
3487
3753
|
if (error) {
|
|
3488
|
-
|
|
3754
|
+
logger19.log({ level: "error", message: error.message });
|
|
3489
3755
|
next(new BadRequestError16(error.message));
|
|
3490
3756
|
return;
|
|
3491
3757
|
}
|
|
3492
3758
|
try {
|
|
3493
|
-
const { id, set, unit, ...value } = payload;
|
|
3494
|
-
await
|
|
3495
|
-
id,
|
|
3496
|
-
parseInt(set),
|
|
3497
|
-
unit,
|
|
3498
|
-
value
|
|
3499
|
-
);
|
|
3759
|
+
const { id, set, unit, decision: decision2, ...value } = payload;
|
|
3760
|
+
await _updateAreaChecklistUnits(id, parseInt(set), unit, value);
|
|
3500
3761
|
res.json({ message: "Area checklist updated successfully." });
|
|
3501
3762
|
return;
|
|
3502
3763
|
} catch (error2) {
|
|
3503
|
-
|
|
3764
|
+
logger19.log({ level: "error", message: error2.message });
|
|
3504
3765
|
next(error2);
|
|
3505
3766
|
return;
|
|
3506
3767
|
}
|
|
@@ -3511,14 +3772,14 @@ function useAreaChecklistController() {
|
|
|
3511
3772
|
getAreaChecklistHistory,
|
|
3512
3773
|
getAreaChecklistHistoryDetails,
|
|
3513
3774
|
getAreaChecklistUnits,
|
|
3514
|
-
|
|
3775
|
+
updateAreaChecklistUnits
|
|
3515
3776
|
};
|
|
3516
3777
|
}
|
|
3517
3778
|
|
|
3518
3779
|
// src/models/hygiene-supply.model.ts
|
|
3519
3780
|
import Joi10 from "joi";
|
|
3520
3781
|
import { ObjectId as ObjectId10 } from "mongodb";
|
|
3521
|
-
import { BadRequestError as BadRequestError17, logger as
|
|
3782
|
+
import { BadRequestError as BadRequestError17, logger as logger20 } from "@7365admin1/node-server-utils";
|
|
3522
3783
|
var supplySchema = Joi10.object({
|
|
3523
3784
|
site: Joi10.string().hex().required(),
|
|
3524
3785
|
name: Joi10.string().required(),
|
|
@@ -3527,7 +3788,7 @@ var supplySchema = Joi10.object({
|
|
|
3527
3788
|
function MSupply(value) {
|
|
3528
3789
|
const { error } = supplySchema.validate(value);
|
|
3529
3790
|
if (error) {
|
|
3530
|
-
|
|
3791
|
+
logger20.info(`Hygiene Supply Model: ${error.message}`);
|
|
3531
3792
|
throw new BadRequestError17(error.message);
|
|
3532
3793
|
}
|
|
3533
3794
|
if (value.site) {
|
|
@@ -3557,7 +3818,7 @@ import {
|
|
|
3557
3818
|
paginate as paginate5,
|
|
3558
3819
|
BadRequestError as BadRequestError18,
|
|
3559
3820
|
useCache as useCache6,
|
|
3560
|
-
logger as
|
|
3821
|
+
logger as logger21,
|
|
3561
3822
|
makeCacheKey as makeCacheKey6,
|
|
3562
3823
|
NotFoundError as NotFoundError4
|
|
3563
3824
|
} from "@7365admin1/node-server-utils";
|
|
@@ -3604,9 +3865,9 @@ function useSupplyRepository() {
|
|
|
3604
3865
|
value = MSupply(value);
|
|
3605
3866
|
const res = await collection.insertOne(value, { session });
|
|
3606
3867
|
delNamespace().then(() => {
|
|
3607
|
-
|
|
3868
|
+
logger21.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3608
3869
|
}).catch((err) => {
|
|
3609
|
-
|
|
3870
|
+
logger21.error(
|
|
3610
3871
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3611
3872
|
err
|
|
3612
3873
|
);
|
|
@@ -3648,7 +3909,7 @@ function useSupplyRepository() {
|
|
|
3648
3909
|
const cacheKey = makeCacheKey6(namespace_collection, cacheOptions);
|
|
3649
3910
|
const cachedData = await getCache(cacheKey);
|
|
3650
3911
|
if (cachedData) {
|
|
3651
|
-
|
|
3912
|
+
logger21.info(`Cache hit for key: ${cacheKey}`);
|
|
3652
3913
|
return cachedData;
|
|
3653
3914
|
}
|
|
3654
3915
|
try {
|
|
@@ -3668,9 +3929,9 @@ function useSupplyRepository() {
|
|
|
3668
3929
|
const length = await collection.countDocuments(query);
|
|
3669
3930
|
const data = paginate5(items, page, limit, length);
|
|
3670
3931
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3671
|
-
|
|
3932
|
+
logger21.info(`Cache set for key: ${cacheKey}`);
|
|
3672
3933
|
}).catch((err) => {
|
|
3673
|
-
|
|
3934
|
+
logger21.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3674
3935
|
});
|
|
3675
3936
|
return data;
|
|
3676
3937
|
} catch (error) {
|
|
@@ -3693,11 +3954,11 @@ function useSupplyRepository() {
|
|
|
3693
3954
|
if (!session) {
|
|
3694
3955
|
const cachedData = await getCache(cacheKey);
|
|
3695
3956
|
if (cachedData) {
|
|
3696
|
-
|
|
3957
|
+
logger21.info(`Cache hit for key: ${cacheKey}`);
|
|
3697
3958
|
return cachedData;
|
|
3698
3959
|
}
|
|
3699
3960
|
} else {
|
|
3700
|
-
|
|
3961
|
+
logger21.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
3701
3962
|
}
|
|
3702
3963
|
try {
|
|
3703
3964
|
const data = await collection.aggregate([
|
|
@@ -3714,9 +3975,9 @@ function useSupplyRepository() {
|
|
|
3714
3975
|
throw new NotFoundError4("Supply not found.");
|
|
3715
3976
|
}
|
|
3716
3977
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
3717
|
-
|
|
3978
|
+
logger21.info(`Cache set for key: ${cacheKey}`);
|
|
3718
3979
|
}).catch((err) => {
|
|
3719
|
-
|
|
3980
|
+
logger21.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3720
3981
|
});
|
|
3721
3982
|
return data[0];
|
|
3722
3983
|
} catch (error) {
|
|
@@ -3740,9 +4001,9 @@ function useSupplyRepository() {
|
|
|
3740
4001
|
throw new InternalServerError6("Unable to update cleaning supply.");
|
|
3741
4002
|
}
|
|
3742
4003
|
delNamespace().then(() => {
|
|
3743
|
-
|
|
4004
|
+
logger21.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3744
4005
|
}).catch((err) => {
|
|
3745
|
-
|
|
4006
|
+
logger21.error(
|
|
3746
4007
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3747
4008
|
err
|
|
3748
4009
|
);
|
|
@@ -3777,9 +4038,9 @@ function useSupplyRepository() {
|
|
|
3777
4038
|
throw new InternalServerError6("Unable to delete supply.");
|
|
3778
4039
|
}
|
|
3779
4040
|
delNamespace().then(() => {
|
|
3780
|
-
|
|
4041
|
+
logger21.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3781
4042
|
}).catch((err) => {
|
|
3782
|
-
|
|
4043
|
+
logger21.error(
|
|
3783
4044
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3784
4045
|
err
|
|
3785
4046
|
);
|
|
@@ -3803,7 +4064,7 @@ function useSupplyRepository() {
|
|
|
3803
4064
|
|
|
3804
4065
|
// src/controllers/hygiene-supply.controller.ts
|
|
3805
4066
|
import Joi11 from "joi";
|
|
3806
|
-
import { BadRequestError as BadRequestError19, logger as
|
|
4067
|
+
import { BadRequestError as BadRequestError19, logger as logger22 } from "@7365admin1/node-server-utils";
|
|
3807
4068
|
function useSupplyController() {
|
|
3808
4069
|
const {
|
|
3809
4070
|
createSupply: _createSupply,
|
|
@@ -3816,7 +4077,7 @@ function useSupplyController() {
|
|
|
3816
4077
|
const payload = { ...req.body, ...req.params };
|
|
3817
4078
|
const { error } = supplySchema.validate(payload);
|
|
3818
4079
|
if (error) {
|
|
3819
|
-
|
|
4080
|
+
logger22.log({ level: "error", message: error.message });
|
|
3820
4081
|
next(new BadRequestError19(error.message));
|
|
3821
4082
|
return;
|
|
3822
4083
|
}
|
|
@@ -3825,7 +4086,7 @@ function useSupplyController() {
|
|
|
3825
4086
|
res.status(201).json({ message: "Supply created successfully.", id });
|
|
3826
4087
|
return;
|
|
3827
4088
|
} catch (error2) {
|
|
3828
|
-
|
|
4089
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3829
4090
|
next(error2);
|
|
3830
4091
|
return;
|
|
3831
4092
|
}
|
|
@@ -3840,7 +4101,7 @@ function useSupplyController() {
|
|
|
3840
4101
|
});
|
|
3841
4102
|
const { error } = validation.validate(query);
|
|
3842
4103
|
if (error) {
|
|
3843
|
-
|
|
4104
|
+
logger22.log({ level: "error", message: error.message });
|
|
3844
4105
|
next(new BadRequestError19(error.message));
|
|
3845
4106
|
return;
|
|
3846
4107
|
}
|
|
@@ -3858,7 +4119,7 @@ function useSupplyController() {
|
|
|
3858
4119
|
res.json(data);
|
|
3859
4120
|
return;
|
|
3860
4121
|
} catch (error2) {
|
|
3861
|
-
|
|
4122
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3862
4123
|
next(error2);
|
|
3863
4124
|
return;
|
|
3864
4125
|
}
|
|
@@ -3868,7 +4129,7 @@ function useSupplyController() {
|
|
|
3868
4129
|
const _id = req.params.id;
|
|
3869
4130
|
const { error, value } = validation.validate(_id);
|
|
3870
4131
|
if (error) {
|
|
3871
|
-
|
|
4132
|
+
logger22.log({ level: "error", message: error.message });
|
|
3872
4133
|
next(new BadRequestError19(error.message));
|
|
3873
4134
|
return;
|
|
3874
4135
|
}
|
|
@@ -3877,7 +4138,7 @@ function useSupplyController() {
|
|
|
3877
4138
|
res.json(data);
|
|
3878
4139
|
return;
|
|
3879
4140
|
} catch (error2) {
|
|
3880
|
-
|
|
4141
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3881
4142
|
next(error2);
|
|
3882
4143
|
return;
|
|
3883
4144
|
}
|
|
@@ -3892,7 +4153,7 @@ function useSupplyController() {
|
|
|
3892
4153
|
});
|
|
3893
4154
|
const { error } = validation.validate(payload);
|
|
3894
4155
|
if (error) {
|
|
3895
|
-
|
|
4156
|
+
logger22.log({ level: "error", message: error.message });
|
|
3896
4157
|
next(new BadRequestError19(error.message));
|
|
3897
4158
|
return;
|
|
3898
4159
|
}
|
|
@@ -3902,7 +4163,7 @@ function useSupplyController() {
|
|
|
3902
4163
|
res.json({ message: "Supply updated successfully." });
|
|
3903
4164
|
return;
|
|
3904
4165
|
} catch (error2) {
|
|
3905
|
-
|
|
4166
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3906
4167
|
next(error2);
|
|
3907
4168
|
return;
|
|
3908
4169
|
}
|
|
@@ -3914,7 +4175,7 @@ function useSupplyController() {
|
|
|
3914
4175
|
});
|
|
3915
4176
|
const { error, value } = validation.validate({ id });
|
|
3916
4177
|
if (error) {
|
|
3917
|
-
|
|
4178
|
+
logger22.log({ level: "error", message: error.message });
|
|
3918
4179
|
next(new BadRequestError19(error.message));
|
|
3919
4180
|
return;
|
|
3920
4181
|
}
|
|
@@ -3923,7 +4184,7 @@ function useSupplyController() {
|
|
|
3923
4184
|
res.json({ message: "Supply deleted successfully." });
|
|
3924
4185
|
return;
|
|
3925
4186
|
} catch (error2) {
|
|
3926
|
-
|
|
4187
|
+
logger22.log({ level: "error", message: error2.message });
|
|
3927
4188
|
next(error2);
|
|
3928
4189
|
return;
|
|
3929
4190
|
}
|
|
@@ -3940,7 +4201,7 @@ function useSupplyController() {
|
|
|
3940
4201
|
// src/models/hygiene-stock.model.ts
|
|
3941
4202
|
import Joi12 from "joi";
|
|
3942
4203
|
import { ObjectId as ObjectId12 } from "mongodb";
|
|
3943
|
-
import { BadRequestError as BadRequestError20, logger as
|
|
4204
|
+
import { BadRequestError as BadRequestError20, logger as logger23 } from "@7365admin1/node-server-utils";
|
|
3944
4205
|
var stockSchema = Joi12.object({
|
|
3945
4206
|
site: Joi12.string().hex().required(),
|
|
3946
4207
|
supply: Joi12.string().hex().required(),
|
|
@@ -3952,7 +4213,7 @@ var stockSchema = Joi12.object({
|
|
|
3952
4213
|
function MStock(value) {
|
|
3953
4214
|
const { error } = stockSchema.validate(value);
|
|
3954
4215
|
if (error) {
|
|
3955
|
-
|
|
4216
|
+
logger23.info(`Hygiene Stock Model: ${error.message}`);
|
|
3956
4217
|
throw new BadRequestError20(error.message);
|
|
3957
4218
|
}
|
|
3958
4219
|
if (value.site) {
|
|
@@ -3990,7 +4251,7 @@ import {
|
|
|
3990
4251
|
InternalServerError as InternalServerError7,
|
|
3991
4252
|
BadRequestError as BadRequestError21,
|
|
3992
4253
|
useCache as useCache7,
|
|
3993
|
-
logger as
|
|
4254
|
+
logger as logger24,
|
|
3994
4255
|
makeCacheKey as makeCacheKey7,
|
|
3995
4256
|
paginate as paginate6
|
|
3996
4257
|
} from "@7365admin1/node-server-utils";
|
|
@@ -4021,17 +4282,17 @@ function useStockRepository() {
|
|
|
4021
4282
|
value = MStock(value);
|
|
4022
4283
|
const res = await collection.insertOne(value, { session });
|
|
4023
4284
|
delNamespace().then(() => {
|
|
4024
|
-
|
|
4285
|
+
logger24.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4025
4286
|
}).catch((err) => {
|
|
4026
|
-
|
|
4287
|
+
logger24.error(
|
|
4027
4288
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4028
4289
|
err
|
|
4029
4290
|
);
|
|
4030
4291
|
});
|
|
4031
4292
|
delSupplyNamespace().then(() => {
|
|
4032
|
-
|
|
4293
|
+
logger24.info(`Cache cleared for namespace: ${supply_collection}`);
|
|
4033
4294
|
}).catch((err) => {
|
|
4034
|
-
|
|
4295
|
+
logger24.error(
|
|
4035
4296
|
`Failed to clear cache for namespace: ${supply_collection}`,
|
|
4036
4297
|
err
|
|
4037
4298
|
);
|
|
@@ -4077,7 +4338,7 @@ function useStockRepository() {
|
|
|
4077
4338
|
const cacheKey = makeCacheKey7(namespace_collection, cacheOptions);
|
|
4078
4339
|
const cachedData = await getCache(cacheKey);
|
|
4079
4340
|
if (cachedData) {
|
|
4080
|
-
|
|
4341
|
+
logger24.info(`Cache hit for key: ${cacheKey}`);
|
|
4081
4342
|
return cachedData;
|
|
4082
4343
|
}
|
|
4083
4344
|
try {
|
|
@@ -4098,9 +4359,9 @@ function useStockRepository() {
|
|
|
4098
4359
|
const length = await collection.countDocuments(query);
|
|
4099
4360
|
const data = paginate6(items, page, limit, length);
|
|
4100
4361
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4101
|
-
|
|
4362
|
+
logger24.info(`Cache set for key: ${cacheKey}`);
|
|
4102
4363
|
}).catch((err) => {
|
|
4103
|
-
|
|
4364
|
+
logger24.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4104
4365
|
});
|
|
4105
4366
|
return data;
|
|
4106
4367
|
} catch (error) {
|
|
@@ -4170,7 +4431,7 @@ function useStockService() {
|
|
|
4170
4431
|
|
|
4171
4432
|
// src/controllers/hygiene-stock.controller.ts
|
|
4172
4433
|
import Joi13 from "joi";
|
|
4173
|
-
import { BadRequestError as BadRequestError23, logger as
|
|
4434
|
+
import { BadRequestError as BadRequestError23, logger as logger25 } from "@7365admin1/node-server-utils";
|
|
4174
4435
|
function useStockController() {
|
|
4175
4436
|
const { getStocksBySupplyId: _getStocksBySupplyId } = useStockRepository();
|
|
4176
4437
|
const { createStock: _createStock } = useStockService();
|
|
@@ -4184,7 +4445,7 @@ function useStockController() {
|
|
|
4184
4445
|
});
|
|
4185
4446
|
const { error } = validation.validate(payload);
|
|
4186
4447
|
if (error) {
|
|
4187
|
-
|
|
4448
|
+
logger25.log({ level: "error", message: error.message });
|
|
4188
4449
|
next(new BadRequestError23(error.message));
|
|
4189
4450
|
return;
|
|
4190
4451
|
}
|
|
@@ -4193,7 +4454,7 @@ function useStockController() {
|
|
|
4193
4454
|
res.status(201).json({ message: "Stock created successfully.", id });
|
|
4194
4455
|
return;
|
|
4195
4456
|
} catch (error2) {
|
|
4196
|
-
|
|
4457
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4197
4458
|
next(error2);
|
|
4198
4459
|
return;
|
|
4199
4460
|
}
|
|
@@ -4209,7 +4470,7 @@ function useStockController() {
|
|
|
4209
4470
|
});
|
|
4210
4471
|
const { error } = validation.validate(query);
|
|
4211
4472
|
if (error) {
|
|
4212
|
-
|
|
4473
|
+
logger25.log({ level: "error", message: error.message });
|
|
4213
4474
|
next(new BadRequestError23(error.message));
|
|
4214
4475
|
return;
|
|
4215
4476
|
}
|
|
@@ -4229,7 +4490,7 @@ function useStockController() {
|
|
|
4229
4490
|
res.json(data);
|
|
4230
4491
|
return;
|
|
4231
4492
|
} catch (error2) {
|
|
4232
|
-
|
|
4493
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4233
4494
|
next(error2);
|
|
4234
4495
|
return;
|
|
4235
4496
|
}
|
|
@@ -4243,7 +4504,7 @@ function useStockController() {
|
|
|
4243
4504
|
// src/models/hygiene-checkout-item.model.ts
|
|
4244
4505
|
import Joi14 from "joi";
|
|
4245
4506
|
import { ObjectId as ObjectId14 } from "mongodb";
|
|
4246
|
-
import { BadRequestError as BadRequestError24, logger as
|
|
4507
|
+
import { BadRequestError as BadRequestError24, logger as logger26 } from "@7365admin1/node-server-utils";
|
|
4247
4508
|
var allowedCheckOutItemStatus = ["pending", "completed"];
|
|
4248
4509
|
var checkOutItemSchema = Joi14.object({
|
|
4249
4510
|
site: Joi14.string().hex().required(),
|
|
@@ -4257,7 +4518,7 @@ var checkOutItemSchema = Joi14.object({
|
|
|
4257
4518
|
function MCheckOutItem(value) {
|
|
4258
4519
|
const { error } = checkOutItemSchema.validate(value);
|
|
4259
4520
|
if (error) {
|
|
4260
|
-
|
|
4521
|
+
logger26.info(`Hygiene Check Out Item Model: ${error.message}`);
|
|
4261
4522
|
throw new BadRequestError24(error.message);
|
|
4262
4523
|
}
|
|
4263
4524
|
if (value.site) {
|
|
@@ -4295,7 +4556,7 @@ import {
|
|
|
4295
4556
|
useAtlas as useAtlas11,
|
|
4296
4557
|
InternalServerError as InternalServerError8,
|
|
4297
4558
|
useCache as useCache8,
|
|
4298
|
-
logger as
|
|
4559
|
+
logger as logger27,
|
|
4299
4560
|
makeCacheKey as makeCacheKey8,
|
|
4300
4561
|
paginate as paginate7,
|
|
4301
4562
|
BadRequestError as BadRequestError25,
|
|
@@ -4336,9 +4597,9 @@ function useCheckOutItemRepository() {
|
|
|
4336
4597
|
value = MCheckOutItem(value);
|
|
4337
4598
|
const res = await collection.insertOne(value, { session });
|
|
4338
4599
|
delNamespace().then(() => {
|
|
4339
|
-
|
|
4600
|
+
logger27.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4340
4601
|
}).catch((err) => {
|
|
4341
|
-
|
|
4602
|
+
logger27.error(
|
|
4342
4603
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4343
4604
|
err
|
|
4344
4605
|
);
|
|
@@ -4376,7 +4637,7 @@ function useCheckOutItemRepository() {
|
|
|
4376
4637
|
const cacheKey = makeCacheKey8(namespace_collection, cacheOptions);
|
|
4377
4638
|
const cachedData = await getCache(cacheKey);
|
|
4378
4639
|
if (cachedData) {
|
|
4379
|
-
|
|
4640
|
+
logger27.info(`Cache hit for key: ${cacheKey}`);
|
|
4380
4641
|
return cachedData;
|
|
4381
4642
|
}
|
|
4382
4643
|
try {
|
|
@@ -4425,9 +4686,9 @@ function useCheckOutItemRepository() {
|
|
|
4425
4686
|
const length = await collection.countDocuments(query);
|
|
4426
4687
|
const data = paginate7(items, page, limit, length);
|
|
4427
4688
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4428
|
-
|
|
4689
|
+
logger27.info(`Cache set for key: ${cacheKey}`);
|
|
4429
4690
|
}).catch((err) => {
|
|
4430
|
-
|
|
4691
|
+
logger27.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4431
4692
|
});
|
|
4432
4693
|
return data;
|
|
4433
4694
|
} catch (error) {
|
|
@@ -4447,11 +4708,11 @@ function useCheckOutItemRepository() {
|
|
|
4447
4708
|
if (!session) {
|
|
4448
4709
|
const cachedData = await getCache(cacheKey);
|
|
4449
4710
|
if (cachedData) {
|
|
4450
|
-
|
|
4711
|
+
logger27.info(`Cache hit for key: ${cacheKey}`);
|
|
4451
4712
|
return cachedData;
|
|
4452
4713
|
}
|
|
4453
4714
|
} else {
|
|
4454
|
-
|
|
4715
|
+
logger27.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
4455
4716
|
}
|
|
4456
4717
|
try {
|
|
4457
4718
|
const data = await collection.aggregate(
|
|
@@ -4489,9 +4750,9 @@ function useCheckOutItemRepository() {
|
|
|
4489
4750
|
throw new NotFoundError6("Check out item not found.");
|
|
4490
4751
|
}
|
|
4491
4752
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
4492
|
-
|
|
4753
|
+
logger27.info(`Cache set for key: ${cacheKey}`);
|
|
4493
4754
|
}).catch((err) => {
|
|
4494
|
-
|
|
4755
|
+
logger27.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4495
4756
|
});
|
|
4496
4757
|
return data[0];
|
|
4497
4758
|
} catch (error) {
|
|
@@ -4518,9 +4779,9 @@ function useCheckOutItemRepository() {
|
|
|
4518
4779
|
throw new InternalServerError8("Unable to complete check out item.");
|
|
4519
4780
|
}
|
|
4520
4781
|
delNamespace().then(() => {
|
|
4521
|
-
|
|
4782
|
+
logger27.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4522
4783
|
}).catch((err) => {
|
|
4523
|
-
|
|
4784
|
+
logger27.error(
|
|
4524
4785
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4525
4786
|
err
|
|
4526
4787
|
);
|
|
@@ -4641,7 +4902,7 @@ function useCheckOutItemService() {
|
|
|
4641
4902
|
|
|
4642
4903
|
// src/controllers/hygiene-checkout-item.controller.ts
|
|
4643
4904
|
import Joi15 from "joi";
|
|
4644
|
-
import { BadRequestError as BadRequestError27, logger as
|
|
4905
|
+
import { BadRequestError as BadRequestError27, logger as logger28 } from "@7365admin1/node-server-utils";
|
|
4645
4906
|
function useCheckOutItemController() {
|
|
4646
4907
|
const {
|
|
4647
4908
|
getCheckOutItems: _getCheckOutItems,
|
|
@@ -4671,7 +4932,7 @@ function useCheckOutItemController() {
|
|
|
4671
4932
|
});
|
|
4672
4933
|
const { error } = validation.validate(payload);
|
|
4673
4934
|
if (error) {
|
|
4674
|
-
|
|
4935
|
+
logger28.log({ level: "error", message: error.message });
|
|
4675
4936
|
next(new BadRequestError27(error.message));
|
|
4676
4937
|
return;
|
|
4677
4938
|
}
|
|
@@ -4680,7 +4941,7 @@ function useCheckOutItemController() {
|
|
|
4680
4941
|
res.status(201).json({ message: "Check out item created successfully.", id });
|
|
4681
4942
|
return;
|
|
4682
4943
|
} catch (error2) {
|
|
4683
|
-
|
|
4944
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4684
4945
|
next(error2);
|
|
4685
4946
|
return;
|
|
4686
4947
|
}
|
|
@@ -4709,7 +4970,7 @@ function useCheckOutItemController() {
|
|
|
4709
4970
|
});
|
|
4710
4971
|
const { error } = validation.validate(payload);
|
|
4711
4972
|
if (error) {
|
|
4712
|
-
|
|
4973
|
+
logger28.log({ level: "error", message: error.message });
|
|
4713
4974
|
next(new BadRequestError27(error.message));
|
|
4714
4975
|
return;
|
|
4715
4976
|
}
|
|
@@ -4718,7 +4979,7 @@ function useCheckOutItemController() {
|
|
|
4718
4979
|
res.status(201).json({ message: "Check out items created successfully." });
|
|
4719
4980
|
return;
|
|
4720
4981
|
} catch (error2) {
|
|
4721
|
-
|
|
4982
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4722
4983
|
next(error2);
|
|
4723
4984
|
return;
|
|
4724
4985
|
}
|
|
@@ -4733,7 +4994,7 @@ function useCheckOutItemController() {
|
|
|
4733
4994
|
});
|
|
4734
4995
|
const { error } = validation.validate(query);
|
|
4735
4996
|
if (error) {
|
|
4736
|
-
|
|
4997
|
+
logger28.log({ level: "error", message: error.message });
|
|
4737
4998
|
next(new BadRequestError27(error.message));
|
|
4738
4999
|
return;
|
|
4739
5000
|
}
|
|
@@ -4751,7 +5012,7 @@ function useCheckOutItemController() {
|
|
|
4751
5012
|
res.json(data);
|
|
4752
5013
|
return;
|
|
4753
5014
|
} catch (error2) {
|
|
4754
|
-
|
|
5015
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4755
5016
|
next(error2);
|
|
4756
5017
|
return;
|
|
4757
5018
|
}
|
|
@@ -4761,7 +5022,7 @@ function useCheckOutItemController() {
|
|
|
4761
5022
|
const _id = req.params.id;
|
|
4762
5023
|
const { error, value } = validation.validate(_id);
|
|
4763
5024
|
if (error) {
|
|
4764
|
-
|
|
5025
|
+
logger28.log({ level: "error", message: error.message });
|
|
4765
5026
|
next(new BadRequestError27(error.message));
|
|
4766
5027
|
return;
|
|
4767
5028
|
}
|
|
@@ -4770,7 +5031,7 @@ function useCheckOutItemController() {
|
|
|
4770
5031
|
res.json(data);
|
|
4771
5032
|
return;
|
|
4772
5033
|
} catch (error2) {
|
|
4773
|
-
|
|
5034
|
+
logger28.log({ level: "error", message: error2.message });
|
|
4774
5035
|
next(error2);
|
|
4775
5036
|
return;
|
|
4776
5037
|
}
|
|
@@ -4784,7 +5045,7 @@ function useCheckOutItemController() {
|
|
|
4784
5045
|
}
|
|
4785
5046
|
|
|
4786
5047
|
// src/models/hygiene-schedule-task.model.ts
|
|
4787
|
-
import { BadRequestError as BadRequestError28, logger as
|
|
5048
|
+
import { BadRequestError as BadRequestError28, logger as logger29 } from "@7365admin1/node-server-utils";
|
|
4788
5049
|
import Joi16 from "joi";
|
|
4789
5050
|
import { ObjectId as ObjectId16 } from "mongodb";
|
|
4790
5051
|
var scheduleTaskSchema = Joi16.object({
|
|
@@ -4805,7 +5066,7 @@ var scheduleTaskSchema = Joi16.object({
|
|
|
4805
5066
|
function MScheduleTask(value) {
|
|
4806
5067
|
const { error } = scheduleTaskSchema.validate(value);
|
|
4807
5068
|
if (error) {
|
|
4808
|
-
|
|
5069
|
+
logger29.info(`Hygiene Schedule Task Model: ${error.message}`);
|
|
4809
5070
|
throw new BadRequestError28(error.message);
|
|
4810
5071
|
}
|
|
4811
5072
|
if (value.site) {
|
|
@@ -4858,7 +5119,7 @@ import {
|
|
|
4858
5119
|
paginate as paginate8,
|
|
4859
5120
|
BadRequestError as BadRequestError29,
|
|
4860
5121
|
useCache as useCache9,
|
|
4861
|
-
logger as
|
|
5122
|
+
logger as logger30,
|
|
4862
5123
|
makeCacheKey as makeCacheKey9,
|
|
4863
5124
|
NotFoundError as NotFoundError7
|
|
4864
5125
|
} from "@7365admin1/node-server-utils";
|
|
@@ -4896,9 +5157,9 @@ function useScheduleTaskRepository() {
|
|
|
4896
5157
|
value = MScheduleTask(value);
|
|
4897
5158
|
const res = await collection.insertOne(value, { session });
|
|
4898
5159
|
delNamespace().then(() => {
|
|
4899
|
-
|
|
5160
|
+
logger30.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4900
5161
|
}).catch((err) => {
|
|
4901
|
-
|
|
5162
|
+
logger30.error(
|
|
4902
5163
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4903
5164
|
err
|
|
4904
5165
|
);
|
|
@@ -4936,7 +5197,7 @@ function useScheduleTaskRepository() {
|
|
|
4936
5197
|
const cacheKey = makeCacheKey9(namespace_collection, cacheOptions);
|
|
4937
5198
|
const cachedData = await getCache(cacheKey);
|
|
4938
5199
|
if (cachedData) {
|
|
4939
|
-
|
|
5200
|
+
logger30.info(`Cache hit for key: ${cacheKey}`);
|
|
4940
5201
|
return cachedData;
|
|
4941
5202
|
}
|
|
4942
5203
|
try {
|
|
@@ -4956,9 +5217,9 @@ function useScheduleTaskRepository() {
|
|
|
4956
5217
|
const length = await collection.countDocuments(query);
|
|
4957
5218
|
const data = paginate8(items, page, limit, length);
|
|
4958
5219
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4959
|
-
|
|
5220
|
+
logger30.info(`Cache set for key: ${cacheKey}`);
|
|
4960
5221
|
}).catch((err) => {
|
|
4961
|
-
|
|
5222
|
+
logger30.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4962
5223
|
});
|
|
4963
5224
|
return data;
|
|
4964
5225
|
} catch (error) {
|
|
@@ -5004,7 +5265,7 @@ function useScheduleTaskRepository() {
|
|
|
5004
5265
|
const cacheKey = makeCacheKey9(namespace_collection, cacheOptions);
|
|
5005
5266
|
const cachedData = await getCache(cacheKey);
|
|
5006
5267
|
if (cachedData) {
|
|
5007
|
-
|
|
5268
|
+
logger30.info(`Cache hit for key: ${cacheKey}`);
|
|
5008
5269
|
return cachedData;
|
|
5009
5270
|
}
|
|
5010
5271
|
try {
|
|
@@ -5023,9 +5284,9 @@ function useScheduleTaskRepository() {
|
|
|
5023
5284
|
const length = await collection.countDocuments(query);
|
|
5024
5285
|
const data = paginate8(items, page, limit, length);
|
|
5025
5286
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
5026
|
-
|
|
5287
|
+
logger30.info(`Cache set for key: ${cacheKey}`);
|
|
5027
5288
|
}).catch((err) => {
|
|
5028
|
-
|
|
5289
|
+
logger30.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
5029
5290
|
});
|
|
5030
5291
|
return data;
|
|
5031
5292
|
} catch (error) {
|
|
@@ -5048,11 +5309,11 @@ function useScheduleTaskRepository() {
|
|
|
5048
5309
|
if (!session) {
|
|
5049
5310
|
const cachedData = await getCache(cacheKey);
|
|
5050
5311
|
if (cachedData) {
|
|
5051
|
-
|
|
5312
|
+
logger30.info(`Cache hit for key: ${cacheKey}`);
|
|
5052
5313
|
return cachedData;
|
|
5053
5314
|
}
|
|
5054
5315
|
} else {
|
|
5055
|
-
|
|
5316
|
+
logger30.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
5056
5317
|
}
|
|
5057
5318
|
try {
|
|
5058
5319
|
const data = await collection.aggregate([
|
|
@@ -5074,9 +5335,9 @@ function useScheduleTaskRepository() {
|
|
|
5074
5335
|
throw new NotFoundError7("Schedule task not found.");
|
|
5075
5336
|
}
|
|
5076
5337
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
5077
|
-
|
|
5338
|
+
logger30.info(`Cache set for key: ${cacheKey}`);
|
|
5078
5339
|
}).catch((err) => {
|
|
5079
|
-
|
|
5340
|
+
logger30.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
5080
5341
|
});
|
|
5081
5342
|
return data[0];
|
|
5082
5343
|
} catch (error) {
|
|
@@ -5114,9 +5375,9 @@ function useScheduleTaskRepository() {
|
|
|
5114
5375
|
);
|
|
5115
5376
|
}
|
|
5116
5377
|
delNamespace().then(() => {
|
|
5117
|
-
|
|
5378
|
+
logger30.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
5118
5379
|
}).catch((err) => {
|
|
5119
|
-
|
|
5380
|
+
logger30.error(
|
|
5120
5381
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
5121
5382
|
err
|
|
5122
5383
|
);
|
|
@@ -5139,7 +5400,7 @@ function useScheduleTaskRepository() {
|
|
|
5139
5400
|
}
|
|
5140
5401
|
|
|
5141
5402
|
// src/services/hygiene-schedule-task.service.ts
|
|
5142
|
-
import { logger as
|
|
5403
|
+
import { logger as logger31 } from "@7365admin1/node-server-utils";
|
|
5143
5404
|
function useScheduleTaskService() {
|
|
5144
5405
|
const { createParentChecklist } = useParentChecklistRepo();
|
|
5145
5406
|
const { getAllScheduleTask } = useScheduleTaskRepository();
|
|
@@ -5162,13 +5423,13 @@ function useScheduleTaskService() {
|
|
|
5162
5423
|
const currentDateString = now.toLocaleDateString("en-US", {
|
|
5163
5424
|
timeZone: "Asia/Singapore"
|
|
5164
5425
|
});
|
|
5165
|
-
|
|
5426
|
+
logger31.info(
|
|
5166
5427
|
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Current date ${currentDateString}, Schedule time ${schedule.time}, Start date ${schedule.startDate}, End date ${schedule.endDate}`
|
|
5167
5428
|
);
|
|
5168
5429
|
const startDate = /* @__PURE__ */ new Date(schedule.startDate + "T00:00:00");
|
|
5169
5430
|
const currentDateOnly = /* @__PURE__ */ new Date(currentDateString + "T00:00:00");
|
|
5170
5431
|
if (currentDateOnly < startDate) {
|
|
5171
|
-
|
|
5432
|
+
logger31.info(
|
|
5172
5433
|
`Schedule ${schedule._id}: Current date ${currentDateString} is before start date ${schedule.startDate}`
|
|
5173
5434
|
);
|
|
5174
5435
|
return false;
|
|
@@ -5176,7 +5437,7 @@ function useScheduleTaskService() {
|
|
|
5176
5437
|
if (schedule.endDate) {
|
|
5177
5438
|
const endDate = /* @__PURE__ */ new Date(schedule.endDate + "T00:00:00");
|
|
5178
5439
|
if (currentDateOnly > endDate) {
|
|
5179
|
-
|
|
5440
|
+
logger31.info(
|
|
5180
5441
|
`Schedule ${schedule._id}: Current date ${currentDateString} is after end date ${schedule.endDate}`
|
|
5181
5442
|
);
|
|
5182
5443
|
return false;
|
|
@@ -5185,17 +5446,17 @@ function useScheduleTaskService() {
|
|
|
5185
5446
|
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
5186
5447
|
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
5187
5448
|
if (!timeMatches) {
|
|
5188
|
-
|
|
5449
|
+
logger31.info(
|
|
5189
5450
|
`Schedule ${schedule._id}: Time does not match. Current: ${currentHour}:${currentMinute}, Expected: ${scheduleHour}:${scheduleMinute}`
|
|
5190
5451
|
);
|
|
5191
5452
|
return false;
|
|
5192
5453
|
}
|
|
5193
|
-
|
|
5454
|
+
logger31.info(
|
|
5194
5455
|
`Schedule ${schedule._id}: All conditions matched - Date is within range and time matches`
|
|
5195
5456
|
);
|
|
5196
5457
|
return true;
|
|
5197
5458
|
} catch (error) {
|
|
5198
|
-
|
|
5459
|
+
logger31.error(
|
|
5199
5460
|
`Error checking schedule conditions for ${schedule._id}:`,
|
|
5200
5461
|
error
|
|
5201
5462
|
);
|
|
@@ -5204,40 +5465,40 @@ function useScheduleTaskService() {
|
|
|
5204
5465
|
}
|
|
5205
5466
|
async function processScheduledTasks(currentDate) {
|
|
5206
5467
|
try {
|
|
5207
|
-
|
|
5468
|
+
logger31.info("Starting scheduled task processing...");
|
|
5208
5469
|
const scheduleTasks = await getAllScheduleTask();
|
|
5209
5470
|
if (!scheduleTasks || scheduleTasks.length === 0) {
|
|
5210
|
-
|
|
5471
|
+
logger31.info("No schedule tasks found to process");
|
|
5211
5472
|
return { processed: 0, validated: 0 };
|
|
5212
5473
|
}
|
|
5213
|
-
|
|
5474
|
+
logger31.info(`Found ${scheduleTasks.length} schedule tasks to check`);
|
|
5214
5475
|
let processedCount = 0;
|
|
5215
5476
|
let validatedCount = 0;
|
|
5216
5477
|
const validatedTasks = [];
|
|
5217
5478
|
for (const scheduleTask of scheduleTasks) {
|
|
5218
5479
|
try {
|
|
5219
|
-
|
|
5480
|
+
logger31.info(
|
|
5220
5481
|
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, startDate=${scheduleTask.startDate}, endDate=${scheduleTask.endDate}`
|
|
5221
5482
|
);
|
|
5222
5483
|
const shouldRun = checkScheduleConditions(scheduleTask, currentDate);
|
|
5223
5484
|
if (!shouldRun) {
|
|
5224
|
-
|
|
5485
|
+
logger31.info(
|
|
5225
5486
|
`Schedule ${scheduleTask._id} conditions not met, skipping`
|
|
5226
5487
|
);
|
|
5227
5488
|
continue;
|
|
5228
5489
|
}
|
|
5229
|
-
|
|
5490
|
+
logger31.info(
|
|
5230
5491
|
`Schedule ${scheduleTask._id} conditions validated, creating area checklists`
|
|
5231
5492
|
);
|
|
5232
5493
|
if (!scheduleTask._id) {
|
|
5233
|
-
|
|
5494
|
+
logger31.warn(`Schedule ${scheduleTask.title} has no _id, skipping`);
|
|
5234
5495
|
continue;
|
|
5235
5496
|
}
|
|
5236
5497
|
if (!scheduleTask.site) {
|
|
5237
|
-
|
|
5498
|
+
logger31.warn(`Schedule ${scheduleTask._id} has no site, skipping`);
|
|
5238
5499
|
continue;
|
|
5239
5500
|
}
|
|
5240
|
-
|
|
5501
|
+
logger31.info(
|
|
5241
5502
|
`Getting or creating parent checklist for schedule ${scheduleTask._id} in site ${scheduleTask.site}`
|
|
5242
5503
|
);
|
|
5243
5504
|
const parentChecklistIds = await createParentChecklist({
|
|
@@ -5245,7 +5506,7 @@ function useScheduleTaskService() {
|
|
|
5245
5506
|
createdAt: /* @__PURE__ */ new Date()
|
|
5246
5507
|
});
|
|
5247
5508
|
const parentChecklistId = Array.isArray(parentChecklistIds) ? parentChecklistIds[0] : parentChecklistIds;
|
|
5248
|
-
|
|
5509
|
+
logger31.info(
|
|
5249
5510
|
`Using parent checklist ${parentChecklistId}, now creating/updating area checklists`
|
|
5250
5511
|
);
|
|
5251
5512
|
for (const area of scheduleTask.areas) {
|
|
@@ -5258,14 +5519,14 @@ function useScheduleTaskService() {
|
|
|
5258
5519
|
unit: unit.unit.toString(),
|
|
5259
5520
|
name: unit.name
|
|
5260
5521
|
}));
|
|
5261
|
-
|
|
5522
|
+
logger31.info(
|
|
5262
5523
|
`Area ${area.name} (${areaId}): Using units from area details: ${JSON.stringify(
|
|
5263
5524
|
units
|
|
5264
5525
|
)}`
|
|
5265
5526
|
);
|
|
5266
5527
|
}
|
|
5267
5528
|
if (units.length === 0) {
|
|
5268
|
-
|
|
5529
|
+
logger31.warn(
|
|
5269
5530
|
`Area ${area.name} (${areaId}): No units found, skipping area.`
|
|
5270
5531
|
);
|
|
5271
5532
|
continue;
|
|
@@ -5276,11 +5537,11 @@ function useScheduleTaskService() {
|
|
|
5276
5537
|
parentChecklistId.toString(),
|
|
5277
5538
|
areaId
|
|
5278
5539
|
);
|
|
5279
|
-
|
|
5540
|
+
logger31.info(
|
|
5280
5541
|
`Area ${area.name} (${areaId}): Existing area checklist found: ${existingAreaChecklist ? "Yes" : "No"}`
|
|
5281
5542
|
);
|
|
5282
5543
|
if (existingAreaChecklist) {
|
|
5283
|
-
|
|
5544
|
+
logger31.info(
|
|
5284
5545
|
`Area ${area.name} (${areaId}): Existing checklist content: ${JSON.stringify(
|
|
5285
5546
|
existingAreaChecklist.checklist
|
|
5286
5547
|
)}`
|
|
@@ -5288,7 +5549,7 @@ function useScheduleTaskService() {
|
|
|
5288
5549
|
}
|
|
5289
5550
|
} catch (error) {
|
|
5290
5551
|
existingAreaChecklist = null;
|
|
5291
|
-
|
|
5552
|
+
logger31.info(
|
|
5292
5553
|
`Area ${area.name} (${areaId}): No existing area checklist found (exception).`
|
|
5293
5554
|
);
|
|
5294
5555
|
}
|
|
@@ -5302,7 +5563,7 @@ function useScheduleTaskService() {
|
|
|
5302
5563
|
...existingAreaChecklist.checklist || [],
|
|
5303
5564
|
newSet
|
|
5304
5565
|
];
|
|
5305
|
-
|
|
5566
|
+
logger31.info(
|
|
5306
5567
|
`Area ${area.name} (${areaId}): Appending new set ${newSet.set} to checklist. Updated checklist: ${JSON.stringify(
|
|
5307
5568
|
updatedChecklist
|
|
5308
5569
|
)}`
|
|
@@ -5310,7 +5571,7 @@ function useScheduleTaskService() {
|
|
|
5310
5571
|
await updateAreaChecklist(existingAreaChecklist._id, {
|
|
5311
5572
|
checklist: updatedChecklist
|
|
5312
5573
|
});
|
|
5313
|
-
|
|
5574
|
+
logger31.info(
|
|
5314
5575
|
`Appended set ${newSet.set} to area checklist for area ${area.name}`
|
|
5315
5576
|
);
|
|
5316
5577
|
try {
|
|
@@ -5318,13 +5579,13 @@ function useScheduleTaskService() {
|
|
|
5318
5579
|
parentChecklistId.toString(),
|
|
5319
5580
|
areaId
|
|
5320
5581
|
);
|
|
5321
|
-
|
|
5582
|
+
logger31.info(
|
|
5322
5583
|
`Area ${area.name} (${areaId}): Checklist after update: ${JSON.stringify(
|
|
5323
5584
|
verifyChecklist.checklist
|
|
5324
5585
|
)}`
|
|
5325
5586
|
);
|
|
5326
5587
|
} catch (verifyError) {
|
|
5327
|
-
|
|
5588
|
+
logger31.warn(
|
|
5328
5589
|
`Area ${area.name} (${areaId}): Error verifying checklist after update:`,
|
|
5329
5590
|
verifyError
|
|
5330
5591
|
);
|
|
@@ -5343,50 +5604,50 @@ function useScheduleTaskService() {
|
|
|
5343
5604
|
],
|
|
5344
5605
|
createdBy: scheduleTask.createdBy
|
|
5345
5606
|
};
|
|
5346
|
-
|
|
5607
|
+
logger31.info(
|
|
5347
5608
|
`Area ${area.name} (${areaId}): Creating new area checklist with data: ${JSON.stringify(
|
|
5348
5609
|
checklistData
|
|
5349
5610
|
)}`
|
|
5350
5611
|
);
|
|
5351
5612
|
await createAreaChecklist(checklistData);
|
|
5352
|
-
|
|
5613
|
+
logger31.info(`Created new area checklist for area ${area.name}`);
|
|
5353
5614
|
try {
|
|
5354
5615
|
const verifyChecklist = await getAreaChecklistByAreaAndSchedule(
|
|
5355
5616
|
parentChecklistId.toString(),
|
|
5356
5617
|
areaId
|
|
5357
5618
|
);
|
|
5358
|
-
|
|
5619
|
+
logger31.info(
|
|
5359
5620
|
`Area ${area.name} (${areaId}): Checklist after creation: ${JSON.stringify(
|
|
5360
5621
|
verifyChecklist.checklist
|
|
5361
5622
|
)}`
|
|
5362
5623
|
);
|
|
5363
5624
|
} catch (verifyError) {
|
|
5364
|
-
|
|
5625
|
+
logger31.warn(
|
|
5365
5626
|
`Area ${area.name} (${areaId}): Error verifying checklist after creation:`,
|
|
5366
5627
|
verifyError
|
|
5367
5628
|
);
|
|
5368
5629
|
}
|
|
5369
5630
|
}
|
|
5370
5631
|
} catch (error) {
|
|
5371
|
-
|
|
5632
|
+
logger31.error(`Error processing area ${area.name}:`, error);
|
|
5372
5633
|
continue;
|
|
5373
5634
|
}
|
|
5374
5635
|
}
|
|
5375
5636
|
processedCount++;
|
|
5376
5637
|
validatedCount++;
|
|
5377
5638
|
validatedTasks.push(scheduleTask);
|
|
5378
|
-
|
|
5639
|
+
logger31.info(
|
|
5379
5640
|
`Successfully processed schedule ${scheduleTask._id}, created/updated area checklists for all areas.`
|
|
5380
5641
|
);
|
|
5381
5642
|
} catch (error) {
|
|
5382
|
-
|
|
5643
|
+
logger31.error(
|
|
5383
5644
|
`Error processing schedule task ${scheduleTask._id}:`,
|
|
5384
5645
|
error
|
|
5385
5646
|
);
|
|
5386
5647
|
continue;
|
|
5387
5648
|
}
|
|
5388
5649
|
}
|
|
5389
|
-
|
|
5650
|
+
logger31.info(
|
|
5390
5651
|
`Scheduled task processing completed. Processed: ${processedCount}, Validated: ${validatedCount} tasks`
|
|
5391
5652
|
);
|
|
5392
5653
|
return {
|
|
@@ -5395,7 +5656,7 @@ function useScheduleTaskService() {
|
|
|
5395
5656
|
tasks: validatedTasks
|
|
5396
5657
|
};
|
|
5397
5658
|
} catch (error) {
|
|
5398
|
-
|
|
5659
|
+
logger31.error("Error processing scheduled tasks:", error);
|
|
5399
5660
|
throw error;
|
|
5400
5661
|
}
|
|
5401
5662
|
}
|
|
@@ -5404,7 +5665,7 @@ function useScheduleTaskService() {
|
|
|
5404
5665
|
|
|
5405
5666
|
// src/controllers/hygiene-schedule-task.controller.ts
|
|
5406
5667
|
import Joi17 from "joi";
|
|
5407
|
-
import { BadRequestError as BadRequestError30, logger as
|
|
5668
|
+
import { BadRequestError as BadRequestError30, logger as logger32 } from "@7365admin1/node-server-utils";
|
|
5408
5669
|
function useScheduleTaskController() {
|
|
5409
5670
|
const {
|
|
5410
5671
|
createScheduleTask: _createScheduleTask,
|
|
@@ -5422,7 +5683,7 @@ function useScheduleTaskController() {
|
|
|
5422
5683
|
const payload = { ...req.body, ...req.params, createdBy };
|
|
5423
5684
|
const { error } = scheduleTaskSchema.validate(payload);
|
|
5424
5685
|
if (error) {
|
|
5425
|
-
|
|
5686
|
+
logger32.log({ level: "error", message: error.message });
|
|
5426
5687
|
next(new BadRequestError30(error.message));
|
|
5427
5688
|
return;
|
|
5428
5689
|
}
|
|
@@ -5431,7 +5692,7 @@ function useScheduleTaskController() {
|
|
|
5431
5692
|
res.status(201).json({ message: "Schedule task created successfully.", id });
|
|
5432
5693
|
return;
|
|
5433
5694
|
} catch (error2) {
|
|
5434
|
-
|
|
5695
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5435
5696
|
next(error2);
|
|
5436
5697
|
return;
|
|
5437
5698
|
}
|
|
@@ -5446,7 +5707,7 @@ function useScheduleTaskController() {
|
|
|
5446
5707
|
});
|
|
5447
5708
|
const { error } = validation.validate(query);
|
|
5448
5709
|
if (error) {
|
|
5449
|
-
|
|
5710
|
+
logger32.log({ level: "error", message: error.message });
|
|
5450
5711
|
next(new BadRequestError30(error.message));
|
|
5451
5712
|
return;
|
|
5452
5713
|
}
|
|
@@ -5464,7 +5725,7 @@ function useScheduleTaskController() {
|
|
|
5464
5725
|
res.json(data);
|
|
5465
5726
|
return;
|
|
5466
5727
|
} catch (error2) {
|
|
5467
|
-
|
|
5728
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5468
5729
|
next(error2);
|
|
5469
5730
|
return;
|
|
5470
5731
|
}
|
|
@@ -5479,7 +5740,7 @@ function useScheduleTaskController() {
|
|
|
5479
5740
|
});
|
|
5480
5741
|
const { error } = validation.validate(query);
|
|
5481
5742
|
if (error) {
|
|
5482
|
-
|
|
5743
|
+
logger32.log({ level: "error", message: error.message });
|
|
5483
5744
|
next(new BadRequestError30(error.message));
|
|
5484
5745
|
return;
|
|
5485
5746
|
}
|
|
@@ -5497,7 +5758,7 @@ function useScheduleTaskController() {
|
|
|
5497
5758
|
res.json(data);
|
|
5498
5759
|
return;
|
|
5499
5760
|
} catch (error2) {
|
|
5500
|
-
|
|
5761
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5501
5762
|
next(error2);
|
|
5502
5763
|
return;
|
|
5503
5764
|
}
|
|
@@ -5507,7 +5768,7 @@ function useScheduleTaskController() {
|
|
|
5507
5768
|
const _id = req.params.id;
|
|
5508
5769
|
const { error, value } = validation.validate(_id);
|
|
5509
5770
|
if (error) {
|
|
5510
|
-
|
|
5771
|
+
logger32.log({ level: "error", message: error.message });
|
|
5511
5772
|
next(new BadRequestError30(error.message));
|
|
5512
5773
|
return;
|
|
5513
5774
|
}
|
|
@@ -5516,7 +5777,7 @@ function useScheduleTaskController() {
|
|
|
5516
5777
|
res.json(data);
|
|
5517
5778
|
return;
|
|
5518
5779
|
} catch (error2) {
|
|
5519
|
-
|
|
5780
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5520
5781
|
next(error2);
|
|
5521
5782
|
return;
|
|
5522
5783
|
}
|
|
@@ -5539,7 +5800,7 @@ function useScheduleTaskController() {
|
|
|
5539
5800
|
});
|
|
5540
5801
|
const { error } = validation.validate(payload);
|
|
5541
5802
|
if (error) {
|
|
5542
|
-
|
|
5803
|
+
logger32.log({ level: "error", message: error.message });
|
|
5543
5804
|
next(new BadRequestError30(error.message));
|
|
5544
5805
|
return;
|
|
5545
5806
|
}
|
|
@@ -5549,7 +5810,7 @@ function useScheduleTaskController() {
|
|
|
5549
5810
|
res.json({ message: "Schedule task updated successfully." });
|
|
5550
5811
|
return;
|
|
5551
5812
|
} catch (error2) {
|
|
5552
|
-
|
|
5813
|
+
logger32.log({ level: "error", message: error2.message });
|
|
5553
5814
|
next(error2);
|
|
5554
5815
|
return;
|
|
5555
5816
|
}
|
|
@@ -5562,6 +5823,275 @@ function useScheduleTaskController() {
|
|
|
5562
5823
|
updateScheduleTask
|
|
5563
5824
|
};
|
|
5564
5825
|
}
|
|
5826
|
+
|
|
5827
|
+
// src/services/hygiene-qr.service.ts
|
|
5828
|
+
import { logger as logger33 } from "@7365admin1/node-server-utils";
|
|
5829
|
+
import { launch } from "puppeteer";
|
|
5830
|
+
import QRCode from "qrcode";
|
|
5831
|
+
function useQRService() {
|
|
5832
|
+
async function generateQRDataUrl(qrUrl) {
|
|
5833
|
+
return QRCode.toDataURL(qrUrl, {
|
|
5834
|
+
width: 350,
|
|
5835
|
+
margin: 2,
|
|
5836
|
+
errorCorrectionLevel: "M",
|
|
5837
|
+
color: {
|
|
5838
|
+
dark: "#000000",
|
|
5839
|
+
light: "#FFFFFF"
|
|
5840
|
+
}
|
|
5841
|
+
});
|
|
5842
|
+
}
|
|
5843
|
+
async function generateQRImage(qrUrl) {
|
|
5844
|
+
try {
|
|
5845
|
+
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
5846
|
+
const browser = await launch({
|
|
5847
|
+
headless: true,
|
|
5848
|
+
executablePath: process.env.CHROME_BINARY,
|
|
5849
|
+
args: [`--no-sandbox`, `--disable-gpu`, `--disable-dev-shm-usage`]
|
|
5850
|
+
});
|
|
5851
|
+
const page = await browser.newPage();
|
|
5852
|
+
await page.setViewport({
|
|
5853
|
+
width: 400,
|
|
5854
|
+
height: 400
|
|
5855
|
+
});
|
|
5856
|
+
const html = `
|
|
5857
|
+
<!DOCTYPE html>
|
|
5858
|
+
<html>
|
|
5859
|
+
<head>
|
|
5860
|
+
<meta charset="UTF-8">
|
|
5861
|
+
<style>
|
|
5862
|
+
body {
|
|
5863
|
+
margin: 0;
|
|
5864
|
+
padding: 20px;
|
|
5865
|
+
display: flex;
|
|
5866
|
+
justify-content: center;
|
|
5867
|
+
align-items: center;
|
|
5868
|
+
background: white;
|
|
5869
|
+
min-height: 100vh;
|
|
5870
|
+
}
|
|
5871
|
+
#qr-image {
|
|
5872
|
+
display: block;
|
|
5873
|
+
width: 350px;
|
|
5874
|
+
height: 350px;
|
|
5875
|
+
}
|
|
5876
|
+
</style>
|
|
5877
|
+
</head>
|
|
5878
|
+
<body>
|
|
5879
|
+
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
5880
|
+
</body>
|
|
5881
|
+
</html>
|
|
5882
|
+
`;
|
|
5883
|
+
await page.setContent(html, {
|
|
5884
|
+
waitUntil: ["load", "networkidle0"]
|
|
5885
|
+
});
|
|
5886
|
+
await page.waitForSelector("#qr-image", { timeout: 1e4 });
|
|
5887
|
+
const imageBuffer = await page.screenshot({
|
|
5888
|
+
type: "png",
|
|
5889
|
+
clip: {
|
|
5890
|
+
x: 0,
|
|
5891
|
+
y: 0,
|
|
5892
|
+
width: 400,
|
|
5893
|
+
height: 400
|
|
5894
|
+
}
|
|
5895
|
+
});
|
|
5896
|
+
await browser.close();
|
|
5897
|
+
return imageBuffer;
|
|
5898
|
+
} catch (error) {
|
|
5899
|
+
logger33.log({
|
|
5900
|
+
level: "error",
|
|
5901
|
+
message: `Failed to generate QR image: ${error.message}`
|
|
5902
|
+
});
|
|
5903
|
+
throw error;
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
async function generateQRPDF(qrUrl, title) {
|
|
5907
|
+
try {
|
|
5908
|
+
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
5909
|
+
const browser = await launch({
|
|
5910
|
+
headless: true,
|
|
5911
|
+
executablePath: process.env.CHROME_BINARY,
|
|
5912
|
+
args: [`--no-sandbox`, `--disable-gpu`, `--disable-dev-shm-usage`]
|
|
5913
|
+
});
|
|
5914
|
+
const page = await browser.newPage();
|
|
5915
|
+
await page.setViewport({
|
|
5916
|
+
width: 800,
|
|
5917
|
+
height: 1100
|
|
5918
|
+
});
|
|
5919
|
+
const escapedTitle = (title || "Cleaning Schedule QR Code").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5920
|
+
const html = `
|
|
5921
|
+
<!DOCTYPE html>
|
|
5922
|
+
<html>
|
|
5923
|
+
<head>
|
|
5924
|
+
<meta charset="UTF-8">
|
|
5925
|
+
<style>
|
|
5926
|
+
* {
|
|
5927
|
+
margin: 0;
|
|
5928
|
+
padding: 0;
|
|
5929
|
+
box-sizing: border-box;
|
|
5930
|
+
}
|
|
5931
|
+
body {
|
|
5932
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
5933
|
+
background: white;
|
|
5934
|
+
padding: 60px 40px;
|
|
5935
|
+
display: flex;
|
|
5936
|
+
flex-direction: column;
|
|
5937
|
+
align-items: center;
|
|
5938
|
+
justify-content: center;
|
|
5939
|
+
min-height: 100vh;
|
|
5940
|
+
}
|
|
5941
|
+
.qr-container {
|
|
5942
|
+
text-align: center;
|
|
5943
|
+
background: transparent;
|
|
5944
|
+
padding: 0;
|
|
5945
|
+
}
|
|
5946
|
+
h1 {
|
|
5947
|
+
font-size: 28px;
|
|
5948
|
+
color: #333;
|
|
5949
|
+
margin-bottom: 20px;
|
|
5950
|
+
font-weight: 600;
|
|
5951
|
+
}
|
|
5952
|
+
.qr-wrapper {
|
|
5953
|
+
display: inline-block;
|
|
5954
|
+
padding: 0;
|
|
5955
|
+
background: transparent;
|
|
5956
|
+
border: none;
|
|
5957
|
+
border-radius: 0;
|
|
5958
|
+
margin: 20px 0;
|
|
5959
|
+
}
|
|
5960
|
+
#qr-image {
|
|
5961
|
+
display: block;
|
|
5962
|
+
width: 350px;
|
|
5963
|
+
height: 350px;
|
|
5964
|
+
}
|
|
5965
|
+
.instructions {
|
|
5966
|
+
margin-top: 30px;
|
|
5967
|
+
font-size: 16px;
|
|
5968
|
+
color: #666;
|
|
5969
|
+
line-height: 1.6;
|
|
5970
|
+
}
|
|
5971
|
+
.url-info {
|
|
5972
|
+
margin-top: 20px;
|
|
5973
|
+
padding: 15px;
|
|
5974
|
+
background: #f5f5f5;
|
|
5975
|
+
border-radius: 6px;
|
|
5976
|
+
font-size: 12px;
|
|
5977
|
+
color: #888;
|
|
5978
|
+
word-break: break-all;
|
|
5979
|
+
}
|
|
5980
|
+
</style>
|
|
5981
|
+
</head>
|
|
5982
|
+
<body>
|
|
5983
|
+
<div class="qr-container">
|
|
5984
|
+
<h1>${escapedTitle}</h1>
|
|
5985
|
+
<div class="qr-wrapper">
|
|
5986
|
+
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
5987
|
+
</div>
|
|
5988
|
+
<div class="instructions">
|
|
5989
|
+
<p>Scan this QR code to access the cleaning schedule.</p>
|
|
5990
|
+
</div>
|
|
5991
|
+
<div class="url-info">
|
|
5992
|
+
${qrUrl}
|
|
5993
|
+
</div>
|
|
5994
|
+
</div>
|
|
5995
|
+
</body>
|
|
5996
|
+
</html>
|
|
5997
|
+
`;
|
|
5998
|
+
await page.setContent(html, {
|
|
5999
|
+
waitUntil: ["load", "networkidle0"]
|
|
6000
|
+
});
|
|
6001
|
+
await page.waitForSelector("#qr-image", { timeout: 1e4 });
|
|
6002
|
+
await page.waitForFunction(
|
|
6003
|
+
() => {
|
|
6004
|
+
const img = document.getElementById("qr-image");
|
|
6005
|
+
return img && img.complete && img.naturalWidth > 0;
|
|
6006
|
+
},
|
|
6007
|
+
{ timeout: 1e4 }
|
|
6008
|
+
);
|
|
6009
|
+
const pdfBuffer = await page.pdf({
|
|
6010
|
+
format: "A4",
|
|
6011
|
+
printBackground: true,
|
|
6012
|
+
margin: {
|
|
6013
|
+
top: "20mm",
|
|
6014
|
+
right: "20mm",
|
|
6015
|
+
bottom: "20mm",
|
|
6016
|
+
left: "20mm"
|
|
6017
|
+
}
|
|
6018
|
+
});
|
|
6019
|
+
await browser.close();
|
|
6020
|
+
return pdfBuffer;
|
|
6021
|
+
} catch (error) {
|
|
6022
|
+
logger33.log({
|
|
6023
|
+
level: "error",
|
|
6024
|
+
message: `Failed to generate QR PDF: ${error.message}`
|
|
6025
|
+
});
|
|
6026
|
+
throw error;
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
return {
|
|
6030
|
+
generateQRImage,
|
|
6031
|
+
generateQRPDF
|
|
6032
|
+
};
|
|
6033
|
+
}
|
|
6034
|
+
|
|
6035
|
+
// src/controllers/hygiene-qr.controller.ts
|
|
6036
|
+
import Joi18 from "joi";
|
|
6037
|
+
import { BadRequestError as BadRequestError31, logger as logger34 } from "@7365admin1/node-server-utils";
|
|
6038
|
+
function useQRController() {
|
|
6039
|
+
const { generateQRImage: _generateQRImage, generateQRPDF: _generateQRPDF } = useQRService();
|
|
6040
|
+
async function generateQR(req, res, next) {
|
|
6041
|
+
const validation = Joi18.object({
|
|
6042
|
+
url: Joi18.string().uri().required(),
|
|
6043
|
+
filename: Joi18.string().optional().allow("", null),
|
|
6044
|
+
title: Joi18.string().optional().allow("", null),
|
|
6045
|
+
download: Joi18.boolean().optional().default(false)
|
|
6046
|
+
});
|
|
6047
|
+
const query = { ...req.query };
|
|
6048
|
+
const { error, value } = validation.validate(query);
|
|
6049
|
+
if (error) {
|
|
6050
|
+
logger34.log({ level: "error", message: error.message });
|
|
6051
|
+
next(new BadRequestError31(error.message));
|
|
6052
|
+
return;
|
|
6053
|
+
}
|
|
6054
|
+
try {
|
|
6055
|
+
const { url, filename, title, download } = value;
|
|
6056
|
+
if (download) {
|
|
6057
|
+
const pdfBuffer = await _generateQRPDF(url, title);
|
|
6058
|
+
if (!pdfBuffer || pdfBuffer.length === 0) {
|
|
6059
|
+
throw new Error("Generated QR PDF is empty or invalid.");
|
|
6060
|
+
}
|
|
6061
|
+
const sanitizedFilename = (filename || "qrcode").replace(/['"]/g, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6062
|
+
const date = /* @__PURE__ */ new Date();
|
|
6063
|
+
const formattedDate = `${String(date.getMonth() + 1).padStart(
|
|
6064
|
+
2,
|
|
6065
|
+
"0"
|
|
6066
|
+
)}-${String(date.getDate()).padStart(2, "0")}-${date.getFullYear()}`;
|
|
6067
|
+
res.setHeader("Content-Type", "application/pdf");
|
|
6068
|
+
res.setHeader(
|
|
6069
|
+
"Content-Disposition",
|
|
6070
|
+
`attachment; filename="${sanitizedFilename}-${formattedDate}.pdf"`
|
|
6071
|
+
);
|
|
6072
|
+
res.setHeader("Content-Length", pdfBuffer.length);
|
|
6073
|
+
res.end(pdfBuffer);
|
|
6074
|
+
} else {
|
|
6075
|
+
const imageBuffer = await _generateQRImage(url);
|
|
6076
|
+
if (!imageBuffer || imageBuffer.length === 0) {
|
|
6077
|
+
throw new Error("Generated QR image is empty or invalid.");
|
|
6078
|
+
}
|
|
6079
|
+
res.setHeader("Content-Type", "image/png");
|
|
6080
|
+
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
6081
|
+
res.setHeader("Content-Length", imageBuffer.length);
|
|
6082
|
+
res.end(imageBuffer);
|
|
6083
|
+
}
|
|
6084
|
+
return;
|
|
6085
|
+
} catch (error2) {
|
|
6086
|
+
logger34.log({ level: "error", message: error2.message });
|
|
6087
|
+
next(error2);
|
|
6088
|
+
return;
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
return {
|
|
6092
|
+
generateQR
|
|
6093
|
+
};
|
|
6094
|
+
}
|
|
5565
6095
|
export {
|
|
5566
6096
|
MArea,
|
|
5567
6097
|
MAreaChecklist,
|
|
@@ -5588,6 +6118,7 @@ export {
|
|
|
5588
6118
|
useAreaChecklistRepo,
|
|
5589
6119
|
useAreaChecklistService,
|
|
5590
6120
|
useAreaController,
|
|
6121
|
+
useAreaExportService,
|
|
5591
6122
|
useAreaRepo,
|
|
5592
6123
|
useAreaService,
|
|
5593
6124
|
useCheckOutItemController,
|
|
@@ -5597,6 +6128,8 @@ export {
|
|
|
5597
6128
|
useHygieneDashboardRepository,
|
|
5598
6129
|
useParentChecklistController,
|
|
5599
6130
|
useParentChecklistRepo,
|
|
6131
|
+
useQRController,
|
|
6132
|
+
useQRService,
|
|
5600
6133
|
useScheduleTaskController,
|
|
5601
6134
|
useScheduleTaskRepository,
|
|
5602
6135
|
useScheduleTaskService,
|
|
@@ -5606,6 +6139,7 @@ export {
|
|
|
5606
6139
|
useSupplyController,
|
|
5607
6140
|
useSupplyRepository,
|
|
5608
6141
|
useUnitController,
|
|
6142
|
+
useUnitExportService,
|
|
5609
6143
|
useUnitRepository,
|
|
5610
6144
|
useUnitService
|
|
5611
6145
|
};
|