@iservice365/module-hygiene 0.0.1 → 0.1.1

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/dist/index.js CHANGED
@@ -1,2 +1,4682 @@
1
1
  "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ MArea: () => MArea,
34
+ MAreaChecklist: () => MAreaChecklist,
35
+ MParentChecklist: () => MParentChecklist,
36
+ MScheduleTaskArea: () => MScheduleTaskArea,
37
+ MToiletLocation: () => MToiletLocation,
38
+ MUnit: () => MUnit,
39
+ MUnitChecklist: () => MUnitChecklist,
40
+ allowedStatus: () => allowedStatus,
41
+ allowedTypes: () => allowedTypes,
42
+ areaChecklistSchema: () => areaChecklistSchema,
43
+ areaSchema: () => areaSchema,
44
+ parentChecklistSchema: () => parentChecklistSchema,
45
+ scheduleTaskAreaSchema: () => scheduleTaskAreaSchema,
46
+ toiletLocationSchema: () => toiletLocationSchema,
47
+ unitChecklistSchema: () => unitChecklistSchema,
48
+ unitSchema: () => unitSchema,
49
+ useAreaChecklistController: () => useAreaChecklistController,
50
+ useAreaChecklistRepo: () => useAreaChecklistRepo,
51
+ useAreaController: () => useAreaController,
52
+ useAreaRepository: () => useAreaRepository,
53
+ useAreaService: () => useAreaService,
54
+ useParentChecklistController: () => useParentChecklistController,
55
+ useParentChecklistRepo: () => useParentChecklistRepo,
56
+ useScheduleTaskAreaController: () => useScheduleTaskAreaController,
57
+ useScheduleTaskAreaRepository: () => useScheduleTaskAreaRepository,
58
+ useScheduleTaskAreaService: () => useScheduleTaskAreaService,
59
+ useToiletLocationController: () => useToiletLocationController,
60
+ useToiletLocationRepository: () => useToiletLocationRepository,
61
+ useToiletLocationService: () => useToiletLocationService,
62
+ useUnitChecklistController: () => useUnitChecklistController,
63
+ useUnitChecklistRepo: () => useUnitChecklistRepo,
64
+ useUnitController: () => useUnitController,
65
+ useUnitRepository: () => useUnitRepository,
66
+ useUnitService: () => useUnitService
67
+ });
68
+ module.exports = __toCommonJS(src_exports);
69
+
70
+ // src/models/hygiene-area.model.ts
71
+ var import_node_server_utils = require("@iservice365/node-server-utils");
72
+ var import_joi = __toESM(require("joi"));
73
+ var import_mongodb = require("mongodb");
74
+ var areaSchema = import_joi.default.object({
75
+ name: import_joi.default.string().required(),
76
+ site: import_joi.default.string().hex().required(),
77
+ createdBy: import_joi.default.string().hex().required(),
78
+ checklist: import_joi.default.array().items(
79
+ import_joi.default.object({
80
+ _id: import_joi.default.string().hex().required(),
81
+ name: import_joi.default.string().required()
82
+ })
83
+ ).optional()
84
+ });
85
+ function MArea(value) {
86
+ const { error } = areaSchema.validate(value);
87
+ if (error) {
88
+ import_node_server_utils.logger.info(`Hygiene Area Model: ${error.message}`);
89
+ throw new import_node_server_utils.BadRequestError(error.message);
90
+ }
91
+ if (value.site) {
92
+ try {
93
+ value.site = new import_mongodb.ObjectId(value.site);
94
+ } catch (error2) {
95
+ throw new import_node_server_utils.BadRequestError("Invalid site ID format.");
96
+ }
97
+ }
98
+ if (value.createdBy) {
99
+ try {
100
+ value.createdBy = new import_mongodb.ObjectId(value.createdBy);
101
+ } catch (error2) {
102
+ throw new import_node_server_utils.BadRequestError("Invalid createdBy ID format.");
103
+ }
104
+ }
105
+ if (value.checklist && Array.isArray(value.checklist)) {
106
+ value.checklist = value.checklist.map((item) => {
107
+ try {
108
+ return {
109
+ ...item,
110
+ _id: new import_mongodb.ObjectId(item._id)
111
+ };
112
+ } catch (error2) {
113
+ throw new import_node_server_utils.BadRequestError(
114
+ `Invalid checklist item ID format: ${item._id}`
115
+ );
116
+ }
117
+ });
118
+ }
119
+ return {
120
+ name: value.name,
121
+ site: value.site,
122
+ createdBy: value.createdBy,
123
+ checklist: value.checklist,
124
+ status: value.status ?? "active",
125
+ createdAt: /* @__PURE__ */ new Date(),
126
+ updatedAt: value.updatedAt ?? "",
127
+ deletedAt: value.deletedAt ?? ""
128
+ };
129
+ }
130
+
131
+ // src/repositories/hygiene-area.repository.ts
132
+ var import_mongodb2 = require("mongodb");
133
+ var import_node_server_utils2 = require("@iservice365/node-server-utils");
134
+ function useAreaRepository() {
135
+ const db = import_node_server_utils2.useAtlas.getDb();
136
+ if (!db) {
137
+ throw new import_node_server_utils2.InternalServerError("Unable to connect to server.");
138
+ }
139
+ const namespace_collection = "hygiene-areas";
140
+ const collection = db.collection(namespace_collection);
141
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils2.useCache)(namespace_collection);
142
+ async function createIndex() {
143
+ try {
144
+ await collection.createIndexes([
145
+ { key: { site: 1 } },
146
+ { key: { createdBy: 1 } },
147
+ { key: { status: 1 } }
148
+ ]);
149
+ } catch (error) {
150
+ throw new import_node_server_utils2.InternalServerError("Failed to create index on hygiene area.");
151
+ }
152
+ }
153
+ async function createTextIndex() {
154
+ try {
155
+ await collection.createIndex({ name: "text" });
156
+ } catch (error) {
157
+ throw new import_node_server_utils2.InternalServerError(
158
+ "Failed to create text index on hygiene area."
159
+ );
160
+ }
161
+ }
162
+ async function createUniqueIndex() {
163
+ try {
164
+ await collection.createIndex(
165
+ { name: 1, site: 1, deletedAt: 1 },
166
+ { unique: true }
167
+ );
168
+ } catch (error) {
169
+ throw new import_node_server_utils2.InternalServerError(
170
+ "Failed to create unique index on hygiene area."
171
+ );
172
+ }
173
+ }
174
+ async function createArea(value, session) {
175
+ try {
176
+ value = MArea(value);
177
+ const res = await collection.insertOne(value, { session });
178
+ delNamespace().then(() => {
179
+ import_node_server_utils2.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
180
+ }).catch((err) => {
181
+ import_node_server_utils2.logger.error(
182
+ `Failed to clear cache for namespace: ${namespace_collection}`,
183
+ err
184
+ );
185
+ });
186
+ return res.insertedId;
187
+ } catch (error) {
188
+ const isDuplicated = error.message.includes("duplicate");
189
+ if (isDuplicated) {
190
+ throw new import_node_server_utils2.BadRequestError("Area already exists.");
191
+ }
192
+ throw error;
193
+ }
194
+ }
195
+ async function getAreas({
196
+ page = 1,
197
+ limit = 10,
198
+ search = "",
199
+ startDate = "",
200
+ endDate = "",
201
+ site = ""
202
+ }) {
203
+ page = page > 0 ? page - 1 : 0;
204
+ const query = {
205
+ status: { $ne: "deleted" }
206
+ };
207
+ const cacheOptions = {
208
+ page,
209
+ limit
210
+ };
211
+ if (site) {
212
+ try {
213
+ site = new import_mongodb2.ObjectId(site);
214
+ cacheOptions.site = site.toString();
215
+ } catch (error) {
216
+ throw new import_node_server_utils2.BadRequestError("Invalid site ID format.");
217
+ }
218
+ }
219
+ if (search) {
220
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
221
+ cacheOptions.search = search;
222
+ }
223
+ if (startDate && endDate) {
224
+ query.createdAt = {
225
+ $gte: new Date(startDate),
226
+ $lte: new Date(endDate)
227
+ };
228
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
229
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
230
+ } else if (startDate) {
231
+ query.createdAt = { $gte: new Date(startDate) };
232
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
233
+ } else if (endDate) {
234
+ query.createdAt = { $lte: new Date(endDate) };
235
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
236
+ }
237
+ const cacheKey = (0, import_node_server_utils2.makeCacheKey)(namespace_collection, cacheOptions);
238
+ const cachedData = await getCache(cacheKey);
239
+ if (cachedData) {
240
+ import_node_server_utils2.logger.info(`Cache hit for key: ${cacheKey}`);
241
+ return cachedData;
242
+ }
243
+ try {
244
+ const items = await collection.aggregate([
245
+ { $match: query },
246
+ {
247
+ $lookup: {
248
+ from: "sites",
249
+ localField: "site",
250
+ foreignField: "_id",
251
+ pipeline: [{ $project: { name: 1 } }],
252
+ as: "site"
253
+ }
254
+ },
255
+ {
256
+ $unwind: {
257
+ path: "$site",
258
+ preserveNullAndEmptyArrays: true
259
+ }
260
+ },
261
+ {
262
+ $lookup: {
263
+ from: "users",
264
+ localField: "createdBy",
265
+ foreignField: "_id",
266
+ pipeline: [{ $project: { name: 1 } }],
267
+ as: "createdBy"
268
+ }
269
+ },
270
+ {
271
+ $unwind: {
272
+ path: "$createdBy",
273
+ preserveNullAndEmptyArrays: true
274
+ }
275
+ },
276
+ {
277
+ $project: {
278
+ name: 1,
279
+ site: "$site._id",
280
+ siteName: "$site.name",
281
+ createdByName: "$createdBy.name",
282
+ checklist: 1,
283
+ status: 1,
284
+ createdAt: 1
285
+ }
286
+ },
287
+ { $sort: { _id: -1 } },
288
+ { $skip: page * limit },
289
+ { $limit: limit }
290
+ ]).toArray();
291
+ const length = await collection.countDocuments(query);
292
+ const data = (0, import_node_server_utils2.paginate)(items, page, limit, length);
293
+ setCache(cacheKey, data, 15 * 60).then(() => {
294
+ import_node_server_utils2.logger.info(`Cache set for key: ${cacheKey}`);
295
+ }).catch((err) => {
296
+ import_node_server_utils2.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
297
+ });
298
+ return data;
299
+ } catch (error) {
300
+ throw error;
301
+ }
302
+ }
303
+ async function getAreaById(_id) {
304
+ try {
305
+ _id = new import_mongodb2.ObjectId(_id);
306
+ } catch (error) {
307
+ throw new import_node_server_utils2.BadRequestError("Invalid area ID format.");
308
+ }
309
+ const query = {
310
+ _id,
311
+ status: { $ne: "deleted" }
312
+ };
313
+ const cacheKey = (0, import_node_server_utils2.makeCacheKey)(namespace_collection, {
314
+ _id: _id.toString()
315
+ });
316
+ const cachedData = await getCache(cacheKey);
317
+ if (cachedData) {
318
+ import_node_server_utils2.logger.info(`Cache hit for key: ${cacheKey}`);
319
+ return cachedData;
320
+ }
321
+ try {
322
+ const data = await collection.aggregate([
323
+ { $match: query },
324
+ {
325
+ $project: {
326
+ name: 1,
327
+ checklist: 1
328
+ }
329
+ }
330
+ ]).toArray();
331
+ if (!data || !data.length) {
332
+ throw new import_node_server_utils2.NotFoundError("Area not found.");
333
+ }
334
+ setCache(cacheKey, data[0], 15 * 60).then(() => {
335
+ import_node_server_utils2.logger.info(`Cache set for key: ${cacheKey}`);
336
+ }).catch((err) => {
337
+ import_node_server_utils2.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
338
+ });
339
+ return data[0];
340
+ } catch (error) {
341
+ throw error;
342
+ }
343
+ }
344
+ async function getAreaByName(name, site) {
345
+ try {
346
+ if (site)
347
+ site = new import_mongodb2.ObjectId(site);
348
+ } catch (error) {
349
+ throw new import_node_server_utils2.BadRequestError("Invalid site ID format.");
350
+ }
351
+ try {
352
+ return await collection.findOne({
353
+ name: { $regex: new RegExp(`^${name}$`, "i") },
354
+ ...site && { site }
355
+ });
356
+ } catch (error) {
357
+ throw new import_node_server_utils2.BadRequestError("Unable to fetch area by name.");
358
+ }
359
+ }
360
+ async function updateArea(_id, value) {
361
+ try {
362
+ _id = new import_mongodb2.ObjectId(_id);
363
+ } catch (error) {
364
+ throw new import_node_server_utils2.BadRequestError("Invalid area ID format.");
365
+ }
366
+ try {
367
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
368
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
369
+ if (res.modifiedCount === 0) {
370
+ throw new import_node_server_utils2.InternalServerError("Unable to update cleaning area.");
371
+ }
372
+ delNamespace().then(() => {
373
+ import_node_server_utils2.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
374
+ }).catch((err) => {
375
+ import_node_server_utils2.logger.error(
376
+ `Failed to clear cache for namespace: ${namespace_collection}`,
377
+ err
378
+ );
379
+ });
380
+ return res.modifiedCount;
381
+ } catch (error) {
382
+ const isDuplicated = error.message.includes("duplicate");
383
+ if (isDuplicated) {
384
+ throw new import_node_server_utils2.BadRequestError("Area already exists.");
385
+ }
386
+ throw error;
387
+ }
388
+ }
389
+ async function updateAreaChecklist(_id, value) {
390
+ try {
391
+ _id = new import_mongodb2.ObjectId(_id);
392
+ } catch (error) {
393
+ throw new import_node_server_utils2.BadRequestError("Invalid area ID format.");
394
+ }
395
+ if (value.checklist && Array.isArray(value.checklist)) {
396
+ value.checklist = value.checklist.map((item) => {
397
+ try {
398
+ return {
399
+ ...item,
400
+ _id: new import_mongodb2.ObjectId(item._id)
401
+ };
402
+ } catch (error) {
403
+ throw new import_node_server_utils2.BadRequestError(
404
+ `Invalid checklist item ID format: ${item._id}`
405
+ );
406
+ }
407
+ });
408
+ }
409
+ try {
410
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
411
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
412
+ if (res.modifiedCount === 0) {
413
+ throw new import_node_server_utils2.InternalServerError("Unable to update cleaning area.");
414
+ }
415
+ delNamespace().then(() => {
416
+ import_node_server_utils2.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
417
+ }).catch((err) => {
418
+ import_node_server_utils2.logger.error(
419
+ `Failed to clear cache for namespace: ${namespace_collection}`,
420
+ err
421
+ );
422
+ });
423
+ return res.modifiedCount;
424
+ } catch (error) {
425
+ throw error;
426
+ }
427
+ }
428
+ async function deleteArea(_id, session) {
429
+ try {
430
+ _id = new import_mongodb2.ObjectId(_id);
431
+ } catch (error) {
432
+ throw new import_node_server_utils2.BadRequestError("Invalid area ID format.");
433
+ }
434
+ try {
435
+ const updateValue = {
436
+ status: "deleted",
437
+ updatedAt: /* @__PURE__ */ new Date(),
438
+ deletedAt: /* @__PURE__ */ new Date()
439
+ };
440
+ const res = await collection.updateOne(
441
+ { _id },
442
+ { $set: updateValue },
443
+ { session }
444
+ );
445
+ if (res.modifiedCount === 0) {
446
+ throw new import_node_server_utils2.InternalServerError("Unable to delete area.");
447
+ }
448
+ delNamespace().then(() => {
449
+ import_node_server_utils2.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
450
+ }).catch((err) => {
451
+ import_node_server_utils2.logger.error(
452
+ `Failed to clear cache for namespace: ${namespace_collection}`,
453
+ err
454
+ );
455
+ });
456
+ return res.modifiedCount;
457
+ } catch (error) {
458
+ throw error;
459
+ }
460
+ }
461
+ return {
462
+ createIndex,
463
+ createTextIndex,
464
+ createUniqueIndex,
465
+ createArea,
466
+ getAreas,
467
+ getAreaById,
468
+ getAreaByName,
469
+ updateArea,
470
+ updateAreaChecklist,
471
+ deleteArea
472
+ };
473
+ }
474
+
475
+ // src/services/hygiene-area.service.ts
476
+ var import_node_server_utils3 = require("@iservice365/node-server-utils");
477
+ function useAreaService() {
478
+ const { createArea: _createArea } = useAreaRepository();
479
+ async function uploadByFile({
480
+ dataJson,
481
+ createdBy,
482
+ site
483
+ }) {
484
+ let dataArray;
485
+ try {
486
+ dataArray = JSON.parse(dataJson);
487
+ } catch (error) {
488
+ throw new import_node_server_utils3.BadRequestError("Invalid JSON format for data in excel");
489
+ }
490
+ if (!dataArray || dataArray.length === 0) {
491
+ throw new import_node_server_utils3.NotFoundError("No data found in the uploaded file");
492
+ }
493
+ const session = import_node_server_utils3.useAtlas.getClient()?.startSession();
494
+ const insertedAreaIds = [];
495
+ try {
496
+ session?.startTransaction();
497
+ for (const row of dataArray) {
498
+ if (!row?.AREA_NAME) {
499
+ import_node_server_utils3.logger.warn("Skipping row with missing AREA_NAME:", row);
500
+ continue;
501
+ }
502
+ try {
503
+ const insertedId = await _createArea(
504
+ {
505
+ name: String(row.AREA_NAME).trim(),
506
+ site,
507
+ createdBy
508
+ },
509
+ session
510
+ );
511
+ insertedAreaIds.push(insertedId);
512
+ } catch (error) {
513
+ import_node_server_utils3.logger.error(
514
+ `Error creating area "${row.AREA_NAME}":`,
515
+ error.message
516
+ );
517
+ continue;
518
+ }
519
+ }
520
+ await session?.commitTransaction();
521
+ import_node_server_utils3.logger.info(`Successfully uploaded ${insertedAreaIds.length} areas`);
522
+ return {
523
+ message: `Successfully uploaded ${insertedAreaIds.length} areas`
524
+ };
525
+ } catch (error) {
526
+ await session?.abortTransaction();
527
+ import_node_server_utils3.logger.error("Error while uploading area information", error);
528
+ throw error;
529
+ } finally {
530
+ session?.endSession();
531
+ }
532
+ }
533
+ return {
534
+ uploadByFile
535
+ };
536
+ }
537
+
538
+ // src/controllers/hygiene-area.controller.ts
539
+ var import_node_server_utils4 = require("@iservice365/node-server-utils");
540
+ var import_joi2 = __toESM(require("joi"));
541
+
542
+ // src/utils/convert-excel.util.ts
543
+ var import_stream = require("stream");
544
+ var xlsx = __toESM(require("xlsx"));
545
+ function convertBufferFile(bufferFile) {
546
+ return new Promise((resolve, reject) => {
547
+ const fileStream = import_stream.Readable.from(bufferFile);
548
+ let fileBuffer = Buffer.alloc(0);
549
+ fileStream.on("data", (chunk) => {
550
+ fileBuffer = Buffer.concat([fileBuffer, chunk]);
551
+ });
552
+ fileStream.on("end", () => {
553
+ try {
554
+ const workbook = xlsx.read(fileBuffer, { type: "buffer" });
555
+ const sheetName = workbook.SheetNames[0];
556
+ const sheet = workbook.Sheets[sheetName];
557
+ const jsonData = xlsx.utils.sheet_to_json(sheet);
558
+ resolve(jsonData);
559
+ } catch (error) {
560
+ reject("Error parsing file");
561
+ }
562
+ });
563
+ fileStream.on("error", (err) => {
564
+ reject("Error Reading File: " + err.message);
565
+ });
566
+ });
567
+ }
568
+
569
+ // src/controllers/hygiene-area.controller.ts
570
+ function useAreaController() {
571
+ const {
572
+ createArea: _createArea,
573
+ getAreas: _getAll,
574
+ getAreaById: _getAreaById,
575
+ updateArea: _updateArea,
576
+ updateAreaChecklist: _updateAreaChecklist,
577
+ deleteArea: _deleteById
578
+ } = useAreaRepository();
579
+ const { uploadByFile: _uploadByFile } = useAreaService();
580
+ async function createArea(req, res, next) {
581
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
582
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
583
+ {}
584
+ ) : {};
585
+ const createdBy = cookies["user"] || "";
586
+ const payload = { ...req.body, createdBy };
587
+ const { error } = areaSchema.validate(payload);
588
+ if (error) {
589
+ import_node_server_utils4.logger.log({ level: "error", message: error.message });
590
+ next(new import_node_server_utils4.BadRequestError(error.message));
591
+ return;
592
+ }
593
+ try {
594
+ const id = await _createArea(payload);
595
+ res.status(201).json({ message: "Area created successfully.", id });
596
+ return;
597
+ } catch (error2) {
598
+ import_node_server_utils4.logger.log({ level: "error", message: error2.message });
599
+ next(error2);
600
+ return;
601
+ }
602
+ }
603
+ async function getAll(req, res, next) {
604
+ const query = req.query;
605
+ const validation = import_joi2.default.object({
606
+ page: import_joi2.default.number().min(1).optional().allow("", null),
607
+ limit: import_joi2.default.number().min(1).optional().allow("", null),
608
+ search: import_joi2.default.string().optional().allow("", null),
609
+ startDate: import_joi2.default.alternatives().try(import_joi2.default.date(), import_joi2.default.string()).optional().allow("", null),
610
+ endDate: import_joi2.default.alternatives().try(import_joi2.default.date(), import_joi2.default.string()).optional().allow("", null),
611
+ site: import_joi2.default.string().hex().optional().allow("", null)
612
+ });
613
+ const { error } = validation.validate(query);
614
+ if (error) {
615
+ next(new import_node_server_utils4.BadRequestError(error.message));
616
+ return;
617
+ }
618
+ const page = parseInt(req.query.page) ?? 1;
619
+ const limit = parseInt(req.query.limit) ?? 20;
620
+ const search = req.query.search ?? "";
621
+ const site = req.query.site ?? "";
622
+ const startDate = req.query.startDate ?? "";
623
+ const endDate = req.query.endDate ?? "";
624
+ try {
625
+ const data = await _getAll({
626
+ page,
627
+ limit,
628
+ search,
629
+ site,
630
+ startDate,
631
+ endDate
632
+ });
633
+ res.json(data);
634
+ return;
635
+ } catch (error2) {
636
+ next(error2);
637
+ return;
638
+ }
639
+ }
640
+ async function getAreaById(req, res, next) {
641
+ const validation = import_joi2.default.string().hex().required();
642
+ const _id = req.params.id;
643
+ const { error } = validation.validate(_id);
644
+ if (error) {
645
+ import_node_server_utils4.logger.log({ level: "error", message: error.message });
646
+ next(new import_node_server_utils4.BadRequestError(error.message));
647
+ return;
648
+ }
649
+ try {
650
+ const data = await _getAreaById(_id);
651
+ res.json(data);
652
+ return;
653
+ } catch (error2) {
654
+ import_node_server_utils4.logger.log({ level: "error", message: error2.message });
655
+ next(error2);
656
+ return;
657
+ }
658
+ }
659
+ async function updateArea(req, res, next) {
660
+ const payload = { id: req.params.id, ...req.body };
661
+ const schema = import_joi2.default.object({
662
+ id: import_joi2.default.string().hex().required(),
663
+ name: import_joi2.default.string().required()
664
+ });
665
+ const { error } = schema.validate(payload);
666
+ if (error) {
667
+ import_node_server_utils4.logger.log({ level: "error", message: error.message });
668
+ next(new import_node_server_utils4.BadRequestError(error.message));
669
+ return;
670
+ }
671
+ try {
672
+ const { id, ...value } = payload;
673
+ await _updateArea(id, value);
674
+ res.json({ message: "Area updated successfully." });
675
+ return;
676
+ } catch (error2) {
677
+ import_node_server_utils4.logger.log({ level: "error", message: error2.message });
678
+ next(error2);
679
+ return;
680
+ }
681
+ }
682
+ async function updateAreaChecklist(req, res, next) {
683
+ const payload = { id: req.params.id, ...req.body };
684
+ const schema = import_joi2.default.object({
685
+ id: import_joi2.default.string().hex().required(),
686
+ checklist: import_joi2.default.array().items(
687
+ import_joi2.default.object({
688
+ _id: import_joi2.default.string().hex().required(),
689
+ name: import_joi2.default.string().required()
690
+ }).required()
691
+ ).min(1).unique("_id", { ignoreUndefined: true }).messages({
692
+ "array.unique": "Duplicate checklist items are not allowed"
693
+ })
694
+ });
695
+ const { error } = schema.validate(payload);
696
+ if (error) {
697
+ import_node_server_utils4.logger.log({ level: "error", message: error.message });
698
+ next(new import_node_server_utils4.BadRequestError(error.message));
699
+ return;
700
+ }
701
+ try {
702
+ const { id, ...value } = payload;
703
+ await _updateAreaChecklist(id, value);
704
+ res.json({ message: "Area updated successfully." });
705
+ return;
706
+ } catch (error2) {
707
+ import_node_server_utils4.logger.log({ level: "error", message: error2.message });
708
+ next(error2);
709
+ return;
710
+ }
711
+ }
712
+ async function deleteArea(req, res, next) {
713
+ const id = req.params.id;
714
+ const validation = import_joi2.default.object({
715
+ id: import_joi2.default.string().hex().required()
716
+ });
717
+ const { error } = validation.validate({ id });
718
+ if (error) {
719
+ import_node_server_utils4.logger.log({ level: "error", message: error.message });
720
+ next(new import_node_server_utils4.BadRequestError(error.message));
721
+ return;
722
+ }
723
+ try {
724
+ await _deleteById(id);
725
+ res.json({ message: "Area deleted successfully." });
726
+ return;
727
+ } catch (error2) {
728
+ import_node_server_utils4.logger.log({ level: "error", message: error2.message });
729
+ next(error2);
730
+ return;
731
+ }
732
+ }
733
+ async function uploadByFile(req, res, next) {
734
+ if (!req.file) {
735
+ next(new import_node_server_utils4.BadRequestError("File is required!"));
736
+ return;
737
+ }
738
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
739
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
740
+ {}
741
+ ) : {};
742
+ const createdBy = cookies["user"] || "";
743
+ const { site } = req.body;
744
+ const schema = import_joi2.default.object({
745
+ site: import_joi2.default.string().hex().optional().allow("", null),
746
+ createdBy: import_joi2.default.string().hex().required()
747
+ });
748
+ const { error } = schema.validate({ site, createdBy });
749
+ if (error) {
750
+ import_node_server_utils4.logger.log({ level: "error", message: error.message });
751
+ next(new import_node_server_utils4.BadRequestError(error.message));
752
+ return;
753
+ }
754
+ try {
755
+ const xlsData = await convertBufferFile(req.file.buffer);
756
+ const dataJson = JSON.stringify(xlsData);
757
+ const result = await _uploadByFile({ dataJson, createdBy, site });
758
+ return res.status(201).json(result);
759
+ } catch (error2) {
760
+ import_node_server_utils4.logger.log({ level: "error", message: error2.message });
761
+ next(error2);
762
+ return;
763
+ }
764
+ }
765
+ return {
766
+ createArea,
767
+ getAll,
768
+ getAreaById,
769
+ updateArea,
770
+ updateAreaChecklist,
771
+ deleteArea,
772
+ uploadByFile
773
+ };
774
+ }
775
+
776
+ // src/models/hygiene-toilet-location.model.ts
777
+ var import_node_server_utils5 = require("@iservice365/node-server-utils");
778
+ var import_joi3 = __toESM(require("joi"));
779
+ var import_mongodb3 = require("mongodb");
780
+ var toiletLocationSchema = import_joi3.default.object({
781
+ name: import_joi3.default.string().required(),
782
+ site: import_joi3.default.string().hex().required(),
783
+ createdBy: import_joi3.default.string().hex().required(),
784
+ checklist: import_joi3.default.array().items(
785
+ import_joi3.default.object({
786
+ _id: import_joi3.default.string().hex().required(),
787
+ name: import_joi3.default.string().required()
788
+ })
789
+ ).optional(),
790
+ updatedAt: import_joi3.default.date().optional().allow("", null),
791
+ status: import_joi3.default.string().allow("", null).optional()
792
+ });
793
+ function MToiletLocation(value) {
794
+ const { error } = toiletLocationSchema.validate(value);
795
+ if (error) {
796
+ throw new import_node_server_utils5.BadRequestError(error.message);
797
+ }
798
+ if (value.site) {
799
+ try {
800
+ value.site = new import_mongodb3.ObjectId(value.site);
801
+ } catch (error2) {
802
+ throw new import_node_server_utils5.BadRequestError("Invalid site ID format.");
803
+ }
804
+ }
805
+ if (value.createdBy) {
806
+ try {
807
+ value.createdBy = new import_mongodb3.ObjectId(value.createdBy);
808
+ } catch (error2) {
809
+ throw new import_node_server_utils5.BadRequestError("Invalid createdBy ID format.");
810
+ }
811
+ }
812
+ if (value.checklist && Array.isArray(value.checklist)) {
813
+ value.checklist = value.checklist.map((item) => {
814
+ try {
815
+ return {
816
+ ...item,
817
+ _id: new import_mongodb3.ObjectId(item._id)
818
+ };
819
+ } catch (error2) {
820
+ throw new import_node_server_utils5.BadRequestError(
821
+ `Invalid checklist item ID format: ${item._id}`
822
+ );
823
+ }
824
+ });
825
+ }
826
+ return {
827
+ name: value.name,
828
+ site: value.site,
829
+ createdBy: value.createdBy,
830
+ checklist: value.checklist,
831
+ status: value.status ?? "",
832
+ createdAt: /* @__PURE__ */ new Date(),
833
+ updatedAt: value.updatedAt ?? "",
834
+ deletedAt: value.deletedAt ?? ""
835
+ };
836
+ }
837
+
838
+ // src/repositories/hygiene-toilet-location.repository.ts
839
+ var import_mongodb4 = require("mongodb");
840
+ var import_node_server_utils6 = require("@iservice365/node-server-utils");
841
+ function useToiletLocationRepository() {
842
+ const db = import_node_server_utils6.useAtlas.getDb();
843
+ if (!db) {
844
+ throw new import_node_server_utils6.InternalServerError("Unable to connect to server.");
845
+ }
846
+ const namespace_collection = "hygiene-toilet-locations";
847
+ const collection = db.collection(namespace_collection);
848
+ const { delNamespace, setCache, getCache, delCache } = (0, import_node_server_utils6.useCache)(namespace_collection);
849
+ async function createIndexes() {
850
+ try {
851
+ await collection.createIndexes([
852
+ { key: { site: 1 } },
853
+ { key: { name: "text" } }
854
+ ]);
855
+ } catch (error) {
856
+ throw new import_node_server_utils6.InternalServerError("Failed to create index on site.");
857
+ }
858
+ }
859
+ async function createUniqueIndex() {
860
+ try {
861
+ await collection.createIndex(
862
+ { name: 1, site: 1, deletedAt: 1 },
863
+ { unique: true }
864
+ );
865
+ } catch (error) {
866
+ throw new import_node_server_utils6.InternalServerError(
867
+ "Failed to create unique index on hygiene area."
868
+ );
869
+ }
870
+ }
871
+ async function create(value, session) {
872
+ try {
873
+ value = MToiletLocation(value);
874
+ const res = await collection.insertOne(value, { session });
875
+ delNamespace().then(() => {
876
+ import_node_server_utils6.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
877
+ }).catch((err) => {
878
+ import_node_server_utils6.logger.error(
879
+ `Failed to clear cache for namespace: ${namespace_collection}`,
880
+ err
881
+ );
882
+ });
883
+ return res.insertedId;
884
+ } catch (error) {
885
+ const isDuplicated = error.message.includes("duplicate");
886
+ if (isDuplicated) {
887
+ throw new import_node_server_utils6.BadRequestError("Toilet location already exists.");
888
+ }
889
+ throw error;
890
+ }
891
+ }
892
+ async function updateToiletLocation(_id, value) {
893
+ try {
894
+ _id = new import_mongodb4.ObjectId(_id);
895
+ } catch (error) {
896
+ throw new import_node_server_utils6.BadRequestError("Invalid area ID format.");
897
+ }
898
+ if (value.checklist && Array.isArray(value.checklist)) {
899
+ value.checklist = value.checklist.map((item) => {
900
+ try {
901
+ return {
902
+ ...item,
903
+ _id: new import_mongodb4.ObjectId(item._id)
904
+ };
905
+ } catch (error) {
906
+ throw new import_node_server_utils6.BadRequestError(
907
+ `Invalid checklist item ID format: ${item._id}`
908
+ );
909
+ }
910
+ });
911
+ }
912
+ try {
913
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
914
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
915
+ if (res.modifiedCount === 0) {
916
+ throw new import_node_server_utils6.InternalServerError("Unable to update toilet location.");
917
+ }
918
+ delNamespace().then(() => {
919
+ import_node_server_utils6.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
920
+ }).catch((err) => {
921
+ import_node_server_utils6.logger.error(
922
+ `Failed to clear cache for namespace: ${namespace_collection}`,
923
+ err
924
+ );
925
+ });
926
+ return res.modifiedCount;
927
+ } catch (error) {
928
+ const isDuplicated = error.message.includes("duplicate");
929
+ if (isDuplicated) {
930
+ throw new import_node_server_utils6.BadRequestError("Toilet location already exists.");
931
+ }
932
+ throw error;
933
+ }
934
+ }
935
+ async function deleteToiletLocation(_id, session) {
936
+ try {
937
+ _id = new import_mongodb4.ObjectId(_id);
938
+ } catch (error) {
939
+ throw new import_node_server_utils6.BadRequestError("Invalid area ID format.");
940
+ }
941
+ try {
942
+ const updateValue = {
943
+ status: "deleted",
944
+ updatedAt: /* @__PURE__ */ new Date(),
945
+ deletedAt: /* @__PURE__ */ new Date()
946
+ };
947
+ const res = await collection.updateOne(
948
+ { _id },
949
+ { $set: updateValue },
950
+ { session }
951
+ );
952
+ if (res.modifiedCount === 0) {
953
+ throw new import_node_server_utils6.InternalServerError("Unable to delete toilet location.");
954
+ }
955
+ delNamespace().then(() => {
956
+ import_node_server_utils6.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
957
+ }).catch((err) => {
958
+ import_node_server_utils6.logger.error(
959
+ `Failed to clear cache for namespace: ${namespace_collection}`,
960
+ err
961
+ );
962
+ });
963
+ return res.modifiedCount;
964
+ } catch (error) {
965
+ throw error;
966
+ }
967
+ }
968
+ async function getToiletLocations({
969
+ page = 1,
970
+ limit = 10,
971
+ search = "",
972
+ sort = {},
973
+ startDate = "",
974
+ endDate = "",
975
+ site = ""
976
+ }) {
977
+ page = page > 0 ? page - 1 : 0;
978
+ let dateFilter = {};
979
+ try {
980
+ site = new import_mongodb4.ObjectId(site);
981
+ } catch (error) {
982
+ throw new import_node_server_utils6.BadRequestError("Invalid site ID format.");
983
+ }
984
+ const query = {
985
+ status: { $ne: "deleted" }
986
+ };
987
+ const cacheOptions = {
988
+ site: site.toString()
989
+ };
990
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
991
+ cacheOptions.sort = JSON.stringify(sort);
992
+ if (search) {
993
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
994
+ cacheOptions.search = search;
995
+ }
996
+ delNamespace().then(() => {
997
+ import_node_server_utils6.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
998
+ }).catch((err) => {
999
+ import_node_server_utils6.logger.error(
1000
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1001
+ err
1002
+ );
1003
+ });
1004
+ const cacheKey = (0, import_node_server_utils6.makeCacheKey)(namespace_collection, cacheOptions);
1005
+ const cachedData = await getCache(cacheKey);
1006
+ if (cachedData) {
1007
+ import_node_server_utils6.logger.info(`Cache hit for key: ${cacheKey}`);
1008
+ return cachedData;
1009
+ }
1010
+ if (startDate && endDate) {
1011
+ dateFilter = {
1012
+ createdAt: {
1013
+ $gte: new Date(startDate),
1014
+ $lte: new Date(endDate)
1015
+ }
1016
+ };
1017
+ } else if (startDate) {
1018
+ dateFilter = { createdAt: { $gte: new Date(startDate) } };
1019
+ } else if (endDate) {
1020
+ dateFilter = { createdAt: { $lte: new Date(endDate) } };
1021
+ }
1022
+ try {
1023
+ const items = await collection.aggregate([
1024
+ {
1025
+ $match: {
1026
+ ...dateFilter,
1027
+ ...query
1028
+ }
1029
+ },
1030
+ {
1031
+ $lookup: {
1032
+ from: "sites",
1033
+ localField: "site",
1034
+ foreignField: "_id",
1035
+ pipeline: [{ $project: { name: 1 } }],
1036
+ as: "site"
1037
+ }
1038
+ },
1039
+ {
1040
+ $unwind: {
1041
+ path: "$site",
1042
+ preserveNullAndEmptyArrays: true
1043
+ }
1044
+ },
1045
+ {
1046
+ $lookup: {
1047
+ from: "users",
1048
+ localField: "createdBy",
1049
+ foreignField: "_id",
1050
+ pipeline: [{ $project: { name: 1 } }],
1051
+ as: "createdBy"
1052
+ }
1053
+ },
1054
+ {
1055
+ $unwind: {
1056
+ path: "$createdBy",
1057
+ preserveNullAndEmptyArrays: true
1058
+ }
1059
+ },
1060
+ {
1061
+ $project: {
1062
+ name: 1,
1063
+ site: "$site._id",
1064
+ siteName: "$site.name",
1065
+ createdByName: "$createdBy.name",
1066
+ checklist: 1,
1067
+ status: 1,
1068
+ createdAt: 1
1069
+ }
1070
+ },
1071
+ { $sort: { _id: -1 } },
1072
+ { $skip: page * limit },
1073
+ { $limit: limit }
1074
+ ]).toArray();
1075
+ const length = await collection.countDocuments(query);
1076
+ const data = (0, import_node_server_utils6.paginate)(items, page, limit, length);
1077
+ setCache(cacheKey, data, 15 * 60).then(() => {
1078
+ import_node_server_utils6.logger.info(`Cache set for key: ${cacheKey}`);
1079
+ }).catch((err) => {
1080
+ import_node_server_utils6.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
1081
+ });
1082
+ return data;
1083
+ } catch (error) {
1084
+ throw error;
1085
+ }
1086
+ }
1087
+ async function getToiletLocationByName(name, site) {
1088
+ try {
1089
+ if (site)
1090
+ site = new import_mongodb4.ObjectId(site);
1091
+ } catch (error) {
1092
+ throw new import_node_server_utils6.BadRequestError("Invalid site ID format.");
1093
+ }
1094
+ try {
1095
+ return await collection.findOne({
1096
+ name: { $regex: new RegExp(`^${name}$`, "i") },
1097
+ ...site && { site }
1098
+ });
1099
+ } catch (error) {
1100
+ throw new import_node_server_utils6.BadRequestError("Unable to fetch toilet location by name.");
1101
+ }
1102
+ }
1103
+ async function updateToiletLocationChecklist(_id, value) {
1104
+ try {
1105
+ _id = new import_mongodb4.ObjectId(_id);
1106
+ } catch (error) {
1107
+ throw new import_node_server_utils6.BadRequestError("Invalid area ID format.");
1108
+ }
1109
+ if (value.checklist && Array.isArray(value.checklist)) {
1110
+ value.checklist = value.checklist.map((item) => {
1111
+ try {
1112
+ return {
1113
+ ...item,
1114
+ _id: new import_mongodb4.ObjectId(item._id)
1115
+ };
1116
+ } catch (error) {
1117
+ throw new import_node_server_utils6.BadRequestError(
1118
+ `Invalid checklist item ID format: ${item._id}`
1119
+ );
1120
+ }
1121
+ });
1122
+ }
1123
+ try {
1124
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
1125
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
1126
+ if (res.modifiedCount === 0) {
1127
+ throw new import_node_server_utils6.InternalServerError("Unable to update toilet location.");
1128
+ }
1129
+ delNamespace().then(() => {
1130
+ import_node_server_utils6.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
1131
+ }).catch((err) => {
1132
+ import_node_server_utils6.logger.error(
1133
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1134
+ err
1135
+ );
1136
+ });
1137
+ return res.modifiedCount;
1138
+ } catch (error) {
1139
+ throw error;
1140
+ }
1141
+ }
1142
+ async function getToiletLocationById(_id) {
1143
+ try {
1144
+ _id = new import_mongodb4.ObjectId(_id);
1145
+ } catch (error) {
1146
+ throw new import_node_server_utils6.BadRequestError("Invalid area ID format.");
1147
+ }
1148
+ const query = {
1149
+ _id,
1150
+ status: { $ne: "deleted" }
1151
+ };
1152
+ const cacheKey = (0, import_node_server_utils6.makeCacheKey)(namespace_collection, {
1153
+ _id: _id.toString()
1154
+ });
1155
+ const cachedData = await getCache(cacheKey);
1156
+ if (cachedData) {
1157
+ import_node_server_utils6.logger.info(`Cache hit for key: ${cacheKey}`);
1158
+ return cachedData;
1159
+ }
1160
+ try {
1161
+ const data = await collection.aggregate([
1162
+ { $match: query },
1163
+ {
1164
+ $project: {
1165
+ name: 1,
1166
+ checklist: 1
1167
+ }
1168
+ }
1169
+ ]).toArray();
1170
+ if (!data || !data.length) {
1171
+ throw new import_node_server_utils6.NotFoundError("Area not found.");
1172
+ }
1173
+ setCache(cacheKey, data[0], 15 * 60).then(() => {
1174
+ import_node_server_utils6.logger.info(`Cache set for key: ${cacheKey}`);
1175
+ }).catch((err) => {
1176
+ import_node_server_utils6.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
1177
+ });
1178
+ return data[0];
1179
+ } catch (error) {
1180
+ throw error;
1181
+ }
1182
+ }
1183
+ return {
1184
+ createIndexes,
1185
+ createUniqueIndex,
1186
+ getToiletLocations,
1187
+ create,
1188
+ updateToiletLocation,
1189
+ deleteToiletLocation,
1190
+ getToiletLocationByName,
1191
+ getToiletLocationById,
1192
+ updateToiletLocationChecklist
1193
+ };
1194
+ }
1195
+
1196
+ // src/services/hygiene-toilet-location.service.ts
1197
+ var import_node_server_utils7 = require("@iservice365/node-server-utils");
1198
+ function useToiletLocationService() {
1199
+ const { create: _createToilet } = useToiletLocationRepository();
1200
+ async function uploadByFile({
1201
+ dataJson,
1202
+ createdBy,
1203
+ site
1204
+ }) {
1205
+ let dataArray;
1206
+ try {
1207
+ dataArray = JSON.parse(dataJson);
1208
+ } catch (error) {
1209
+ throw new import_node_server_utils7.BadRequestError("Invalid JSON format for data in excel");
1210
+ }
1211
+ if (!dataArray || dataArray.length === 0) {
1212
+ throw new import_node_server_utils7.NotFoundError("No data found in the uploaded file");
1213
+ }
1214
+ const session = import_node_server_utils7.useAtlas.getClient()?.startSession();
1215
+ const insertedAreaIds = [];
1216
+ try {
1217
+ session?.startTransaction();
1218
+ for (const row of dataArray) {
1219
+ if (!row?.AREA_NAME) {
1220
+ import_node_server_utils7.logger.warn("Skipping row with missing TOILET NAME:", row);
1221
+ continue;
1222
+ }
1223
+ try {
1224
+ const insertedId = await _createToilet(
1225
+ {
1226
+ name: String(row.TOILET_NAME).trim(),
1227
+ site,
1228
+ createdBy
1229
+ },
1230
+ session
1231
+ );
1232
+ insertedAreaIds.push(insertedId);
1233
+ } catch (error) {
1234
+ import_node_server_utils7.logger.error(
1235
+ `Error creating area "${row.AREA_NAME}":`,
1236
+ error.message
1237
+ );
1238
+ continue;
1239
+ }
1240
+ }
1241
+ await session?.commitTransaction();
1242
+ import_node_server_utils7.logger.info(`Successfully uploaded ${insertedAreaIds.length} areas`);
1243
+ return {
1244
+ message: `Successfully uploaded ${insertedAreaIds.length} areas`
1245
+ };
1246
+ } catch (error) {
1247
+ await session?.abortTransaction();
1248
+ import_node_server_utils7.logger.error("Error while uploading area information", error);
1249
+ throw error;
1250
+ } finally {
1251
+ session?.endSession();
1252
+ }
1253
+ }
1254
+ return {
1255
+ uploadByFile
1256
+ };
1257
+ }
1258
+
1259
+ // src/controllers/hygiene-toilet-location.controller.ts
1260
+ var import_joi4 = __toESM(require("joi"));
1261
+ var import_node_server_utils8 = require("@iservice365/node-server-utils");
1262
+ function useToiletLocationController() {
1263
+ const { uploadByFile: _uploadByFile } = useToiletLocationService();
1264
+ const {
1265
+ create: _createToiletLocation,
1266
+ updateToiletLocation: _updateToiletLocation,
1267
+ updateToiletLocationChecklist: _updateToiletLocationChecklist,
1268
+ getToiletLocationById: _getToiletLocationById,
1269
+ getToiletLocations: _getAll,
1270
+ deleteToiletLocation: _deleteById
1271
+ } = useToiletLocationRepository();
1272
+ async function getAll(req, res, next) {
1273
+ const query = req.query;
1274
+ const validation = import_joi4.default.object({
1275
+ page: import_joi4.default.number().min(1).optional().allow("", null),
1276
+ limit: import_joi4.default.number().min(1).optional().allow("", null),
1277
+ search: import_joi4.default.string().optional().allow("", null),
1278
+ site: import_joi4.default.string().hex().optional().allow("", null)
1279
+ });
1280
+ const { error } = validation.validate(query);
1281
+ if (error) {
1282
+ next(new import_node_server_utils8.BadRequestError(error.message));
1283
+ return;
1284
+ }
1285
+ const page = parseInt(req.query.page) ?? 1;
1286
+ let limit = parseInt(req.query.limit) ?? 20;
1287
+ limit = isNaN(limit) ? 20 : limit;
1288
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
1289
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
1290
+ const sortObj = {};
1291
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
1292
+ sort.forEach((field, index) => {
1293
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
1294
+ });
1295
+ }
1296
+ const site = req.query.site ?? "";
1297
+ const search = req.query.search ?? "";
1298
+ try {
1299
+ const buildings = await _getAll({
1300
+ page,
1301
+ limit,
1302
+ sort: sortObj,
1303
+ site,
1304
+ search
1305
+ });
1306
+ res.json(buildings);
1307
+ return;
1308
+ } catch (error2) {
1309
+ next(error2);
1310
+ }
1311
+ }
1312
+ async function createToiletLocation(req, res, next) {
1313
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
1314
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
1315
+ {}
1316
+ ) : {};
1317
+ const createdBy = cookies["user"] || "";
1318
+ const payload = { ...req.body, createdBy };
1319
+ const schema = import_joi4.default.object({
1320
+ name: import_joi4.default.string().required(),
1321
+ site: import_joi4.default.string().hex().optional().allow("", null),
1322
+ status: import_joi4.default.string().optional().allow("", null),
1323
+ createdBy: import_joi4.default.string().hex().optional().allow("", null)
1324
+ });
1325
+ const { error } = schema.validate(payload);
1326
+ if (error) {
1327
+ next(new import_node_server_utils8.BadRequestError(error.message));
1328
+ import_node_server_utils8.logger.info(`Controller: ${error.message}`);
1329
+ return;
1330
+ }
1331
+ try {
1332
+ const result = await _createToiletLocation(payload);
1333
+ res.status(201).json(result);
1334
+ return;
1335
+ } catch (error2) {
1336
+ next(error2);
1337
+ }
1338
+ }
1339
+ async function updateToiletLocation(req, res, next) {
1340
+ const payload = { id: req.params.id, ...req.body };
1341
+ const schema = import_joi4.default.object({
1342
+ id: import_joi4.default.string().hex().required(),
1343
+ name: import_joi4.default.string().required(),
1344
+ checklist: import_joi4.default.array().items(
1345
+ import_joi4.default.object({
1346
+ _id: import_joi4.default.string().hex().required(),
1347
+ name: import_joi4.default.string().required()
1348
+ }).optional()
1349
+ ),
1350
+ site: import_joi4.default.string().hex().optional().allow("", null),
1351
+ status: import_joi4.default.string().optional().allow("", null)
1352
+ });
1353
+ const { error } = schema.validate(payload);
1354
+ if (error) {
1355
+ import_node_server_utils8.logger.log({ level: "error", message: error.message });
1356
+ next(new import_node_server_utils8.BadRequestError(error.message));
1357
+ return;
1358
+ }
1359
+ try {
1360
+ const { id, ...value } = payload;
1361
+ await _updateToiletLocation(id, value);
1362
+ res.status(201).json({ message: "Toilet location updated successfully." });
1363
+ return;
1364
+ } catch (error2) {
1365
+ import_node_server_utils8.logger.log({ level: "error", message: error2.message });
1366
+ next(error2);
1367
+ }
1368
+ }
1369
+ async function deleteToiletLocation(req, res, next) {
1370
+ const id = req.params.id;
1371
+ const validation = import_joi4.default.object({
1372
+ id: import_joi4.default.string().hex().required()
1373
+ });
1374
+ const { error } = validation.validate({ id });
1375
+ if (error) {
1376
+ next(new import_node_server_utils8.BadRequestError(error.message));
1377
+ return;
1378
+ }
1379
+ try {
1380
+ const message = await _deleteById(id);
1381
+ res.json(message);
1382
+ return;
1383
+ } catch (error2) {
1384
+ next(error2);
1385
+ }
1386
+ }
1387
+ async function updateToiletLocationChecklist(req, res, next) {
1388
+ const payload = { id: req.params.id, ...req.body };
1389
+ const schema = import_joi4.default.object({
1390
+ id: import_joi4.default.string().hex().required(),
1391
+ checklist: import_joi4.default.array().items(
1392
+ import_joi4.default.object({
1393
+ _id: import_joi4.default.string().hex().required(),
1394
+ name: import_joi4.default.string().required()
1395
+ }).required()
1396
+ ).min(1).unique("_id", { ignoreUndefined: true }).messages({
1397
+ "array.unique": "Duplicate checklist items are not allowed"
1398
+ })
1399
+ });
1400
+ const { error } = schema.validate(payload);
1401
+ if (error) {
1402
+ import_node_server_utils8.logger.log({ level: "error", message: error.message });
1403
+ next(new import_node_server_utils8.BadRequestError(error.message));
1404
+ return;
1405
+ }
1406
+ try {
1407
+ const { id, ...value } = payload;
1408
+ await _updateToiletLocationChecklist(id, value);
1409
+ res.json({ message: "Toilet location updated successfully." });
1410
+ return;
1411
+ } catch (error2) {
1412
+ import_node_server_utils8.logger.log({ level: "error", message: error2.message });
1413
+ next(error2);
1414
+ return;
1415
+ }
1416
+ }
1417
+ async function getToiletLocationById(req, res, next) {
1418
+ const validation = import_joi4.default.string().hex().required();
1419
+ const _id = req.params.id;
1420
+ const { error } = validation.validate(_id);
1421
+ if (error) {
1422
+ import_node_server_utils8.logger.log({ level: "error", message: error.message });
1423
+ next(new import_node_server_utils8.BadRequestError(error.message));
1424
+ return;
1425
+ }
1426
+ try {
1427
+ const data = await _getToiletLocationById(_id);
1428
+ res.json(data);
1429
+ return;
1430
+ } catch (error2) {
1431
+ import_node_server_utils8.logger.log({ level: "error", message: error2.message });
1432
+ next(error2);
1433
+ return;
1434
+ }
1435
+ }
1436
+ async function uploadByFile(req, res, next) {
1437
+ if (!req.file) {
1438
+ next(new import_node_server_utils8.BadRequestError("File is required!"));
1439
+ return;
1440
+ }
1441
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
1442
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
1443
+ {}
1444
+ ) : {};
1445
+ const createdBy = cookies["user"] || "";
1446
+ const { site } = req.body;
1447
+ const schema = import_joi4.default.object({
1448
+ site: import_joi4.default.string().hex().optional().allow("", null),
1449
+ createdBy: import_joi4.default.string().hex().required()
1450
+ });
1451
+ const { error } = schema.validate({ site, createdBy });
1452
+ if (error) {
1453
+ import_node_server_utils8.logger.log({ level: "error", message: error.message });
1454
+ next(new import_node_server_utils8.BadRequestError(error.message));
1455
+ return;
1456
+ }
1457
+ try {
1458
+ const xlsData = await convertBufferFile(req.file.buffer);
1459
+ const dataJson = JSON.stringify(xlsData);
1460
+ const result = await _uploadByFile({ dataJson, createdBy, site });
1461
+ return res.status(201).json(result);
1462
+ } catch (error2) {
1463
+ import_node_server_utils8.logger.log({ level: "error", message: error2.message });
1464
+ next(error2);
1465
+ return;
1466
+ }
1467
+ }
1468
+ return {
1469
+ getAll,
1470
+ createToiletLocation,
1471
+ updateToiletLocation,
1472
+ deleteToiletLocation,
1473
+ updateToiletLocationChecklist,
1474
+ getToiletLocationById,
1475
+ uploadByFile
1476
+ };
1477
+ }
1478
+
1479
+ // src/models/hygiene-parent-checklist.model.ts
1480
+ var import_node_server_utils9 = require("@iservice365/node-server-utils");
1481
+ var import_joi5 = __toESM(require("joi"));
1482
+ var import_mongodb5 = require("mongodb");
1483
+ var allowedTypes = ["cleaner", "toilet"];
1484
+ var allowedStatus = [
1485
+ "To Do",
1486
+ "Pending",
1487
+ "In Progress",
1488
+ "Completed",
1489
+ "Expired"
1490
+ ];
1491
+ var parentChecklistSchema = import_joi5.default.object({
1492
+ date: import_joi5.default.date().required(),
1493
+ status: import_joi5.default.array().items(
1494
+ import_joi5.default.object({
1495
+ type: import_joi5.default.string().required().valid(...allowedTypes),
1496
+ site: import_joi5.default.string().hex().required()
1497
+ })
1498
+ ).optional()
1499
+ });
1500
+ function MParentChecklist(value) {
1501
+ const { error } = parentChecklistSchema.validate(value);
1502
+ if (error) {
1503
+ import_node_server_utils9.logger.info(`Hygiene Parent Checklist Model: ${error.message}`);
1504
+ throw new import_node_server_utils9.BadRequestError(error.message);
1505
+ }
1506
+ if (value.status && Array.isArray(value.status)) {
1507
+ value.status = value.status.map((item) => {
1508
+ try {
1509
+ return {
1510
+ ...item,
1511
+ site: new import_mongodb5.ObjectId(item.site),
1512
+ status: item.status || "To Do",
1513
+ completedAt: item.completedAt || ""
1514
+ };
1515
+ } catch (error2) {
1516
+ throw new import_node_server_utils9.BadRequestError(
1517
+ `Invalid status site ID format: ${item.site}`
1518
+ );
1519
+ }
1520
+ });
1521
+ }
1522
+ return {
1523
+ date: new Date(value.date),
1524
+ status: value.status,
1525
+ createdAt: /* @__PURE__ */ new Date(),
1526
+ updatedAt: value.updatedAt ?? ""
1527
+ };
1528
+ }
1529
+
1530
+ // src/repositories/hygiene-parent-checklist.repository.ts
1531
+ var import_mongodb6 = require("mongodb");
1532
+ var import_node_server_utils10 = require("@iservice365/node-server-utils");
1533
+ function useParentChecklistRepo() {
1534
+ const db = import_node_server_utils10.useAtlas.getDb();
1535
+ if (!db) {
1536
+ throw new import_node_server_utils10.InternalServerError("Unable to connect to server.");
1537
+ }
1538
+ const namespace_collection = "hygiene-parent-checklist";
1539
+ const collection = db.collection(namespace_collection);
1540
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils10.useCache)(namespace_collection);
1541
+ async function createIndex() {
1542
+ try {
1543
+ await collection.createIndexes([
1544
+ { key: { date: 1 } },
1545
+ { key: { "status.type": 1, "status.site": 1 } }
1546
+ ]);
1547
+ } catch (error) {
1548
+ throw new import_node_server_utils10.InternalServerError(
1549
+ "Failed to create index on hygiene parent checklist."
1550
+ );
1551
+ }
1552
+ }
1553
+ async function createParentChecklist(value, session) {
1554
+ try {
1555
+ const currentDate = value.date ? new Date(value.date) : /* @__PURE__ */ new Date();
1556
+ const startOfDay = new Date(currentDate);
1557
+ startOfDay.setUTCHours(0, 0, 0, 0);
1558
+ const endOfDay = new Date(currentDate);
1559
+ endOfDay.setUTCHours(23, 59, 59, 999);
1560
+ const existingChecklist = await collection.findOne({
1561
+ date: {
1562
+ $gte: startOfDay,
1563
+ $lte: endOfDay
1564
+ }
1565
+ });
1566
+ if (existingChecklist) {
1567
+ const dateStr2 = currentDate.toISOString().split("T")[0];
1568
+ import_node_server_utils10.logger.info(`Parent checklist already exists for today: ${dateStr2}`);
1569
+ return existingChecklist._id;
1570
+ }
1571
+ const processedValue = MParentChecklist(value);
1572
+ const result = await collection.insertOne(processedValue, { session });
1573
+ delNamespace().then(() => {
1574
+ import_node_server_utils10.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
1575
+ }).catch((err) => {
1576
+ import_node_server_utils10.logger.error(
1577
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1578
+ err
1579
+ );
1580
+ });
1581
+ const dateStr = currentDate.toISOString().split("T")[0];
1582
+ import_node_server_utils10.logger.info(
1583
+ `Created new parent checklist ${result.insertedId} for today: ${dateStr}`
1584
+ );
1585
+ return result.insertedId;
1586
+ } catch (error) {
1587
+ import_node_server_utils10.logger.error("Failed to create daily parent checklist", error);
1588
+ throw error;
1589
+ }
1590
+ }
1591
+ async function getAllParentChecklist({
1592
+ page = 1,
1593
+ limit = 10,
1594
+ search = "",
1595
+ site,
1596
+ type,
1597
+ startDate = "",
1598
+ endDate = ""
1599
+ }) {
1600
+ page = page > 0 ? page - 1 : 0;
1601
+ const query = {};
1602
+ const cacheOptions = {
1603
+ page,
1604
+ limit
1605
+ };
1606
+ try {
1607
+ site = new import_mongodb6.ObjectId(site);
1608
+ cacheOptions.site = site.toString();
1609
+ } catch (error) {
1610
+ throw new import_node_server_utils10.BadRequestError("Invalid site ID format.");
1611
+ }
1612
+ cacheOptions.type = type;
1613
+ query.status = {
1614
+ $elemMatch: {
1615
+ site: new import_mongodb6.ObjectId(site),
1616
+ type
1617
+ }
1618
+ };
1619
+ if (search) {
1620
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
1621
+ cacheOptions.search = search;
1622
+ }
1623
+ if (startDate && endDate) {
1624
+ query.createdAt = {
1625
+ $gte: new Date(startDate),
1626
+ $lte: new Date(endDate)
1627
+ };
1628
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
1629
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
1630
+ } else if (startDate) {
1631
+ query.createdAt = { $gte: new Date(startDate) };
1632
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
1633
+ } else if (endDate) {
1634
+ query.createdAt = { $lte: new Date(endDate) };
1635
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
1636
+ }
1637
+ const cacheKey = (0, import_node_server_utils10.makeCacheKey)(namespace_collection, cacheOptions);
1638
+ const cachedData = await getCache(cacheKey);
1639
+ if (cachedData) {
1640
+ import_node_server_utils10.logger.info(`Cache hit for key: ${cacheKey}`);
1641
+ return cachedData;
1642
+ }
1643
+ try {
1644
+ const pipeline = [{ $match: query }];
1645
+ const filterConditions = [];
1646
+ filterConditions.push({ $eq: ["$$this.site", new import_mongodb6.ObjectId(site)] });
1647
+ filterConditions.push({ $eq: ["$$this.type", type] });
1648
+ pipeline.push({
1649
+ $addFields: {
1650
+ filteredStatus: {
1651
+ $filter: {
1652
+ input: "$status",
1653
+ cond: { $and: filterConditions }
1654
+ }
1655
+ }
1656
+ }
1657
+ });
1658
+ pipeline.push({
1659
+ $match: {
1660
+ filteredStatus: { $ne: [] }
1661
+ }
1662
+ });
1663
+ pipeline.push({
1664
+ $addFields: {
1665
+ statusObj: { $arrayElemAt: ["$filteredStatus", 0] }
1666
+ }
1667
+ });
1668
+ pipeline.push({
1669
+ $project: {
1670
+ _id: 1,
1671
+ date: 1,
1672
+ status: "$statusObj.status",
1673
+ completedAt: "$statusObj.completedAt",
1674
+ createdAt: 1
1675
+ }
1676
+ });
1677
+ pipeline.push(
1678
+ { $sort: { _id: -1 } },
1679
+ { $skip: page * limit },
1680
+ { $limit: limit }
1681
+ );
1682
+ const items = await collection.aggregate(pipeline).toArray();
1683
+ const length = await collection.countDocuments(query);
1684
+ const data = (0, import_node_server_utils10.paginate)(items, page, limit, length);
1685
+ setCache(cacheKey, data, 15 * 60).then(() => {
1686
+ import_node_server_utils10.logger.info(`Cache set for key: ${cacheKey}`);
1687
+ }).catch((err) => {
1688
+ import_node_server_utils10.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
1689
+ });
1690
+ return data;
1691
+ } catch (error) {
1692
+ throw error;
1693
+ }
1694
+ }
1695
+ async function updateParentChecklistStatuses(date) {
1696
+ try {
1697
+ const currentDate = /* @__PURE__ */ new Date();
1698
+ const dateToUpdate = date || new Date(currentDate.getTime() - 24 * 60 * 60 * 1e3);
1699
+ const startOfDay = new Date(dateToUpdate);
1700
+ startOfDay.setUTCHours(0, 0, 0, 0);
1701
+ const endOfDay = new Date(dateToUpdate);
1702
+ endOfDay.setUTCHours(23, 59, 59, 999);
1703
+ import_node_server_utils10.logger.info(
1704
+ `Updating parent checklist statuses for date: ${dateToUpdate.toISOString().split("T")[0]}`
1705
+ );
1706
+ const statusUpdates = await collection.aggregate([
1707
+ {
1708
+ $match: {
1709
+ createdAt: {
1710
+ $gte: startOfDay,
1711
+ $lte: endOfDay
1712
+ }
1713
+ }
1714
+ },
1715
+ {
1716
+ $lookup: {
1717
+ from: "hygiene-checklist.areas",
1718
+ localField: "_id",
1719
+ foreignField: "parentChecklist",
1720
+ pipeline: [
1721
+ {
1722
+ $group: {
1723
+ _id: {
1724
+ site: "$site",
1725
+ type: "$type"
1726
+ },
1727
+ completedCount: {
1728
+ $sum: {
1729
+ $cond: [{ $eq: ["$status", "Completed"] }, 1, 0]
1730
+ }
1731
+ },
1732
+ inProgressCount: {
1733
+ $sum: {
1734
+ $cond: [{ $eq: ["$status", "In Progress"] }, 1, 0]
1735
+ }
1736
+ },
1737
+ toDoCount: {
1738
+ $sum: {
1739
+ $cond: [
1740
+ {
1741
+ $or: [
1742
+ { $eq: ["$status", "To Do"] },
1743
+ { $eq: ["$status", "Pending"] }
1744
+ ]
1745
+ },
1746
+ 1,
1747
+ 0
1748
+ ]
1749
+ }
1750
+ },
1751
+ totalCount: { $sum: 1 }
1752
+ }
1753
+ },
1754
+ {
1755
+ $addFields: {
1756
+ finalStatus: {
1757
+ $cond: {
1758
+ if: {
1759
+ $and: [
1760
+ { $gt: ["$completedCount", 0] },
1761
+ { $eq: ["$inProgressCount", 0] },
1762
+ { $eq: ["$toDoCount", 0] }
1763
+ ]
1764
+ },
1765
+ then: "Completed",
1766
+ else: {
1767
+ $cond: {
1768
+ if: {
1769
+ $and: [
1770
+ { $eq: ["$completedCount", 0] },
1771
+ { $eq: ["$inProgressCount", 0] }
1772
+ ]
1773
+ },
1774
+ then: "Expired",
1775
+ else: "In Progress"
1776
+ }
1777
+ }
1778
+ }
1779
+ },
1780
+ completedAt: {
1781
+ $cond: {
1782
+ if: {
1783
+ $and: [
1784
+ { $gt: ["$completedCount", 0] },
1785
+ { $eq: ["$inProgressCount", 0] },
1786
+ { $eq: ["$toDoCount", 0] }
1787
+ ]
1788
+ },
1789
+ then: /* @__PURE__ */ new Date(),
1790
+ else: null
1791
+ }
1792
+ }
1793
+ }
1794
+ }
1795
+ ],
1796
+ as: "areaStats"
1797
+ }
1798
+ },
1799
+ {
1800
+ $addFields: {
1801
+ newStatus: {
1802
+ $map: {
1803
+ input: "$areaStats",
1804
+ as: "stat",
1805
+ in: {
1806
+ site: "$$stat._id.site",
1807
+ type: "$$stat._id.type",
1808
+ status: "$$stat.finalStatus",
1809
+ completedAt: "$$stat.completedAt"
1810
+ }
1811
+ }
1812
+ }
1813
+ }
1814
+ },
1815
+ { $match: { newStatus: { $ne: [] } } },
1816
+ {
1817
+ $project: {
1818
+ _id: 1,
1819
+ newStatus: 1
1820
+ }
1821
+ }
1822
+ ]).toArray();
1823
+ import_node_server_utils10.logger.info(
1824
+ `Found ${statusUpdates.length} parent checklists to potentially update`
1825
+ );
1826
+ if (statusUpdates.length === 0) {
1827
+ import_node_server_utils10.logger.info(
1828
+ `No parent checklists found for date range: ${startOfDay.toISOString()} to ${endOfDay.toISOString()}`
1829
+ );
1830
+ return null;
1831
+ }
1832
+ const bulkOps = statusUpdates.map((update) => {
1833
+ const statusTypes = update.newStatus.map((s) => `${s.type}(${s.status})`).join(", ");
1834
+ import_node_server_utils10.logger.info(
1835
+ `Updating parent checklist ${update._id} with ${update.newStatus.length} status entries: [${statusTypes}]`
1836
+ );
1837
+ return {
1838
+ updateOne: {
1839
+ filter: { _id: update._id },
1840
+ update: {
1841
+ $set: {
1842
+ status: update.newStatus,
1843
+ updatedAt: /* @__PURE__ */ new Date()
1844
+ }
1845
+ }
1846
+ }
1847
+ };
1848
+ });
1849
+ let result = null;
1850
+ if (bulkOps.length > 0) {
1851
+ result = await collection.bulkWrite(bulkOps);
1852
+ delNamespace().then(() => {
1853
+ import_node_server_utils10.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
1854
+ }).catch((err) => {
1855
+ import_node_server_utils10.logger.error(
1856
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1857
+ err
1858
+ );
1859
+ });
1860
+ }
1861
+ import_node_server_utils10.logger.info(`Updated statuses for ${bulkOps.length} parent checklists.`);
1862
+ return result;
1863
+ } catch (error) {
1864
+ import_node_server_utils10.logger.error("Failed to update parent checklist statuses", error);
1865
+ throw error;
1866
+ }
1867
+ }
1868
+ return {
1869
+ createIndex,
1870
+ createParentChecklist,
1871
+ getAllParentChecklist,
1872
+ updateParentChecklistStatuses
1873
+ };
1874
+ }
1875
+
1876
+ // src/controllers/hygiene-parent-checklist.controller.ts
1877
+ var import_node_server_utils11 = require("@iservice365/node-server-utils");
1878
+ var import_joi6 = __toESM(require("joi"));
1879
+ function useParentChecklistController() {
1880
+ const {
1881
+ createParentChecklist: _createParentChecklist,
1882
+ getAllParentChecklist: _getAllParentChecklist
1883
+ } = useParentChecklistRepo();
1884
+ async function createParentChecklist(req, res, next) {
1885
+ const payload = req.body;
1886
+ const { error } = parentChecklistSchema.validate(payload);
1887
+ if (error) {
1888
+ import_node_server_utils11.logger.log({ level: "error", message: error.message });
1889
+ next(new import_node_server_utils11.BadRequestError(error.message));
1890
+ return;
1891
+ }
1892
+ try {
1893
+ const id = await _createParentChecklist(payload);
1894
+ res.status(201).json({ message: "Parent checklist created successfully.", id });
1895
+ return;
1896
+ } catch (error2) {
1897
+ import_node_server_utils11.logger.log({ level: "error", message: error2.message });
1898
+ next(error2);
1899
+ return;
1900
+ }
1901
+ }
1902
+ async function getAllParentChecklist(req, res, next) {
1903
+ const query = { ...req.query, ...req.params };
1904
+ const validation = import_joi6.default.object({
1905
+ page: import_joi6.default.number().min(1).optional().allow("", null),
1906
+ limit: import_joi6.default.number().min(1).optional().allow("", null),
1907
+ search: import_joi6.default.string().optional().allow("", null),
1908
+ site: import_joi6.default.string().hex().required(),
1909
+ type: import_joi6.default.string().required().valid(...allowedTypes),
1910
+ startDate: import_joi6.default.alternatives().try(import_joi6.default.date(), import_joi6.default.string()).optional().allow("", null),
1911
+ endDate: import_joi6.default.alternatives().try(import_joi6.default.date(), import_joi6.default.string()).optional().allow("", null)
1912
+ });
1913
+ const { error } = validation.validate(query);
1914
+ if (error) {
1915
+ import_node_server_utils11.logger.log({ level: "error", message: error.message });
1916
+ next(new import_node_server_utils11.BadRequestError(error.message));
1917
+ return;
1918
+ }
1919
+ const page = parseInt(req.query.page) ?? 1;
1920
+ const limit = parseInt(req.query.limit) ?? 20;
1921
+ const search = req.query.search ?? "";
1922
+ const site = req.params.site ?? "";
1923
+ const type = req.params.type ?? "";
1924
+ const startDate = req.query.startDate ?? "";
1925
+ const endDate = req.query.endDate ?? "";
1926
+ try {
1927
+ const data = await _getAllParentChecklist({
1928
+ page,
1929
+ limit,
1930
+ search,
1931
+ site,
1932
+ type,
1933
+ startDate,
1934
+ endDate
1935
+ });
1936
+ res.json(data);
1937
+ return;
1938
+ } catch (error2) {
1939
+ import_node_server_utils11.logger.log({ level: "error", message: error2.message });
1940
+ next(error2);
1941
+ return;
1942
+ }
1943
+ }
1944
+ return {
1945
+ createParentChecklist,
1946
+ getAllParentChecklist
1947
+ };
1948
+ }
1949
+
1950
+ // src/models/hygiene-unit.model.ts
1951
+ var import_node_server_utils12 = require("@iservice365/node-server-utils");
1952
+ var import_joi7 = __toESM(require("joi"));
1953
+ var import_mongodb7 = require("mongodb");
1954
+ var unitSchema = import_joi7.default.object({
1955
+ name: import_joi7.default.string().required(),
1956
+ site: import_joi7.default.string().hex().required(),
1957
+ createdBy: import_joi7.default.string().hex().required()
1958
+ });
1959
+ function MUnit(value) {
1960
+ const { error } = unitSchema.validate(value);
1961
+ if (error) {
1962
+ import_node_server_utils12.logger.info(`Hygiene Unit Model: ${error.message}`);
1963
+ throw new import_node_server_utils12.BadRequestError(error.message);
1964
+ }
1965
+ if (value.site) {
1966
+ try {
1967
+ value.site = new import_mongodb7.ObjectId(value.site);
1968
+ } catch (error2) {
1969
+ throw new import_node_server_utils12.BadRequestError("Invalid site ID format.");
1970
+ }
1971
+ }
1972
+ if (value.createdBy) {
1973
+ try {
1974
+ value.createdBy = new import_mongodb7.ObjectId(value.createdBy);
1975
+ } catch (error2) {
1976
+ throw new import_node_server_utils12.BadRequestError("Invalid createdBy ID format.");
1977
+ }
1978
+ }
1979
+ return {
1980
+ name: value.name,
1981
+ createdBy: value.createdBy,
1982
+ site: value.site,
1983
+ status: "active",
1984
+ createdAt: /* @__PURE__ */ new Date(),
1985
+ updatedAt: value.updatedAt ?? "",
1986
+ deletedAt: value.deletedAt ?? ""
1987
+ };
1988
+ }
1989
+
1990
+ // src/services/hygiene-unit.service.ts
1991
+ var import_node_server_utils14 = require("@iservice365/node-server-utils");
1992
+
1993
+ // src/repositories/hygiene-unit.repository.ts
1994
+ var import_mongodb8 = require("mongodb");
1995
+ var import_node_server_utils13 = require("@iservice365/node-server-utils");
1996
+ function useUnitRepository() {
1997
+ const db = import_node_server_utils13.useAtlas.getDb();
1998
+ if (!db) {
1999
+ throw new import_node_server_utils13.InternalServerError("Unable to connect to server.");
2000
+ }
2001
+ const namespace_collection = "hygiene-units";
2002
+ const collection = db.collection(namespace_collection);
2003
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils13.useCache)(namespace_collection);
2004
+ async function createIndex() {
2005
+ try {
2006
+ await collection.createIndexes([
2007
+ { key: { site: 1 } },
2008
+ { key: { createdBy: 1 } },
2009
+ { key: { status: 1 } }
2010
+ ]);
2011
+ } catch (error) {
2012
+ throw new import_node_server_utils13.InternalServerError("Failed to create index on hygiene unit.");
2013
+ }
2014
+ }
2015
+ async function createTextIndex() {
2016
+ try {
2017
+ await collection.createIndex({ name: "text" });
2018
+ } catch (error) {
2019
+ throw new import_node_server_utils13.InternalServerError(
2020
+ "Failed to create text index on hygiene unit."
2021
+ );
2022
+ }
2023
+ }
2024
+ async function createUniqueIndex() {
2025
+ try {
2026
+ await collection.createIndex(
2027
+ { name: 1, site: 1, deletedAt: 1 },
2028
+ { unique: true }
2029
+ );
2030
+ } catch (error) {
2031
+ throw new import_node_server_utils13.InternalServerError(
2032
+ "Failed to create unique index on hygiene unit."
2033
+ );
2034
+ }
2035
+ }
2036
+ async function createUnit(value, session) {
2037
+ try {
2038
+ value = MUnit(value);
2039
+ const res = await collection.insertOne(value, { session });
2040
+ delNamespace().then(() => {
2041
+ import_node_server_utils13.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
2042
+ }).catch((err) => {
2043
+ import_node_server_utils13.logger.error(
2044
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2045
+ err
2046
+ );
2047
+ });
2048
+ return res.insertedId;
2049
+ } catch (error) {
2050
+ const isDuplicated = error.message.includes("duplicate");
2051
+ if (isDuplicated) {
2052
+ throw new import_node_server_utils13.BadRequestError("Unit already exists.");
2053
+ }
2054
+ throw error;
2055
+ }
2056
+ }
2057
+ async function getUnits({
2058
+ page = 1,
2059
+ limit = 10,
2060
+ search = "",
2061
+ startDate = "",
2062
+ endDate = "",
2063
+ site = ""
2064
+ }) {
2065
+ page = page > 0 ? page - 1 : 0;
2066
+ try {
2067
+ site = new import_mongodb8.ObjectId(site);
2068
+ } catch (error) {
2069
+ throw new import_node_server_utils13.BadRequestError("Invalid site ID format.");
2070
+ }
2071
+ const query = {
2072
+ status: { $ne: "deleted" },
2073
+ site
2074
+ };
2075
+ const cacheOptions = {
2076
+ page,
2077
+ limit,
2078
+ site: site.toString()
2079
+ };
2080
+ if (search) {
2081
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
2082
+ cacheOptions.search = search;
2083
+ }
2084
+ if (startDate && endDate) {
2085
+ query.createdAt = {
2086
+ $gte: new Date(startDate),
2087
+ $lte: new Date(endDate)
2088
+ };
2089
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
2090
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
2091
+ } else if (startDate) {
2092
+ query.createdAt = { $gte: new Date(startDate) };
2093
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
2094
+ } else if (endDate) {
2095
+ query.createdAt = { $lte: new Date(endDate) };
2096
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
2097
+ }
2098
+ const cacheKey = (0, import_node_server_utils13.makeCacheKey)(namespace_collection, cacheOptions);
2099
+ const cachedData = await getCache(cacheKey);
2100
+ if (cachedData) {
2101
+ import_node_server_utils13.logger.info(`Cache hit for key: ${cacheKey}`);
2102
+ return cachedData;
2103
+ }
2104
+ try {
2105
+ const items = await collection.aggregate([
2106
+ { $match: query },
2107
+ {
2108
+ $lookup: {
2109
+ from: "sites",
2110
+ localField: "site",
2111
+ foreignField: "_id",
2112
+ pipeline: [{ $project: { name: 1 } }],
2113
+ as: "site"
2114
+ }
2115
+ },
2116
+ {
2117
+ $unwind: {
2118
+ path: "$site",
2119
+ preserveNullAndEmptyArrays: true
2120
+ }
2121
+ },
2122
+ {
2123
+ $lookup: {
2124
+ from: "users",
2125
+ localField: "createdBy",
2126
+ foreignField: "_id",
2127
+ pipeline: [{ $project: { name: 1 } }],
2128
+ as: "createdBy"
2129
+ }
2130
+ },
2131
+ {
2132
+ $unwind: {
2133
+ path: "$createdBy",
2134
+ preserveNullAndEmptyArrays: true
2135
+ }
2136
+ },
2137
+ {
2138
+ $project: {
2139
+ name: 1,
2140
+ site: "$site._id",
2141
+ siteName: "$site.name",
2142
+ createdByName: "$createdBy.name",
2143
+ checklist: 1,
2144
+ status: 1,
2145
+ createdAt: 1
2146
+ }
2147
+ },
2148
+ { $sort: { _id: -1 } },
2149
+ { $skip: page * limit },
2150
+ { $limit: limit }
2151
+ ]).toArray();
2152
+ const length = await collection.countDocuments(query);
2153
+ const data = (0, import_node_server_utils13.paginate)(items, page, limit, length);
2154
+ setCache(cacheKey, data, 15 * 60).then(() => {
2155
+ import_node_server_utils13.logger.info(`Cache set for key: ${cacheKey}`);
2156
+ }).catch((err) => {
2157
+ import_node_server_utils13.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
2158
+ });
2159
+ return data;
2160
+ } catch (error) {
2161
+ throw error;
2162
+ }
2163
+ }
2164
+ async function getUnitByName(name, site) {
2165
+ try {
2166
+ if (site)
2167
+ site = new import_mongodb8.ObjectId(site);
2168
+ } catch (error) {
2169
+ throw new import_node_server_utils13.BadRequestError("Invalid site ID format.");
2170
+ }
2171
+ try {
2172
+ return await collection.findOne({
2173
+ name: { $regex: new RegExp(`^${name}$`, "i") },
2174
+ ...site && { site }
2175
+ });
2176
+ } catch (error) {
2177
+ throw new import_node_server_utils13.BadRequestError("Unable to fetch unit by name.");
2178
+ }
2179
+ }
2180
+ async function getUnitById(id, site) {
2181
+ try {
2182
+ id = typeof id === "string" ? new import_mongodb8.ObjectId(id) : id;
2183
+ } catch (error) {
2184
+ throw new import_node_server_utils13.BadRequestError("Invalid unit ID format.");
2185
+ }
2186
+ try {
2187
+ if (site)
2188
+ site = new import_mongodb8.ObjectId(site);
2189
+ } catch (error) {
2190
+ throw new import_node_server_utils13.BadRequestError(
2191
+ "Unable to fetch unit by ID, Invalid site ID format."
2192
+ );
2193
+ }
2194
+ try {
2195
+ return await collection.findOne({ _id: id, ...site && { site } });
2196
+ } catch (error) {
2197
+ throw new import_node_server_utils13.BadRequestError("Unable to fetch unit by id.");
2198
+ }
2199
+ }
2200
+ async function updateUnit(_id, value) {
2201
+ try {
2202
+ _id = new import_mongodb8.ObjectId(_id);
2203
+ } catch (error) {
2204
+ throw new import_node_server_utils13.BadRequestError("Invalid unit ID format.");
2205
+ }
2206
+ try {
2207
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
2208
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
2209
+ if (res.modifiedCount === 0) {
2210
+ throw new import_node_server_utils13.InternalServerError("Unable to update cleaning unit.");
2211
+ }
2212
+ delNamespace().then(() => {
2213
+ import_node_server_utils13.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
2214
+ }).catch((err) => {
2215
+ import_node_server_utils13.logger.error(
2216
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2217
+ err
2218
+ );
2219
+ });
2220
+ return res.modifiedCount;
2221
+ } catch (error) {
2222
+ const isDuplicated = error.message.includes("duplicate");
2223
+ if (isDuplicated) {
2224
+ throw new import_node_server_utils13.BadRequestError("Area already exists.");
2225
+ }
2226
+ throw error;
2227
+ }
2228
+ }
2229
+ async function deleteUnit(_id, session) {
2230
+ try {
2231
+ _id = new import_mongodb8.ObjectId(_id);
2232
+ } catch (error) {
2233
+ throw new import_node_server_utils13.BadRequestError("Invalid unit ID format.");
2234
+ }
2235
+ try {
2236
+ const updateValue = {
2237
+ status: "deleted",
2238
+ updatedAt: /* @__PURE__ */ new Date(),
2239
+ deletedAt: /* @__PURE__ */ new Date()
2240
+ };
2241
+ const res = await collection.updateOne(
2242
+ { _id },
2243
+ { $set: updateValue },
2244
+ { session }
2245
+ );
2246
+ if (res.modifiedCount === 0) {
2247
+ throw new import_node_server_utils13.InternalServerError("Unable to delete unit.");
2248
+ }
2249
+ delNamespace().then(() => {
2250
+ import_node_server_utils13.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
2251
+ }).catch((err) => {
2252
+ import_node_server_utils13.logger.error(
2253
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2254
+ err
2255
+ );
2256
+ });
2257
+ return res.modifiedCount;
2258
+ } catch (error) {
2259
+ throw error;
2260
+ }
2261
+ }
2262
+ return {
2263
+ createIndex,
2264
+ createTextIndex,
2265
+ createUniqueIndex,
2266
+ createUnit,
2267
+ getUnits,
2268
+ getUnitByName,
2269
+ getUnitById,
2270
+ updateUnit,
2271
+ deleteUnit
2272
+ };
2273
+ }
2274
+
2275
+ // src/services/hygiene-unit.service.ts
2276
+ function useUnitService() {
2277
+ const { createUnit: _createUnit } = useUnitRepository();
2278
+ async function uploadByFile({
2279
+ dataJson,
2280
+ createdBy,
2281
+ site
2282
+ }) {
2283
+ let dataArray;
2284
+ try {
2285
+ dataArray = JSON.parse(dataJson);
2286
+ } catch (error) {
2287
+ throw new import_node_server_utils14.BadRequestError("Invalid JSON format for data in excel");
2288
+ }
2289
+ if (!dataArray || dataArray.length === 0) {
2290
+ throw new import_node_server_utils14.NotFoundError("No data found in the uploaded file");
2291
+ }
2292
+ const session = import_node_server_utils14.useAtlas.getClient()?.startSession();
2293
+ const insertedUnitIds = [];
2294
+ try {
2295
+ session?.startTransaction();
2296
+ for (const row of dataArray) {
2297
+ if (!row?.UNIT_NAME) {
2298
+ import_node_server_utils14.logger.warn("Skipping row with missing UNIT_NAME:", row);
2299
+ continue;
2300
+ }
2301
+ try {
2302
+ const insertedId = await _createUnit(
2303
+ {
2304
+ name: String(row.UNIT_NAME).trim(),
2305
+ site,
2306
+ createdBy
2307
+ },
2308
+ session
2309
+ );
2310
+ insertedUnitIds.push(insertedId);
2311
+ } catch (error) {
2312
+ import_node_server_utils14.logger.error(
2313
+ `Error creating unit "${row.UNIT_NAME}":`,
2314
+ error.message
2315
+ );
2316
+ continue;
2317
+ }
2318
+ }
2319
+ await session?.commitTransaction();
2320
+ import_node_server_utils14.logger.info(`Successfully uploaded ${insertedUnitIds.length} units`);
2321
+ return {
2322
+ message: `Successfully uploaded ${insertedUnitIds.length} units`
2323
+ };
2324
+ } catch (error) {
2325
+ await session?.abortTransaction();
2326
+ import_node_server_utils14.logger.error("Error while uploading unit information", error);
2327
+ throw error;
2328
+ } finally {
2329
+ session?.endSession();
2330
+ }
2331
+ }
2332
+ return {
2333
+ uploadByFile
2334
+ };
2335
+ }
2336
+
2337
+ // src/controllers/hygiene-unit.controller.ts
2338
+ var import_node_server_utils15 = require("@iservice365/node-server-utils");
2339
+ var import_joi8 = __toESM(require("joi"));
2340
+ function useUnitController() {
2341
+ const {
2342
+ createUnit: _createUnit,
2343
+ getUnits: _getUnits,
2344
+ updateUnit: _updateUnit,
2345
+ deleteUnit: _deleteUnit
2346
+ } = useUnitRepository();
2347
+ const { uploadByFile: _uploadByFile } = useUnitService();
2348
+ async function createUnit(req, res, next) {
2349
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2350
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
2351
+ {}
2352
+ ) : {};
2353
+ const createdBy = cookies["user"] || "";
2354
+ const payload = { ...req.body, createdBy };
2355
+ const { error } = unitSchema.validate(payload);
2356
+ if (error) {
2357
+ import_node_server_utils15.logger.log({ level: "error", message: error.message });
2358
+ next(new import_node_server_utils15.BadRequestError(error.message));
2359
+ return;
2360
+ }
2361
+ try {
2362
+ const id = await _createUnit(payload);
2363
+ res.status(201).json({ message: "Unit created successfully.", id });
2364
+ return;
2365
+ } catch (error2) {
2366
+ import_node_server_utils15.logger.log({ level: "error", message: error2.message });
2367
+ next(error2);
2368
+ return;
2369
+ }
2370
+ }
2371
+ async function getAll(req, res, next) {
2372
+ const query = req.query;
2373
+ const validation = import_joi8.default.object({
2374
+ page: import_joi8.default.number().min(1).optional().allow("", null),
2375
+ limit: import_joi8.default.number().min(1).optional().allow("", null),
2376
+ search: import_joi8.default.string().optional().allow("", null),
2377
+ startDate: import_joi8.default.alternatives().try(import_joi8.default.date(), import_joi8.default.string()).optional().allow("", null),
2378
+ endDate: import_joi8.default.alternatives().try(import_joi8.default.date(), import_joi8.default.string()).optional().allow("", null),
2379
+ site: import_joi8.default.string().hex().optional().allow("", null)
2380
+ });
2381
+ const { error } = validation.validate(query);
2382
+ if (error) {
2383
+ import_node_server_utils15.logger.log({ level: "error", message: error.message });
2384
+ next(new import_node_server_utils15.BadRequestError(error.message));
2385
+ return;
2386
+ }
2387
+ const page = parseInt(req.query.page) ?? 1;
2388
+ const limit = parseInt(req.query.limit) ?? 20;
2389
+ const search = req.query.search ?? "";
2390
+ const site = req.query.site ?? "";
2391
+ const startDate = req.query.startDate ?? "";
2392
+ const endDate = req.query.endDate ?? "";
2393
+ try {
2394
+ const data = await _getUnits({
2395
+ page,
2396
+ limit,
2397
+ search,
2398
+ site,
2399
+ startDate,
2400
+ endDate
2401
+ });
2402
+ res.json(data);
2403
+ return;
2404
+ } catch (error2) {
2405
+ import_node_server_utils15.logger.log({ level: "error", message: error2.message });
2406
+ next(error2);
2407
+ return;
2408
+ }
2409
+ }
2410
+ async function updateUnit(req, res, next) {
2411
+ const payload = { id: req.params.id, ...req.body };
2412
+ const schema = import_joi8.default.object({
2413
+ id: import_joi8.default.string().hex().required(),
2414
+ name: import_joi8.default.string().required()
2415
+ });
2416
+ const { error } = schema.validate(payload);
2417
+ if (error) {
2418
+ import_node_server_utils15.logger.log({ level: "error", message: error.message });
2419
+ next(new import_node_server_utils15.BadRequestError(error.message));
2420
+ return;
2421
+ }
2422
+ try {
2423
+ const { id, ...value } = payload;
2424
+ await _updateUnit(id, value);
2425
+ res.json({ message: "Unit updated successfully." });
2426
+ return;
2427
+ } catch (error2) {
2428
+ import_node_server_utils15.logger.log({ level: "error", message: error2.message });
2429
+ next(error2);
2430
+ return;
2431
+ }
2432
+ }
2433
+ async function deleteUnit(req, res, next) {
2434
+ const id = req.params.id;
2435
+ const validation = import_joi8.default.object({
2436
+ id: import_joi8.default.string().hex().required()
2437
+ });
2438
+ const { error } = validation.validate({ id });
2439
+ if (error) {
2440
+ import_node_server_utils15.logger.log({ level: "error", message: error.message });
2441
+ next(new import_node_server_utils15.BadRequestError(error.message));
2442
+ return;
2443
+ }
2444
+ try {
2445
+ await _deleteUnit(id);
2446
+ res.json({ message: "Unit deleted successfully." });
2447
+ return;
2448
+ } catch (error2) {
2449
+ import_node_server_utils15.logger.log({ level: "error", message: error2.message });
2450
+ next(error2);
2451
+ return;
2452
+ }
2453
+ }
2454
+ async function uploadByFile(req, res, next) {
2455
+ if (!req.file) {
2456
+ next(new import_node_server_utils15.BadRequestError("File is required!"));
2457
+ return;
2458
+ }
2459
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2460
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
2461
+ {}
2462
+ ) : {};
2463
+ const createdBy = cookies["user"] || "";
2464
+ const { site } = req.body;
2465
+ const schema = import_joi8.default.object({
2466
+ site: import_joi8.default.string().hex().optional().allow("", null),
2467
+ createdBy: import_joi8.default.string().hex().required()
2468
+ });
2469
+ const { error } = schema.validate({ site, createdBy });
2470
+ if (error) {
2471
+ import_node_server_utils15.logger.log({ level: "error", message: error.message });
2472
+ next(new import_node_server_utils15.BadRequestError(error.message));
2473
+ return;
2474
+ }
2475
+ try {
2476
+ const xlsData = await convertBufferFile(req.file.buffer);
2477
+ const dataJson = JSON.stringify(xlsData);
2478
+ const result = await _uploadByFile({ dataJson, createdBy, site });
2479
+ return res.status(201).json(result);
2480
+ } catch (error2) {
2481
+ import_node_server_utils15.logger.log({ level: "error", message: error2.message });
2482
+ next(error2);
2483
+ return;
2484
+ }
2485
+ }
2486
+ return {
2487
+ createUnit,
2488
+ getAll,
2489
+ updateUnit,
2490
+ deleteUnit,
2491
+ uploadByFile
2492
+ };
2493
+ }
2494
+
2495
+ // src/models/hygiene-schedule-task-area.model.ts
2496
+ var import_node_server_utils16 = require("@iservice365/node-server-utils");
2497
+ var import_joi9 = __toESM(require("joi"));
2498
+ var import_mongodb9 = require("mongodb");
2499
+ var scheduleTaskAreaSchema = import_joi9.default.object({
2500
+ name: import_joi9.default.string().required(),
2501
+ site: import_joi9.default.string().hex().required(),
2502
+ createdBy: import_joi9.default.string().hex().required(),
2503
+ checklist: import_joi9.default.array().items(
2504
+ import_joi9.default.object({
2505
+ _id: import_joi9.default.string().hex().required(),
2506
+ name: import_joi9.default.string().required()
2507
+ })
2508
+ ).optional(),
2509
+ updatedAt: import_joi9.default.date().optional().allow("", null),
2510
+ status: import_joi9.default.string().allow("", null).optional()
2511
+ });
2512
+ function MScheduleTaskArea(value) {
2513
+ const { error } = scheduleTaskAreaSchema.validate(value);
2514
+ if (error) {
2515
+ throw new import_node_server_utils16.BadRequestError(error.message);
2516
+ }
2517
+ if (value.site) {
2518
+ try {
2519
+ value.site = new import_mongodb9.ObjectId(value.site);
2520
+ } catch (error2) {
2521
+ throw new import_node_server_utils16.BadRequestError("Invalid site ID format.");
2522
+ }
2523
+ }
2524
+ if (value.createdBy) {
2525
+ try {
2526
+ value.createdBy = new import_mongodb9.ObjectId(value.createdBy);
2527
+ } catch (error2) {
2528
+ throw new import_node_server_utils16.BadRequestError("Invalid createdBy ID format.");
2529
+ }
2530
+ }
2531
+ if (value.checklist && Array.isArray(value.checklist)) {
2532
+ value.checklist = value.checklist.map((item) => {
2533
+ try {
2534
+ return {
2535
+ ...item,
2536
+ _id: new import_mongodb9.ObjectId(item._id)
2537
+ };
2538
+ } catch (error2) {
2539
+ throw new import_node_server_utils16.BadRequestError(
2540
+ `Invalid checklist item ID format: ${item._id}`
2541
+ );
2542
+ }
2543
+ });
2544
+ }
2545
+ return {
2546
+ name: value.name,
2547
+ site: value.site,
2548
+ createdBy: value.createdBy,
2549
+ checklist: value.checklist,
2550
+ status: value.status ?? "",
2551
+ createdAt: /* @__PURE__ */ new Date(),
2552
+ updatedAt: value.updatedAt ?? "",
2553
+ deletedAt: value.deletedAt ?? ""
2554
+ };
2555
+ }
2556
+
2557
+ // src/repositories/hygiene-schedule-task-area.repository.ts
2558
+ var import_mongodb10 = require("mongodb");
2559
+ var import_node_server_utils17 = require("@iservice365/node-server-utils");
2560
+ function useScheduleTaskAreaRepository() {
2561
+ const db = import_node_server_utils17.useAtlas.getDb();
2562
+ if (!db) {
2563
+ throw new import_node_server_utils17.InternalServerError("Unable to connect to server.");
2564
+ }
2565
+ const namespace_collection = "hygiene-schedule-task-areas";
2566
+ const collection = db.collection(namespace_collection);
2567
+ const { delNamespace, setCache, getCache, delCache } = (0, import_node_server_utils17.useCache)(namespace_collection);
2568
+ async function createIndexes() {
2569
+ try {
2570
+ await collection.createIndexes([
2571
+ { key: { site: 1 } },
2572
+ { key: { name: "text" } }
2573
+ ]);
2574
+ } catch (error) {
2575
+ throw new import_node_server_utils17.InternalServerError("Failed to create index on site.");
2576
+ }
2577
+ }
2578
+ async function createUniqueIndex() {
2579
+ try {
2580
+ await collection.createIndex(
2581
+ { name: 1, site: 1, deletedAt: 1 },
2582
+ { unique: true }
2583
+ );
2584
+ } catch (error) {
2585
+ throw new import_node_server_utils17.InternalServerError(
2586
+ "Failed to create unique index on hygiene area."
2587
+ );
2588
+ }
2589
+ }
2590
+ async function createScheduleTaskArea(value, session) {
2591
+ try {
2592
+ value = MScheduleTaskArea(value);
2593
+ const res = await collection.insertOne(value, { session });
2594
+ delNamespace().then(() => {
2595
+ import_node_server_utils17.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
2596
+ }).catch((err) => {
2597
+ import_node_server_utils17.logger.error(
2598
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2599
+ err
2600
+ );
2601
+ });
2602
+ return res.insertedId;
2603
+ } catch (error) {
2604
+ const isDuplicated = error.message.includes("duplicate");
2605
+ if (isDuplicated) {
2606
+ throw new import_node_server_utils17.BadRequestError("Area already exists.");
2607
+ }
2608
+ throw error;
2609
+ }
2610
+ }
2611
+ async function updateScheduleTaskArea(_id, params) {
2612
+ try {
2613
+ _id = new import_mongodb10.ObjectId(_id);
2614
+ } catch (error) {
2615
+ throw new import_node_server_utils17.BadRequestError("Invalid area ID format.");
2616
+ }
2617
+ try {
2618
+ const updateValue = { ...params, updatedAt: /* @__PURE__ */ new Date() };
2619
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
2620
+ if (res.modifiedCount === 0) {
2621
+ throw new import_node_server_utils17.InternalServerError("Unable to update cleaning area.");
2622
+ }
2623
+ delNamespace().then(() => {
2624
+ import_node_server_utils17.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
2625
+ }).catch((err) => {
2626
+ import_node_server_utils17.logger.error(
2627
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2628
+ err
2629
+ );
2630
+ });
2631
+ return res.modifiedCount;
2632
+ } catch (error) {
2633
+ const isDuplicated = error.message.includes("duplicate");
2634
+ if (isDuplicated) {
2635
+ throw new import_node_server_utils17.BadRequestError("Area already exists.");
2636
+ }
2637
+ throw error;
2638
+ }
2639
+ }
2640
+ async function deleteScheduleTaskArea(_id, session) {
2641
+ try {
2642
+ _id = new import_mongodb10.ObjectId(_id);
2643
+ } catch (error) {
2644
+ throw new import_node_server_utils17.BadRequestError("Invalid area ID format.");
2645
+ }
2646
+ try {
2647
+ const updateValue = {
2648
+ status: "deleted",
2649
+ updatedAt: /* @__PURE__ */ new Date(),
2650
+ deletedAt: /* @__PURE__ */ new Date()
2651
+ };
2652
+ const res = await collection.updateOne(
2653
+ { _id },
2654
+ { $set: updateValue },
2655
+ { session }
2656
+ );
2657
+ if (res.modifiedCount === 0) {
2658
+ throw new import_node_server_utils17.InternalServerError("Unable to delete area.");
2659
+ }
2660
+ delNamespace().then(() => {
2661
+ import_node_server_utils17.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
2662
+ }).catch((err) => {
2663
+ import_node_server_utils17.logger.error(
2664
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2665
+ err
2666
+ );
2667
+ });
2668
+ return res.modifiedCount;
2669
+ } catch (error) {
2670
+ throw error;
2671
+ }
2672
+ }
2673
+ async function getScheduleTaskAreas({
2674
+ page = 1,
2675
+ limit = 10,
2676
+ search = "",
2677
+ startDate = "",
2678
+ endDate = "",
2679
+ site = ""
2680
+ }) {
2681
+ page = page > 0 ? page - 1 : 0;
2682
+ const query = {
2683
+ status: { $ne: "deleted" }
2684
+ };
2685
+ const cacheOptions = {
2686
+ page,
2687
+ limit
2688
+ };
2689
+ if (site) {
2690
+ try {
2691
+ site = new import_mongodb10.ObjectId(site);
2692
+ cacheOptions.site = site.toString();
2693
+ } catch (error) {
2694
+ throw new import_node_server_utils17.BadRequestError("Invalid site ID format.");
2695
+ }
2696
+ }
2697
+ if (search) {
2698
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
2699
+ cacheOptions.search = search;
2700
+ }
2701
+ if (startDate && endDate) {
2702
+ query.createdAt = {
2703
+ $gte: new Date(startDate),
2704
+ $lte: new Date(endDate)
2705
+ };
2706
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
2707
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
2708
+ } else if (startDate) {
2709
+ query.createdAt = { $gte: new Date(startDate) };
2710
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
2711
+ } else if (endDate) {
2712
+ query.createdAt = { $lte: new Date(endDate) };
2713
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
2714
+ }
2715
+ const cacheKey = (0, import_node_server_utils17.makeCacheKey)(namespace_collection, cacheOptions);
2716
+ const cachedData = await getCache(cacheKey);
2717
+ if (cachedData) {
2718
+ import_node_server_utils17.logger.info(`Cache hit for key: ${cacheKey}`);
2719
+ return cachedData;
2720
+ }
2721
+ try {
2722
+ const items = await collection.aggregate([
2723
+ {
2724
+ $match: query
2725
+ },
2726
+ {
2727
+ $lookup: {
2728
+ from: "sites",
2729
+ localField: "site",
2730
+ foreignField: "_id",
2731
+ pipeline: [{ $project: { name: 1 } }],
2732
+ as: "site"
2733
+ }
2734
+ },
2735
+ {
2736
+ $unwind: {
2737
+ path: "$site",
2738
+ preserveNullAndEmptyArrays: true
2739
+ }
2740
+ },
2741
+ {
2742
+ $lookup: {
2743
+ from: "users",
2744
+ localField: "createdBy",
2745
+ foreignField: "_id",
2746
+ pipeline: [{ $project: { name: 1 } }],
2747
+ as: "createdBy"
2748
+ }
2749
+ },
2750
+ {
2751
+ $unwind: {
2752
+ path: "$createdBy",
2753
+ preserveNullAndEmptyArrays: true
2754
+ }
2755
+ },
2756
+ {
2757
+ $project: {
2758
+ name: 1,
2759
+ site: "$site._id",
2760
+ siteName: "$site.name",
2761
+ createdByName: "$createdBy.name",
2762
+ checklist: 1,
2763
+ status: 1,
2764
+ createdAt: 1
2765
+ }
2766
+ },
2767
+ { $sort: { _id: -1 } },
2768
+ { $skip: page * limit },
2769
+ { $limit: limit }
2770
+ ]).toArray();
2771
+ const length = await collection.countDocuments(query);
2772
+ const data = (0, import_node_server_utils17.paginate)(items, page, limit, length);
2773
+ setCache(cacheKey, data, 15 * 60).then(() => {
2774
+ import_node_server_utils17.logger.info(`Cache set for key: ${cacheKey}`);
2775
+ }).catch((err) => {
2776
+ import_node_server_utils17.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
2777
+ });
2778
+ return data;
2779
+ } catch (error) {
2780
+ throw error;
2781
+ }
2782
+ }
2783
+ async function getScheduleTaskAreaByName(name, site) {
2784
+ try {
2785
+ if (site)
2786
+ site = new import_mongodb10.ObjectId(site);
2787
+ } catch (error) {
2788
+ throw new import_node_server_utils17.BadRequestError("Invalid site ID format.");
2789
+ }
2790
+ try {
2791
+ return await collection.findOne({
2792
+ name: { $regex: new RegExp(`^${name}$`, "i") },
2793
+ ...site && { site }
2794
+ });
2795
+ } catch (error) {
2796
+ throw new import_node_server_utils17.BadRequestError("Unable to fetch schedule task area by name.");
2797
+ }
2798
+ }
2799
+ async function getScheduleTaskAreaById(id, site) {
2800
+ try {
2801
+ id = typeof id === "string" ? new import_mongodb10.ObjectId(id) : id;
2802
+ } catch (error) {
2803
+ throw new import_node_server_utils17.BadRequestError("Invalid unit ID format.");
2804
+ }
2805
+ try {
2806
+ if (site)
2807
+ site = new import_mongodb10.ObjectId(site);
2808
+ } catch (error) {
2809
+ throw new import_node_server_utils17.BadRequestError(
2810
+ "Unable to fetch schedule task area by ID, Invalid site ID format."
2811
+ );
2812
+ }
2813
+ try {
2814
+ return await collection.findOne({ _id: id, ...site && { site } });
2815
+ } catch (error) {
2816
+ throw new import_node_server_utils17.BadRequestError("Unable to fetch schedule task area by id.");
2817
+ }
2818
+ }
2819
+ async function getAreaById(_id) {
2820
+ try {
2821
+ _id = new import_mongodb10.ObjectId(_id);
2822
+ } catch (error) {
2823
+ throw new import_node_server_utils17.BadRequestError("Invalid area ID format.");
2824
+ }
2825
+ const query = {
2826
+ _id,
2827
+ status: { $ne: "deleted" }
2828
+ };
2829
+ const cacheKey = (0, import_node_server_utils17.makeCacheKey)(namespace_collection, {
2830
+ _id: _id.toString()
2831
+ });
2832
+ const cachedData = await getCache(cacheKey);
2833
+ if (cachedData) {
2834
+ import_node_server_utils17.logger.info(`Cache hit for key: ${cacheKey}`);
2835
+ return cachedData;
2836
+ }
2837
+ try {
2838
+ const data = await collection.aggregate([
2839
+ { $match: query },
2840
+ {
2841
+ $project: {
2842
+ name: 1,
2843
+ checklist: 1
2844
+ }
2845
+ }
2846
+ ]).toArray();
2847
+ if (!data || !data.length) {
2848
+ throw new import_node_server_utils17.NotFoundError("Area not found.");
2849
+ }
2850
+ setCache(cacheKey, data[0], 15 * 60).then(() => {
2851
+ import_node_server_utils17.logger.info(`Cache set for key: ${cacheKey}`);
2852
+ }).catch((err) => {
2853
+ import_node_server_utils17.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
2854
+ });
2855
+ return data[0];
2856
+ } catch (error) {
2857
+ throw error;
2858
+ }
2859
+ }
2860
+ return {
2861
+ createUniqueIndex,
2862
+ createScheduleTaskArea,
2863
+ updateScheduleTaskArea,
2864
+ deleteScheduleTaskArea,
2865
+ getScheduleTaskAreas,
2866
+ createIndexes,
2867
+ getScheduleTaskAreaByName,
2868
+ getScheduleTaskAreaById,
2869
+ getAreaById
2870
+ };
2871
+ }
2872
+
2873
+ // src/services/hygiene-schedule-task-area.service.ts
2874
+ var import_node_server_utils18 = require("@iservice365/node-server-utils");
2875
+ function useScheduleTaskAreaService() {
2876
+ const { createScheduleTaskArea: _create } = useScheduleTaskAreaRepository();
2877
+ async function uploadByFile({
2878
+ dataJson,
2879
+ createdBy,
2880
+ site
2881
+ }) {
2882
+ let dataArray;
2883
+ try {
2884
+ dataArray = JSON.parse(dataJson);
2885
+ } catch (error) {
2886
+ throw new import_node_server_utils18.BadRequestError("Invalid JSON format for data in excel");
2887
+ }
2888
+ if (!dataArray || dataArray.length === 0) {
2889
+ throw new import_node_server_utils18.NotFoundError("No data found in the uploaded file");
2890
+ }
2891
+ const session = import_node_server_utils18.useAtlas.getClient()?.startSession();
2892
+ const insertedAreaIds = [];
2893
+ try {
2894
+ session?.startTransaction();
2895
+ for (const row of dataArray) {
2896
+ if (!row?.AREA_NAME) {
2897
+ import_node_server_utils18.logger.warn("Skipping row with missing AREA_NAME:", row);
2898
+ continue;
2899
+ }
2900
+ try {
2901
+ const insertedId = await _create(
2902
+ {
2903
+ name: String(row.AREA_NAME).trim(),
2904
+ site,
2905
+ createdBy
2906
+ },
2907
+ session
2908
+ );
2909
+ insertedAreaIds.push(insertedId);
2910
+ } catch (error) {
2911
+ import_node_server_utils18.logger.error(
2912
+ `Error creating area "${row.AREA_NAME}":`,
2913
+ error.message
2914
+ );
2915
+ continue;
2916
+ }
2917
+ }
2918
+ await session?.commitTransaction();
2919
+ import_node_server_utils18.logger.info(`Successfully uploaded ${insertedAreaIds.length} areas`);
2920
+ return {
2921
+ message: `Successfully uploaded ${insertedAreaIds.length} areas`
2922
+ };
2923
+ } catch (error) {
2924
+ await session?.abortTransaction();
2925
+ import_node_server_utils18.logger.error("Error while uploading area information", error);
2926
+ throw error;
2927
+ } finally {
2928
+ session?.endSession();
2929
+ }
2930
+ }
2931
+ return {
2932
+ uploadByFile
2933
+ };
2934
+ }
2935
+
2936
+ // src/controllers/hygiene-schedule-task-area.controller.ts
2937
+ var import_node_server_utils19 = require("@iservice365/node-server-utils");
2938
+ var import_joi10 = __toESM(require("joi"));
2939
+ function useScheduleTaskAreaController() {
2940
+ const { uploadByFile: _uploadByFile } = useScheduleTaskAreaService();
2941
+ const {
2942
+ getScheduleTaskAreas: _getAll,
2943
+ createScheduleTaskArea: _createScheduleTaskArea,
2944
+ updateScheduleTaskArea: _updateScheduleTaskArea,
2945
+ deleteScheduleTaskArea: _deleteById,
2946
+ getAreaById: _getAreaById
2947
+ } = useScheduleTaskAreaRepository();
2948
+ async function getAll(req, res, next) {
2949
+ const query = req.query;
2950
+ const validation = import_joi10.default.object({
2951
+ page: import_joi10.default.number().min(1).optional().allow("", null),
2952
+ limit: import_joi10.default.number().min(1).optional().allow("", null),
2953
+ search: import_joi10.default.string().optional().allow("", null),
2954
+ startDate: import_joi10.default.alternatives().try(import_joi10.default.date(), import_joi10.default.string()).optional().allow("", null),
2955
+ endDate: import_joi10.default.alternatives().try(import_joi10.default.date(), import_joi10.default.string()).optional().allow("", null),
2956
+ site: import_joi10.default.string().hex().optional().allow("", null)
2957
+ });
2958
+ const { error } = validation.validate(query);
2959
+ if (error) {
2960
+ next(new import_node_server_utils19.BadRequestError(error.message));
2961
+ return;
2962
+ }
2963
+ const page = parseInt(req.query.page) ?? 1;
2964
+ const limit = parseInt(req.query.limit) ?? 20;
2965
+ const search = req.query.search ?? "";
2966
+ const site = req.query.site ?? "";
2967
+ const startDate = req.query.startDate ?? "";
2968
+ const endDate = req.query.endDate ?? "";
2969
+ try {
2970
+ const data = await _getAll({
2971
+ page,
2972
+ limit,
2973
+ search,
2974
+ site,
2975
+ startDate,
2976
+ endDate
2977
+ });
2978
+ res.json(data);
2979
+ return;
2980
+ } catch (error2) {
2981
+ next(error2);
2982
+ return;
2983
+ }
2984
+ }
2985
+ async function createScheduleTaskArea(req, res, next) {
2986
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2987
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
2988
+ {}
2989
+ ) : {};
2990
+ const createdBy = cookies["user"] || "";
2991
+ const payload = { ...req.body, createdBy };
2992
+ const { error } = scheduleTaskAreaSchema.validate(payload);
2993
+ if (error) {
2994
+ import_node_server_utils19.logger.log({ level: "error", message: error.message });
2995
+ next(new import_node_server_utils19.BadRequestError(error.message));
2996
+ return;
2997
+ }
2998
+ try {
2999
+ const id = await _createScheduleTaskArea(payload);
3000
+ res.status(201).json({ message: "Area created successfully.", id });
3001
+ return;
3002
+ } catch (error2) {
3003
+ import_node_server_utils19.logger.log({ level: "error", message: error2.message });
3004
+ next(error2);
3005
+ return;
3006
+ }
3007
+ }
3008
+ async function updateScheduleTaskArea(req, res, next) {
3009
+ const payload = { id: req.params.id, ...req.body };
3010
+ const schema = import_joi10.default.object({
3011
+ id: import_joi10.default.string().hex().required(),
3012
+ name: import_joi10.default.string().required()
3013
+ });
3014
+ const { error } = schema.validate(payload);
3015
+ if (error) {
3016
+ next(new import_node_server_utils19.BadRequestError(error.message));
3017
+ import_node_server_utils19.logger.info(`Controller: ${error.message}`);
3018
+ return;
3019
+ }
3020
+ try {
3021
+ const { id, ...value } = payload;
3022
+ await _updateScheduleTaskArea(id, value);
3023
+ res.json({ message: "Area updated successfully." });
3024
+ return;
3025
+ } catch (error2) {
3026
+ import_node_server_utils19.logger.log({ level: "error", message: error2.message });
3027
+ next(error2);
3028
+ return;
3029
+ }
3030
+ }
3031
+ async function deleteScheduleTaskArea(req, res, next) {
3032
+ const id = req.params.id;
3033
+ const validation = import_joi10.default.object({
3034
+ id: import_joi10.default.string().hex().required()
3035
+ });
3036
+ const { error } = validation.validate({ id });
3037
+ if (error) {
3038
+ import_node_server_utils19.logger.log({ level: "error", message: error.message });
3039
+ next(new import_node_server_utils19.BadRequestError(error.message));
3040
+ return;
3041
+ }
3042
+ try {
3043
+ await _deleteById(id);
3044
+ res.json({ message: "Area deleted successfully." });
3045
+ return;
3046
+ } catch (error2) {
3047
+ import_node_server_utils19.logger.log({ level: "error", message: error2.message });
3048
+ next(error2);
3049
+ return;
3050
+ }
3051
+ }
3052
+ async function uploadByFile(req, res, next) {
3053
+ if (!req.file) {
3054
+ next(new import_node_server_utils19.BadRequestError("File is required!"));
3055
+ return;
3056
+ }
3057
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3058
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
3059
+ {}
3060
+ ) : {};
3061
+ const createdBy = cookies["user"] || "";
3062
+ const { site } = req.body;
3063
+ const schema = import_joi10.default.object({
3064
+ site: import_joi10.default.string().hex().optional().allow("", null),
3065
+ createdBy: import_joi10.default.string().hex().required()
3066
+ });
3067
+ const { error } = schema.validate({ site, createdBy });
3068
+ if (error) {
3069
+ import_node_server_utils19.logger.log({ level: "error", message: error.message });
3070
+ next(new import_node_server_utils19.BadRequestError(error.message));
3071
+ return;
3072
+ }
3073
+ try {
3074
+ const xlsData = await convertBufferFile(req.file.buffer);
3075
+ const dataJson = JSON.stringify(xlsData);
3076
+ const result = await _uploadByFile({ dataJson, createdBy, site });
3077
+ return res.status(201).json(result);
3078
+ } catch (error2) {
3079
+ import_node_server_utils19.logger.log({ level: "error", message: error2.message });
3080
+ next(error2);
3081
+ return;
3082
+ }
3083
+ }
3084
+ async function getAreaById(req, res, next) {
3085
+ const validation = import_joi10.default.string().hex().required();
3086
+ const _id = req.params.id;
3087
+ const { error } = validation.validate(_id);
3088
+ if (error) {
3089
+ import_node_server_utils19.logger.log({ level: "error", message: error.message });
3090
+ next(new import_node_server_utils19.BadRequestError(error.message));
3091
+ return;
3092
+ }
3093
+ try {
3094
+ const data = await _getAreaById(_id);
3095
+ res.json(data);
3096
+ return;
3097
+ } catch (error2) {
3098
+ import_node_server_utils19.logger.log({ level: "error", message: error2.message });
3099
+ next(error2);
3100
+ return;
3101
+ }
3102
+ }
3103
+ return {
3104
+ getAll,
3105
+ getAreaById,
3106
+ createScheduleTaskArea,
3107
+ updateScheduleTaskArea,
3108
+ deleteScheduleTaskArea,
3109
+ uploadByFile
3110
+ };
3111
+ }
3112
+
3113
+ // src/models/hygiene-area-checklist.model.ts
3114
+ var import_node_server_utils20 = require("@iservice365/node-server-utils");
3115
+ var import_joi11 = __toESM(require("joi"));
3116
+ var import_mongodb11 = require("mongodb");
3117
+ var areaChecklistSchema = import_joi11.default.object({
3118
+ type: import_joi11.default.string().required().valid(...allowedTypes),
3119
+ site: import_joi11.default.string().hex().required(),
3120
+ parentChecklist: import_joi11.default.string().hex().required(),
3121
+ area: import_joi11.default.string().hex().required(),
3122
+ name: import_joi11.default.string().optional().allow("", null),
3123
+ createdBy: import_joi11.default.string().hex().optional().allow("", null)
3124
+ });
3125
+ function MAreaChecklist(value) {
3126
+ const { error } = areaChecklistSchema.validate(value);
3127
+ if (error) {
3128
+ import_node_server_utils20.logger.info(`Hygiene Checklist Area Model: ${error.message}`);
3129
+ throw new import_node_server_utils20.BadRequestError(error.message);
3130
+ }
3131
+ if (value.site) {
3132
+ try {
3133
+ value.site = new import_mongodb11.ObjectId(value.site);
3134
+ } catch (error2) {
3135
+ throw new import_node_server_utils20.BadRequestError("Invalid site ID format.");
3136
+ }
3137
+ }
3138
+ if (value.parentChecklist) {
3139
+ try {
3140
+ value.parentChecklist = new import_mongodb11.ObjectId(value.parentChecklist);
3141
+ } catch (error2) {
3142
+ throw new import_node_server_utils20.BadRequestError("Invalid checklist ID format.");
3143
+ }
3144
+ }
3145
+ if (value.area) {
3146
+ try {
3147
+ value.area = new import_mongodb11.ObjectId(value.area);
3148
+ } catch (error2) {
3149
+ throw new import_node_server_utils20.BadRequestError("Invalid area ID format.");
3150
+ }
3151
+ }
3152
+ if (value.createdBy) {
3153
+ try {
3154
+ value.createdBy = new import_mongodb11.ObjectId(value.createdBy);
3155
+ } catch (error2) {
3156
+ throw new import_node_server_utils20.BadRequestError("Invalid createdBy ID format.");
3157
+ }
3158
+ }
3159
+ if (value.acceptedBy) {
3160
+ try {
3161
+ value.acceptedBy = new import_mongodb11.ObjectId(value.acceptedBy);
3162
+ } catch (error2) {
3163
+ throw new import_node_server_utils20.BadRequestError("Invalid acceptedBy ID format.");
3164
+ }
3165
+ }
3166
+ return {
3167
+ type: value.type,
3168
+ site: value.site,
3169
+ parentChecklist: value.parentChecklist,
3170
+ area: value.area,
3171
+ name: value.name ?? "",
3172
+ status: value.status ?? "To Do",
3173
+ createdBy: value.createdBy ?? "",
3174
+ createdAt: /* @__PURE__ */ new Date(),
3175
+ acceptedBy: value.acceptedBy ?? "",
3176
+ acceptedAt: value.acceptedAt ?? "",
3177
+ startedAt: value.startedAt ?? "",
3178
+ endedAt: value.endedAt ?? "",
3179
+ signature: value.signature ?? "",
3180
+ updatedAt: value.updatedAt ?? ""
3181
+ };
3182
+ }
3183
+
3184
+ // src/repositories/hygiene-area-checklist.repository.ts
3185
+ var import_node_server_utils21 = require("@iservice365/node-server-utils");
3186
+ var import_mongodb12 = require("mongodb");
3187
+ function useAreaChecklistRepo() {
3188
+ const db = import_node_server_utils21.useAtlas.getDb();
3189
+ if (!db) {
3190
+ throw new import_node_server_utils21.InternalServerError("Unable to connect to server.");
3191
+ }
3192
+ const namespace_collection = "hygiene-checklist.areas";
3193
+ const unit_checklist_collection = "hygiene-checklist.units";
3194
+ const collection = db.collection(namespace_collection);
3195
+ const unitChecklistCollection = db.collection(unit_checklist_collection);
3196
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils21.useCache)(namespace_collection);
3197
+ const { delNamespace: delUnitNamespace } = (0, import_node_server_utils21.useCache)(
3198
+ unit_checklist_collection
3199
+ );
3200
+ async function createIndex() {
3201
+ try {
3202
+ await collection.createIndexes([
3203
+ { key: { type: 1 } },
3204
+ { key: { site: 1 } },
3205
+ { key: { parentChecklist: 1 } },
3206
+ { key: { area: 1 } },
3207
+ { key: { createdAt: 1 } },
3208
+ { key: { acceptedBy: 1 } }
3209
+ ]);
3210
+ } catch (error) {
3211
+ throw new import_node_server_utils21.InternalServerError(
3212
+ "Failed to create index on hygiene checklist area."
3213
+ );
3214
+ }
3215
+ }
3216
+ async function createTextIndex() {
3217
+ try {
3218
+ await collection.createIndex({ name: "text" });
3219
+ } catch (error) {
3220
+ throw new import_node_server_utils21.InternalServerError(
3221
+ "Failed to create text index on hygiene checklist area."
3222
+ );
3223
+ }
3224
+ }
3225
+ async function createAreaChecklist(value, session) {
3226
+ try {
3227
+ const siteId = new import_mongodb12.ObjectId(value.site);
3228
+ const parentChecklistId = new import_mongodb12.ObjectId(value.parentChecklist);
3229
+ const areaId = new import_mongodb12.ObjectId(value.area);
3230
+ const currentDate = /* @__PURE__ */ new Date();
3231
+ const startOfDay = new Date(currentDate);
3232
+ startOfDay.setUTCHours(0, 0, 0, 0);
3233
+ const endOfDay = new Date(currentDate);
3234
+ endOfDay.setUTCHours(23, 59, 59, 999);
3235
+ const existingChecklist = await collection.findOne({
3236
+ type: value.type,
3237
+ site: siteId,
3238
+ parentChecklist: parentChecklistId,
3239
+ area: areaId,
3240
+ createdAt: {
3241
+ $gte: startOfDay,
3242
+ $lte: endOfDay
3243
+ }
3244
+ });
3245
+ if (existingChecklist) {
3246
+ import_node_server_utils21.logger.info(
3247
+ `Area checklist already exists for area ${areaId} on ${currentDate.toISOString().split("T")[0]}`
3248
+ );
3249
+ return existingChecklist._id;
3250
+ }
3251
+ const processedValue = MAreaChecklist(value);
3252
+ const result = await collection.insertOne(processedValue, { session });
3253
+ delNamespace().then(() => {
3254
+ import_node_server_utils21.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
3255
+ }).catch((err) => {
3256
+ import_node_server_utils21.logger.error(
3257
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3258
+ err
3259
+ );
3260
+ });
3261
+ return result.insertedId;
3262
+ } catch (error) {
3263
+ throw error;
3264
+ }
3265
+ }
3266
+ async function getAllAreaChecklist({
3267
+ page = 1,
3268
+ limit = 10,
3269
+ search = "",
3270
+ site,
3271
+ type,
3272
+ parentChecklist
3273
+ }) {
3274
+ page = page > 0 ? page - 1 : 0;
3275
+ const query = { type };
3276
+ const cacheOptions = {
3277
+ page,
3278
+ limit
3279
+ };
3280
+ try {
3281
+ query.site = new import_mongodb12.ObjectId(site);
3282
+ cacheOptions.site = site.toString();
3283
+ } catch (error) {
3284
+ throw new import_node_server_utils21.BadRequestError("Invalid site ID format.");
3285
+ }
3286
+ try {
3287
+ query.parentChecklist = new import_mongodb12.ObjectId(parentChecklist);
3288
+ cacheOptions.parentChecklist = parentChecklist.toString();
3289
+ } catch (error) {
3290
+ throw new import_node_server_utils21.BadRequestError("Invalid parent checklist ID format.");
3291
+ }
3292
+ if (search) {
3293
+ query.$text = { $search: search };
3294
+ cacheOptions.search = search;
3295
+ }
3296
+ const cacheKey = (0, import_node_server_utils21.makeCacheKey)(namespace_collection, cacheOptions);
3297
+ const cachedData = await getCache(cacheKey);
3298
+ if (cachedData) {
3299
+ import_node_server_utils21.logger.info(`Cache hit for key: ${cacheKey}`);
3300
+ return cachedData;
3301
+ }
3302
+ try {
3303
+ const pipeline = [
3304
+ { $match: query },
3305
+ {
3306
+ $lookup: {
3307
+ from: "users",
3308
+ localField: "acceptedBy",
3309
+ foreignField: "_id",
3310
+ pipeline: [{ $project: { name: 1 } }],
3311
+ as: "acceptedBy"
3312
+ }
3313
+ },
3314
+ {
3315
+ $unwind: {
3316
+ path: "$acceptedBy",
3317
+ preserveNullAndEmptyArrays: true
3318
+ }
3319
+ },
3320
+ {
3321
+ $project: {
3322
+ name: 1,
3323
+ acceptedByName: "$acceptedBy.name",
3324
+ status: 1,
3325
+ createdAt: 1
3326
+ }
3327
+ },
3328
+ { $sort: { _id: -1 } },
3329
+ { $skip: page * limit },
3330
+ { $limit: limit }
3331
+ ];
3332
+ const items = await collection.aggregate(pipeline).toArray();
3333
+ const length = await collection.countDocuments(query);
3334
+ const data = (0, import_node_server_utils21.paginate)(items, page, limit, length);
3335
+ setCache(cacheKey, data, 15 * 60).then(() => {
3336
+ import_node_server_utils21.logger.info(`Cache set for key: ${cacheKey}`);
3337
+ }).catch((err) => {
3338
+ import_node_server_utils21.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
3339
+ });
3340
+ return data;
3341
+ } catch (error) {
3342
+ throw error;
3343
+ }
3344
+ }
3345
+ async function getAreaChecklistHistory({
3346
+ page = 1,
3347
+ limit = 10,
3348
+ search = "",
3349
+ site,
3350
+ type,
3351
+ parentChecklist,
3352
+ status,
3353
+ createdAt,
3354
+ user
3355
+ }) {
3356
+ page = page > 0 ? page - 1 : 0;
3357
+ const query = { type };
3358
+ const cacheOptions = {
3359
+ page,
3360
+ limit
3361
+ };
3362
+ try {
3363
+ query.site = new import_mongodb12.ObjectId(site);
3364
+ cacheOptions.site = site.toString();
3365
+ } catch (error) {
3366
+ throw new import_node_server_utils21.BadRequestError("Invalid site ID format.");
3367
+ }
3368
+ try {
3369
+ query.parentChecklist = new import_mongodb12.ObjectId(parentChecklist);
3370
+ cacheOptions.parentChecklist = parentChecklist.toString();
3371
+ } catch (error) {
3372
+ throw new import_node_server_utils21.BadRequestError("Invalid parent checklist ID format.");
3373
+ }
3374
+ if (search) {
3375
+ query.$text = { $search: search };
3376
+ cacheOptions.search = search;
3377
+ }
3378
+ if (createdAt) {
3379
+ query.createdAt = {
3380
+ $gte: /* @__PURE__ */ new Date(`${createdAt}T00:00:00Z`),
3381
+ $lte: /* @__PURE__ */ new Date(`${createdAt}T23:59:59Z`)
3382
+ };
3383
+ cacheOptions.createdAt = new Date(createdAt).toISOString().split("T")[0];
3384
+ }
3385
+ if (status) {
3386
+ query.status = status;
3387
+ cacheOptions.status = status;
3388
+ } else {
3389
+ query.status = { $in: ["In Progress", "Completed"] };
3390
+ }
3391
+ if (user) {
3392
+ try {
3393
+ query.acceptedBy = new import_mongodb12.ObjectId(user);
3394
+ cacheOptions.user = user.toString();
3395
+ } catch (error) {
3396
+ throw new import_node_server_utils21.BadRequestError("Invalid user ID format.");
3397
+ }
3398
+ }
3399
+ const cacheKey = (0, import_node_server_utils21.makeCacheKey)(namespace_collection, cacheOptions);
3400
+ const cachedData = await getCache(cacheKey);
3401
+ if (cachedData) {
3402
+ import_node_server_utils21.logger.info(`Cache hit for key: ${cacheKey}`);
3403
+ return cachedData;
3404
+ }
3405
+ try {
3406
+ const pipeline = [
3407
+ { $match: query },
3408
+ {
3409
+ $lookup: {
3410
+ from: "users",
3411
+ localField: "acceptedBy",
3412
+ foreignField: "_id",
3413
+ pipeline: [
3414
+ ...search ? [{ $match: { name: { $regex: search, $options: "i" } } }] : [],
3415
+ { $project: { name: 1 } }
3416
+ ],
3417
+ as: "acceptedBy"
3418
+ }
3419
+ },
3420
+ {
3421
+ $unwind: {
3422
+ path: "$acceptedBy",
3423
+ preserveNullAndEmptyArrays: true
3424
+ }
3425
+ },
3426
+ {
3427
+ $project: {
3428
+ name: 1,
3429
+ createdAt: 1,
3430
+ acceptedByName: "$acceptedBy.name",
3431
+ status: 1,
3432
+ startedAt: 1,
3433
+ endedAt: 1
3434
+ }
3435
+ },
3436
+ { $sort: { status: 1 } },
3437
+ { $skip: page * limit },
3438
+ { $limit: limit }
3439
+ ];
3440
+ const items = await collection.aggregate(pipeline).toArray();
3441
+ const length = await collection.countDocuments(query);
3442
+ const data = (0, import_node_server_utils21.paginate)(items, page, limit, length);
3443
+ setCache(cacheKey, data, 15 * 60).then(() => {
3444
+ import_node_server_utils21.logger.info(`Cache set for key: ${cacheKey}`);
3445
+ }).catch((err) => {
3446
+ import_node_server_utils21.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
3447
+ });
3448
+ return data;
3449
+ } catch (error) {
3450
+ throw error;
3451
+ }
3452
+ }
3453
+ async function getAreaChecklistHistoryDetails(_id) {
3454
+ try {
3455
+ _id = new import_mongodb12.ObjectId(_id);
3456
+ } catch (error) {
3457
+ throw new import_node_server_utils21.BadRequestError("Invalid area checklist ID format.");
3458
+ }
3459
+ const cacheKey = (0, import_node_server_utils21.makeCacheKey)(namespace_collection, {
3460
+ _id: _id.toString()
3461
+ });
3462
+ const cachedData = await getCache(cacheKey);
3463
+ if (cachedData) {
3464
+ import_node_server_utils21.logger.info(`Cache hit for key: ${cacheKey}`);
3465
+ return cachedData;
3466
+ }
3467
+ try {
3468
+ const areaPipeline = [
3469
+ { $match: { _id } },
3470
+ {
3471
+ $lookup: {
3472
+ from: "users",
3473
+ localField: "createdBy",
3474
+ foreignField: "_id",
3475
+ pipeline: [{ $project: { name: 1 } }],
3476
+ as: "createdBy"
3477
+ }
3478
+ },
3479
+ {
3480
+ $unwind: {
3481
+ path: "$createdBy",
3482
+ preserveNullAndEmptyArrays: true
3483
+ }
3484
+ },
3485
+ {
3486
+ $lookup: {
3487
+ from: "users",
3488
+ localField: "acceptedBy",
3489
+ foreignField: "_id",
3490
+ pipeline: [{ $project: { name: 1 } }],
3491
+ as: "acceptedBy"
3492
+ }
3493
+ },
3494
+ {
3495
+ $unwind: {
3496
+ path: "$acceptedBy",
3497
+ preserveNullAndEmptyArrays: true
3498
+ }
3499
+ },
3500
+ {
3501
+ $project: {
3502
+ name: 1,
3503
+ createdAt: 1,
3504
+ createdByName: "$createdBy.name",
3505
+ startedAt: 1,
3506
+ endedAt: 1,
3507
+ status: 1,
3508
+ signature: 1,
3509
+ acceptedByName: "$acceptedBy.name"
3510
+ }
3511
+ }
3512
+ ];
3513
+ const unitPipeline = [
3514
+ { $match: { areaChecklist: _id } },
3515
+ {
3516
+ $project: {
3517
+ name: 1,
3518
+ remarks: "$metadata.remarks",
3519
+ attachments: "$metadata.attachments",
3520
+ approve: { $ifNull: ["$approve", null] },
3521
+ reject: { $ifNull: ["$reject", null] }
3522
+ }
3523
+ }
3524
+ ];
3525
+ const [area, units] = await Promise.all([
3526
+ collection.aggregate(areaPipeline).toArray(),
3527
+ unitChecklistCollection.aggregate(unitPipeline).toArray()
3528
+ ]);
3529
+ if (!area.length) {
3530
+ throw new import_node_server_utils21.BadRequestError("Area checklist not found.");
3531
+ }
3532
+ const items = {
3533
+ area: area[0],
3534
+ units
3535
+ };
3536
+ setCache(cacheKey, items, 15 * 60).then(() => {
3537
+ import_node_server_utils21.logger.info(`Cache set for key: ${cacheKey}`);
3538
+ }).catch((err) => {
3539
+ import_node_server_utils21.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
3540
+ });
3541
+ return items;
3542
+ } catch (error) {
3543
+ throw error;
3544
+ }
3545
+ }
3546
+ async function acceptAreaChecklist(_id, acceptedBy) {
3547
+ try {
3548
+ _id = new import_mongodb12.ObjectId(_id);
3549
+ } catch (error) {
3550
+ throw new import_node_server_utils21.BadRequestError("Invalid area ID format.");
3551
+ }
3552
+ try {
3553
+ acceptedBy = new import_mongodb12.ObjectId(acceptedBy);
3554
+ } catch (error) {
3555
+ throw new import_node_server_utils21.BadRequestError("Invalid acceptedBy ID format.");
3556
+ }
3557
+ try {
3558
+ const now = /* @__PURE__ */ new Date();
3559
+ const updateValue = {
3560
+ acceptedBy,
3561
+ acceptedAt: now,
3562
+ startedAt: now,
3563
+ updatedAt: now
3564
+ };
3565
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
3566
+ if (res.modifiedCount === 0) {
3567
+ throw new import_node_server_utils21.InternalServerError("Unable to update cleaning area.");
3568
+ }
3569
+ delNamespace().then(() => {
3570
+ import_node_server_utils21.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
3571
+ }).catch((err) => {
3572
+ import_node_server_utils21.logger.error(
3573
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3574
+ err
3575
+ );
3576
+ });
3577
+ return res.modifiedCount;
3578
+ } catch (error) {
3579
+ throw error;
3580
+ }
3581
+ }
3582
+ async function attachImageAreaChecklist(_id, attachments, session) {
3583
+ try {
3584
+ _id = new import_mongodb12.ObjectId(_id);
3585
+ } catch (error) {
3586
+ throw new import_node_server_utils21.BadRequestError("Invalid area checklist ID format.");
3587
+ }
3588
+ try {
3589
+ const now = /* @__PURE__ */ new Date();
3590
+ const updateValue = {
3591
+ metadata: { attachments },
3592
+ updatedAt: now
3593
+ };
3594
+ const res = await collection.updateOne(
3595
+ { _id },
3596
+ { $set: updateValue },
3597
+ { session }
3598
+ );
3599
+ if (res.modifiedCount === 0) {
3600
+ throw new import_node_server_utils21.InternalServerError(
3601
+ "Unable to update cleaning area checklist."
3602
+ );
3603
+ }
3604
+ delNamespace().then(() => {
3605
+ import_node_server_utils21.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
3606
+ }).catch((err) => {
3607
+ import_node_server_utils21.logger.error(
3608
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3609
+ err
3610
+ );
3611
+ });
3612
+ delUnitNamespace().then(() => {
3613
+ import_node_server_utils21.logger.info(
3614
+ `Cache cleared for namespace: ${unit_checklist_collection}`
3615
+ );
3616
+ }).catch((err) => {
3617
+ import_node_server_utils21.logger.error(
3618
+ `Failed to clear cache for namespace: ${unit_checklist_collection}`,
3619
+ err
3620
+ );
3621
+ });
3622
+ return res.modifiedCount;
3623
+ } catch (error) {
3624
+ throw error;
3625
+ }
3626
+ }
3627
+ async function submitAreaChecklist(_id, signature) {
3628
+ try {
3629
+ _id = new import_mongodb12.ObjectId(_id);
3630
+ } catch (error) {
3631
+ throw new import_node_server_utils21.BadRequestError("Invalid area ID format.");
3632
+ }
3633
+ try {
3634
+ const now = /* @__PURE__ */ new Date();
3635
+ const updateValue = {
3636
+ signature,
3637
+ status: "In Progress",
3638
+ endedAt: now,
3639
+ updatedAt: now
3640
+ };
3641
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
3642
+ if (res.modifiedCount === 0) {
3643
+ throw new import_node_server_utils21.InternalServerError("Unable to update cleaning area.");
3644
+ }
3645
+ delNamespace().then(() => {
3646
+ import_node_server_utils21.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
3647
+ }).catch((err) => {
3648
+ import_node_server_utils21.logger.error(
3649
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3650
+ err
3651
+ );
3652
+ });
3653
+ return res.modifiedCount;
3654
+ } catch (error) {
3655
+ throw error;
3656
+ }
3657
+ }
3658
+ async function completeAreaChecklist(_id, signature) {
3659
+ try {
3660
+ _id = new import_mongodb12.ObjectId(_id);
3661
+ } catch (error) {
3662
+ throw new import_node_server_utils21.BadRequestError("Invalid area ID format.");
3663
+ }
3664
+ try {
3665
+ const now = /* @__PURE__ */ new Date();
3666
+ const updateValue = {
3667
+ signature,
3668
+ status: "Completed",
3669
+ endedAt: now,
3670
+ updatedAt: now
3671
+ };
3672
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
3673
+ if (res.modifiedCount === 0) {
3674
+ throw new import_node_server_utils21.InternalServerError("Unable to update cleaning area.");
3675
+ }
3676
+ delNamespace().then(() => {
3677
+ import_node_server_utils21.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
3678
+ }).catch((err) => {
3679
+ import_node_server_utils21.logger.error(
3680
+ `Failed to clear cache for namespace: ${namespace_collection}`,
3681
+ err
3682
+ );
3683
+ });
3684
+ return res.modifiedCount;
3685
+ } catch (error) {
3686
+ throw error;
3687
+ }
3688
+ }
3689
+ return {
3690
+ createIndex,
3691
+ createTextIndex,
3692
+ createAreaChecklist,
3693
+ getAllAreaChecklist,
3694
+ getAreaChecklistHistory,
3695
+ getAreaChecklistHistoryDetails,
3696
+ acceptAreaChecklist,
3697
+ attachImageAreaChecklist,
3698
+ submitAreaChecklist,
3699
+ completeAreaChecklist
3700
+ };
3701
+ }
3702
+
3703
+ // src/controllers/hygiene-area-checklist.controller.ts
3704
+ var import_node_server_utils23 = require("@iservice365/node-server-utils");
3705
+ var import_joi12 = __toESM(require("joi"));
3706
+
3707
+ // src/services/hygiene-area-checklist.service.ts
3708
+ var import_node_server_utils22 = require("@iservice365/node-server-utils");
3709
+ function useAreaChecklistService() {
3710
+ const { createAreaChecklist: _createAreaChecklist } = useAreaChecklistRepo();
3711
+ const { getAreas } = useAreaRepository();
3712
+ const { getToiletLocations } = useToiletLocationRepository();
3713
+ async function createAreaChecklist(value) {
3714
+ try {
3715
+ const results = [];
3716
+ if (value.type === "cleaner") {
3717
+ const cleanerAreasResult = await getAreas({
3718
+ site: value.site
3719
+ });
3720
+ const cleanerAreas = cleanerAreasResult.items || [];
3721
+ if (cleanerAreas.length === 0) {
3722
+ import_node_server_utils22.logger.warn(`No cleaner areas found for site: ${value.site}`);
3723
+ return results;
3724
+ }
3725
+ for (const area of cleanerAreas) {
3726
+ const checklistData = {
3727
+ type: "cleaner",
3728
+ site: value.site,
3729
+ parentChecklist: value.parentChecklist,
3730
+ area: area._id.toString(),
3731
+ name: area.name,
3732
+ createdBy: value.createdBy
3733
+ };
3734
+ const insertedId = await _createAreaChecklist(checklistData);
3735
+ results.push(insertedId);
3736
+ }
3737
+ import_node_server_utils22.logger.info(
3738
+ `Created ${results.length} cleaner area checklists for site: ${value.site}`
3739
+ );
3740
+ }
3741
+ if (value.type === "toilet") {
3742
+ const toiletAreasResult = await getToiletLocations({
3743
+ site: value.site
3744
+ });
3745
+ const toiletAreas = toiletAreasResult.items || [];
3746
+ if (toiletAreas.length === 0) {
3747
+ import_node_server_utils22.logger.warn(`No toilet locations found for site: ${value.site}`);
3748
+ return results;
3749
+ }
3750
+ for (const toiletLocation of toiletAreas) {
3751
+ const checklistData = {
3752
+ type: "toilet",
3753
+ site: value.site,
3754
+ parentChecklist: value.parentChecklist,
3755
+ area: toiletLocation._id,
3756
+ name: toiletLocation.name,
3757
+ createdBy: value.createdBy
3758
+ };
3759
+ const insertedId = await _createAreaChecklist(checklistData);
3760
+ results.push(insertedId);
3761
+ }
3762
+ import_node_server_utils22.logger.info(
3763
+ `Created ${results.length} toilet area checklists for site: ${value.site}`
3764
+ );
3765
+ }
3766
+ return results;
3767
+ } catch (error) {
3768
+ import_node_server_utils22.logger.error(`Error generating area checklists:`, error);
3769
+ throw error;
3770
+ }
3771
+ }
3772
+ return { createAreaChecklist };
3773
+ }
3774
+
3775
+ // src/controllers/hygiene-area-checklist.controller.ts
3776
+ function useAreaChecklistController() {
3777
+ const {
3778
+ getAllAreaChecklist: _getAllAreaChecklist,
3779
+ getAreaChecklistHistory: _getAreaChecklistHistory,
3780
+ getAreaChecklistHistoryDetails: _getAreaChecklistHistoryDetails,
3781
+ acceptAreaChecklist: _acceptAreaChecklist,
3782
+ attachImageAreaChecklist: _attachImageAreaChecklist,
3783
+ submitAreaChecklist: _submitAreaChecklist,
3784
+ completeAreaChecklist: _completeAreaChecklist
3785
+ } = useAreaChecklistRepo();
3786
+ const { createAreaChecklist: _createAreaChecklist } = useAreaChecklistService();
3787
+ async function createAreaChecklist(req, res, next) {
3788
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3789
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
3790
+ {}
3791
+ ) : {};
3792
+ const createdBy = cookies["user"] || "";
3793
+ const payload = { ...req.body, ...req.params, createdBy };
3794
+ const validation = import_joi12.default.object({
3795
+ type: import_joi12.default.string().required().valid(...allowedTypes),
3796
+ site: import_joi12.default.string().hex().required(),
3797
+ parentChecklist: import_joi12.default.string().hex().required(),
3798
+ createdBy: import_joi12.default.string().hex().optional().allow("", null)
3799
+ });
3800
+ const { error } = validation.validate(payload);
3801
+ if (error) {
3802
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3803
+ next(new import_node_server_utils23.BadRequestError(error.message));
3804
+ return;
3805
+ }
3806
+ try {
3807
+ await _createAreaChecklist(payload);
3808
+ res.status(201).json({ message: "Area checklists generated successfully." });
3809
+ return;
3810
+ } catch (error2) {
3811
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3812
+ next(error2);
3813
+ return;
3814
+ }
3815
+ }
3816
+ async function getAllAreaChecklist(req, res, next) {
3817
+ const query = { ...req.query, ...req.params };
3818
+ const validation = import_joi12.default.object({
3819
+ page: import_joi12.default.number().min(1).optional().allow("", null),
3820
+ limit: import_joi12.default.number().min(1).optional().allow("", null),
3821
+ search: import_joi12.default.string().optional().allow("", null),
3822
+ site: import_joi12.default.string().hex().required(),
3823
+ type: import_joi12.default.string().valid(...allowedTypes).required(),
3824
+ parentChecklist: import_joi12.default.string().hex().required()
3825
+ });
3826
+ const { error } = validation.validate(query);
3827
+ if (error) {
3828
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3829
+ next(new import_node_server_utils23.BadRequestError(error.message));
3830
+ return;
3831
+ }
3832
+ const page = parseInt(req.query.page) ?? 1;
3833
+ const limit = parseInt(req.query.limit) ?? 20;
3834
+ const search = req.query.search ?? "";
3835
+ const site = req.params.site ?? "";
3836
+ const type = req.params.type ?? "";
3837
+ const parentChecklist = req.params.parentChecklist ?? "";
3838
+ try {
3839
+ const data = await _getAllAreaChecklist({
3840
+ page,
3841
+ limit,
3842
+ search,
3843
+ site,
3844
+ type,
3845
+ parentChecklist
3846
+ });
3847
+ res.json(data);
3848
+ return;
3849
+ } catch (error2) {
3850
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3851
+ next(error2);
3852
+ return;
3853
+ }
3854
+ }
3855
+ async function getAreaChecklistHistory(req, res, next) {
3856
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3857
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
3858
+ {}
3859
+ ) : {};
3860
+ const user = cookies["user"] || "";
3861
+ const query = { ...req.query, ...req.params, user };
3862
+ const validation = import_joi12.default.object({
3863
+ page: import_joi12.default.number().min(1).optional().allow("", null),
3864
+ limit: import_joi12.default.number().min(1).optional().allow("", null),
3865
+ search: import_joi12.default.string().optional().allow("", null),
3866
+ site: import_joi12.default.string().hex().required(),
3867
+ type: import_joi12.default.string().valid(...allowedTypes).required(),
3868
+ parentChecklist: import_joi12.default.string().hex().required(),
3869
+ status: import_joi12.default.string().allow("", null, ...allowedStatus),
3870
+ createdAt: import_joi12.default.alternatives().try(import_joi12.default.date(), import_joi12.default.string()).optional().allow("", null),
3871
+ user: import_joi12.default.string().hex().optional().allow("", null)
3872
+ });
3873
+ const { error } = validation.validate(query);
3874
+ if (error) {
3875
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3876
+ next(new import_node_server_utils23.BadRequestError(error.message));
3877
+ return;
3878
+ }
3879
+ const page = parseInt(req.query.page) ?? 1;
3880
+ const limit = parseInt(req.query.limit) ?? 20;
3881
+ const search = req.query.search ?? "";
3882
+ const site = req.params.site ?? "";
3883
+ const type = req.params.type ?? "";
3884
+ const parentChecklist = req.params.parentChecklist ?? "";
3885
+ const status = req.query.status ?? "";
3886
+ const createdAt = req.query.createdAt ?? "";
3887
+ try {
3888
+ const data = await _getAreaChecklistHistory({
3889
+ page,
3890
+ limit,
3891
+ search,
3892
+ site,
3893
+ type,
3894
+ parentChecklist,
3895
+ status,
3896
+ createdAt,
3897
+ user
3898
+ });
3899
+ res.json(data);
3900
+ return;
3901
+ } catch (error2) {
3902
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3903
+ next(error2);
3904
+ return;
3905
+ }
3906
+ }
3907
+ async function getAreaChecklistHistoryDetails(req, res, next) {
3908
+ const validation = import_joi12.default.string().hex().required();
3909
+ const _id = req.params.id;
3910
+ const { error } = validation.validate(_id);
3911
+ if (error) {
3912
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3913
+ next(new import_node_server_utils23.BadRequestError(error.message));
3914
+ return;
3915
+ }
3916
+ try {
3917
+ const data = await _getAreaChecklistHistoryDetails(_id);
3918
+ res.json(data);
3919
+ return;
3920
+ } catch (error2) {
3921
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3922
+ next(error2);
3923
+ return;
3924
+ }
3925
+ }
3926
+ async function acceptAreaChecklist(req, res, next) {
3927
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
3928
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
3929
+ {}
3930
+ ) : {};
3931
+ const acceptedBy = cookies["user"] || "";
3932
+ const payload = { id: req.params.id, acceptedBy };
3933
+ const validation = import_joi12.default.object({
3934
+ id: import_joi12.default.string().hex().required(),
3935
+ acceptedBy: import_joi12.default.string().hex().required()
3936
+ });
3937
+ const { error } = validation.validate(payload);
3938
+ if (error) {
3939
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3940
+ next(new import_node_server_utils23.BadRequestError(error.message));
3941
+ return;
3942
+ }
3943
+ try {
3944
+ await _acceptAreaChecklist(payload.id, payload.acceptedBy);
3945
+ res.json({ message: "Area checklist updated successfully." });
3946
+ return;
3947
+ } catch (error2) {
3948
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3949
+ next(error2);
3950
+ return;
3951
+ }
3952
+ }
3953
+ async function attachImageAreaChecklist(req, res, next) {
3954
+ const payload = { id: req.params.id, attachments: req.body.attachments };
3955
+ const validation = import_joi12.default.object({
3956
+ id: import_joi12.default.string().hex().required(),
3957
+ attachments: import_joi12.default.array().items(import_joi12.default.string()).optional()
3958
+ });
3959
+ const { error } = validation.validate(payload);
3960
+ if (error) {
3961
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3962
+ next(new import_node_server_utils23.BadRequestError(error.message));
3963
+ return;
3964
+ }
3965
+ try {
3966
+ await _attachImageAreaChecklist(payload.id, payload.attachments);
3967
+ res.json({ message: "Area checklist attachments updated successfully." });
3968
+ return;
3969
+ } catch (error2) {
3970
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3971
+ next(error2);
3972
+ return;
3973
+ }
3974
+ }
3975
+ async function submitAreaChecklist(req, res, next) {
3976
+ const payload = { id: req.params.id, signature: req.body.signature };
3977
+ const validation = import_joi12.default.object({
3978
+ id: import_joi12.default.string().hex().required(),
3979
+ signature: import_joi12.default.string().required()
3980
+ });
3981
+ const { error } = validation.validate(payload);
3982
+ if (error) {
3983
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
3984
+ next(new import_node_server_utils23.BadRequestError(error.message));
3985
+ return;
3986
+ }
3987
+ try {
3988
+ await _submitAreaChecklist(payload.id, payload.signature);
3989
+ res.json({ message: "Area checklist submitted successfully." });
3990
+ return;
3991
+ } catch (error2) {
3992
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
3993
+ next(error2);
3994
+ return;
3995
+ }
3996
+ }
3997
+ async function completeAreaChecklist(req, res, next) {
3998
+ const payload = { id: req.params.id, signature: req.body.signature };
3999
+ const validation = import_joi12.default.object({
4000
+ id: import_joi12.default.string().hex().required(),
4001
+ signature: import_joi12.default.string().required()
4002
+ });
4003
+ const { error } = validation.validate(payload);
4004
+ if (error) {
4005
+ import_node_server_utils23.logger.log({ level: "error", message: error.message });
4006
+ next(new import_node_server_utils23.BadRequestError(error.message));
4007
+ return;
4008
+ }
4009
+ try {
4010
+ await _completeAreaChecklist(payload.id, payload.signature);
4011
+ res.json({ message: "Area checklist completed successfully." });
4012
+ return;
4013
+ } catch (error2) {
4014
+ import_node_server_utils23.logger.log({ level: "error", message: error2.message });
4015
+ next(error2);
4016
+ return;
4017
+ }
4018
+ }
4019
+ return {
4020
+ createAreaChecklist,
4021
+ getAllAreaChecklist,
4022
+ getAreaChecklistHistory,
4023
+ getAreaChecklistHistoryDetails,
4024
+ acceptAreaChecklist,
4025
+ attachImageAreaChecklist,
4026
+ submitAreaChecklist,
4027
+ completeAreaChecklist
4028
+ };
4029
+ }
4030
+
4031
+ // src/models/hygiene-unit-checklist.model.ts
4032
+ var import_node_server_utils24 = require("@iservice365/node-server-utils");
4033
+ var import_joi13 = __toESM(require("joi"));
4034
+ var import_mongodb13 = require("mongodb");
4035
+ var unitChecklistSchema = import_joi13.default.object({
4036
+ site: import_joi13.default.string().hex().required(),
4037
+ type: import_joi13.default.string().valid(...allowedTypes).required(),
4038
+ parentChecklist: import_joi13.default.string().hex().required(),
4039
+ areaChecklist: import_joi13.default.string().hex().required(),
4040
+ unit: import_joi13.default.string().hex().required(),
4041
+ name: import_joi13.default.string().optional().allow("", null),
4042
+ createdBy: import_joi13.default.string().hex().optional().allow("", null)
4043
+ });
4044
+ function MUnitChecklist(value) {
4045
+ const { error } = unitChecklistSchema.validate(value);
4046
+ if (error) {
4047
+ import_node_server_utils24.logger.info(`Hygiene Checklist Unit Model: ${error.message}`);
4048
+ throw new import_node_server_utils24.BadRequestError(error.message);
4049
+ }
4050
+ if (value.site) {
4051
+ try {
4052
+ value.site = new import_mongodb13.ObjectId(value.site);
4053
+ } catch (error2) {
4054
+ throw new import_node_server_utils24.BadRequestError("Invalid site ID format.");
4055
+ }
4056
+ }
4057
+ if (value.parentChecklist) {
4058
+ try {
4059
+ value.parentChecklist = new import_mongodb13.ObjectId(value.parentChecklist);
4060
+ } catch (error2) {
4061
+ throw new import_node_server_utils24.BadRequestError("Invalid parent checklist ID format.");
4062
+ }
4063
+ }
4064
+ if (value.areaChecklist) {
4065
+ try {
4066
+ value.areaChecklist = new import_mongodb13.ObjectId(value.areaChecklist);
4067
+ } catch (error2) {
4068
+ throw new import_node_server_utils24.BadRequestError("Invalid area checklist ID format.");
4069
+ }
4070
+ }
4071
+ if (value.unit) {
4072
+ try {
4073
+ value.unit = new import_mongodb13.ObjectId(value.unit);
4074
+ } catch (error2) {
4075
+ throw new import_node_server_utils24.BadRequestError("Invalid unit ID format.");
4076
+ }
4077
+ }
4078
+ if (value.createdBy) {
4079
+ try {
4080
+ value.createdBy = new import_mongodb13.ObjectId(value.createdBy);
4081
+ } catch (error2) {
4082
+ throw new import_node_server_utils24.BadRequestError("Invalid createdBy ID format.");
4083
+ }
4084
+ }
4085
+ return {
4086
+ site: value.site,
4087
+ type: value.type,
4088
+ parentChecklist: value.parentChecklist,
4089
+ areaChecklist: value.areaChecklist,
4090
+ unit: value.unit,
4091
+ name: value.name ?? "",
4092
+ approve: false,
4093
+ reject: false,
4094
+ createdBy: value.createdBy ?? "",
4095
+ createdAt: /* @__PURE__ */ new Date(),
4096
+ updatedAt: value.updatedAt ?? ""
4097
+ };
4098
+ }
4099
+
4100
+ // src/repositories/hygiene-unit-checklist.repository.ts
4101
+ var import_node_server_utils25 = require("@iservice365/node-server-utils");
4102
+ var import_mongodb14 = require("mongodb");
4103
+ function useUnitChecklistRepo() {
4104
+ const db = import_node_server_utils25.useAtlas.getDb();
4105
+ if (!db) {
4106
+ throw new import_node_server_utils25.InternalServerError("Unable to connect to server.");
4107
+ }
4108
+ const namespace_collection = "hygiene-checklist.units";
4109
+ const collection = db.collection(namespace_collection);
4110
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils25.useCache)(namespace_collection);
4111
+ async function createIndex() {
4112
+ try {
4113
+ await collection.createIndexes([
4114
+ { key: { site: 1 } },
4115
+ { key: { type: 1 } },
4116
+ { key: { parentChecklist: 1 } },
4117
+ { key: { areaChecklist: 1 } },
4118
+ { key: { "metadata.workOrder.category": 1 } },
4119
+ { key: { "metadata.workOrder.createdBy": 1 } },
4120
+ { key: { "metadata.workOrder.serviceProvider": 1 } },
4121
+ { key: { "metadata.workOrder.organization": 1 } },
4122
+ { key: { "metadata.workOrder.site": 1 } }
4123
+ ]);
4124
+ } catch (error) {
4125
+ throw new import_node_server_utils25.InternalServerError(
4126
+ "Failed to create index on hygiene unit checklist."
4127
+ );
4128
+ }
4129
+ }
4130
+ async function createTextIndex() {
4131
+ try {
4132
+ await collection.createIndex({ name: "text" });
4133
+ } catch (error) {
4134
+ throw new import_node_server_utils25.InternalServerError(
4135
+ "Failed to create text index on hygiene unit checklist."
4136
+ );
4137
+ }
4138
+ }
4139
+ async function createUnitChecklist(value, session) {
4140
+ try {
4141
+ value = MUnitChecklist(value);
4142
+ const res = await collection.insertOne(value, { session });
4143
+ delNamespace().then(() => {
4144
+ import_node_server_utils25.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
4145
+ }).catch((err) => {
4146
+ import_node_server_utils25.logger.error(
4147
+ `Failed to clear cache for namespace: ${namespace_collection}`,
4148
+ err
4149
+ );
4150
+ });
4151
+ return res.insertedId;
4152
+ } catch (error) {
4153
+ throw error;
4154
+ }
4155
+ }
4156
+ async function getAllUnitChecklist({
4157
+ page = 1,
4158
+ limit = 10,
4159
+ search = "",
4160
+ site,
4161
+ type,
4162
+ parentChecklist,
4163
+ areaChecklist
4164
+ }) {
4165
+ page = page > 0 ? page - 1 : 0;
4166
+ const query = { type };
4167
+ const cacheOptions = {
4168
+ page,
4169
+ limit
4170
+ };
4171
+ try {
4172
+ query.site = new import_mongodb14.ObjectId(site);
4173
+ cacheOptions.site = site.toString();
4174
+ } catch (error) {
4175
+ throw new import_node_server_utils25.BadRequestError("Invalid site ID format.");
4176
+ }
4177
+ try {
4178
+ query.parentChecklist = new import_mongodb14.ObjectId(parentChecklist);
4179
+ cacheOptions.parentChecklist = parentChecklist.toString();
4180
+ } catch (error) {
4181
+ throw new import_node_server_utils25.BadRequestError("Invalid parent checklist ID format.");
4182
+ }
4183
+ try {
4184
+ query.areaChecklist = new import_mongodb14.ObjectId(areaChecklist);
4185
+ cacheOptions.areaChecklist = areaChecklist.toString();
4186
+ } catch (error) {
4187
+ throw new import_node_server_utils25.BadRequestError("Invalid area checklist ID format.");
4188
+ }
4189
+ if (search) {
4190
+ query.$text = { $search: search };
4191
+ cacheOptions.search = search;
4192
+ }
4193
+ const cacheKey = (0, import_node_server_utils25.makeCacheKey)(namespace_collection, cacheOptions);
4194
+ const cachedData = await getCache(cacheKey);
4195
+ if (cachedData) {
4196
+ import_node_server_utils25.logger.info(`Cache hit for key: ${cacheKey}`);
4197
+ return cachedData;
4198
+ }
4199
+ try {
4200
+ const areaAttachmentsResult = await collection.aggregate([
4201
+ { $match: query },
4202
+ {
4203
+ $lookup: {
4204
+ from: "hygiene-checklist.areas",
4205
+ localField: "areaChecklist",
4206
+ foreignField: "_id",
4207
+ pipeline: [
4208
+ { $project: { attachments: "$metadata.attachments" } }
4209
+ ],
4210
+ as: "areaChecklistData"
4211
+ }
4212
+ },
4213
+ {
4214
+ $unwind: {
4215
+ path: "$areaChecklistData",
4216
+ preserveNullAndEmptyArrays: true
4217
+ }
4218
+ },
4219
+ {
4220
+ $group: {
4221
+ _id: null,
4222
+ attachments: { $first: "$areaChecklistData.attachments" }
4223
+ }
4224
+ }
4225
+ ]).toArray();
4226
+ const areaAttachments = areaAttachmentsResult.length > 0 ? areaAttachmentsResult[0].attachments || [] : [];
4227
+ const pipeline = [
4228
+ { $match: query },
4229
+ {
4230
+ $lookup: {
4231
+ from: "organizations",
4232
+ localField: "metadata.workOrder.category",
4233
+ foreignField: "_id",
4234
+ pipeline: [{ $project: { nature: 1 } }],
4235
+ as: "categoryData"
4236
+ }
4237
+ },
4238
+ {
4239
+ $lookup: {
4240
+ from: "users",
4241
+ localField: "metadata.workOrder.createdBy",
4242
+ foreignField: "_id",
4243
+ pipeline: [{ $project: { name: 1 } }],
4244
+ as: "createdByData"
4245
+ }
4246
+ },
4247
+ {
4248
+ $lookup: {
4249
+ from: "service-providers",
4250
+ localField: "metadata.workOrder.serviceProvider",
4251
+ foreignField: "_id",
4252
+ pipeline: [{ $project: { name: 1 } }],
4253
+ as: "serviceProviderData"
4254
+ }
4255
+ },
4256
+ {
4257
+ $lookup: {
4258
+ from: "organizations",
4259
+ localField: "metadata.workOrder.organization",
4260
+ foreignField: "_id",
4261
+ pipeline: [{ $project: { name: 1 } }],
4262
+ as: "organizationData"
4263
+ }
4264
+ },
4265
+ {
4266
+ $lookup: {
4267
+ from: "sites",
4268
+ localField: "metadata.workOrder.site",
4269
+ foreignField: "_id",
4270
+ pipeline: [{ $project: { name: 1 } }],
4271
+ as: "siteData"
4272
+ }
4273
+ },
4274
+ {
4275
+ $addFields: {
4276
+ "metadata.workOrder.categoryName": {
4277
+ $arrayElemAt: ["$categoryData.nature", 0]
4278
+ },
4279
+ "metadata.workOrder.createdByName": {
4280
+ $arrayElemAt: ["$createdByData.name", 0]
4281
+ },
4282
+ "metadata.workOrder.serviceProviderName": {
4283
+ $arrayElemAt: ["$serviceProviderData.name", 0]
4284
+ },
4285
+ "metadata.workOrder.organizationName": {
4286
+ $arrayElemAt: ["$organizationData.name", 0]
4287
+ },
4288
+ "metadata.workOrder.siteName": {
4289
+ $arrayElemAt: ["$siteData.name", 0]
4290
+ }
4291
+ }
4292
+ },
4293
+ {
4294
+ $project: {
4295
+ name: 1,
4296
+ remarks: "$metadata.remarks",
4297
+ attachments: "$metadata.attachments",
4298
+ workOrder: "$metadata.workOrder",
4299
+ approve: { $ifNull: ["$approve", null] },
4300
+ reject: { $ifNull: ["$reject", null] }
4301
+ }
4302
+ },
4303
+ { $sort: { _id: -1 } },
4304
+ { $skip: page * limit },
4305
+ { $limit: limit }
4306
+ ];
4307
+ const items = await collection.aggregate(pipeline).toArray();
4308
+ const length = await collection.countDocuments(query);
4309
+ const paginatedData = (0, import_node_server_utils25.paginate)(items, page, limit, length);
4310
+ const data = {
4311
+ attachments: areaAttachments,
4312
+ ...paginatedData
4313
+ };
4314
+ setCache(cacheKey, data, 15 * 60).then(() => {
4315
+ import_node_server_utils25.logger.info(`Cache set for key: ${cacheKey}`);
4316
+ }).catch((err) => {
4317
+ import_node_server_utils25.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
4318
+ });
4319
+ return data;
4320
+ } catch (error) {
4321
+ throw error;
4322
+ }
4323
+ }
4324
+ async function getUnitChecklistById(_id) {
4325
+ try {
4326
+ _id = new import_mongodb14.ObjectId(_id);
4327
+ } catch (error) {
4328
+ throw new import_node_server_utils25.BadRequestError("Invalid unit checklist ID format.");
4329
+ }
4330
+ try {
4331
+ return await collection.findOne({ _id });
4332
+ } catch (error) {
4333
+ throw error;
4334
+ }
4335
+ }
4336
+ async function updateUnitChecklist(_id, value, session) {
4337
+ try {
4338
+ _id = new import_mongodb14.ObjectId(_id);
4339
+ } catch (error) {
4340
+ throw new import_node_server_utils25.BadRequestError("Invalid unit checklist ID format.");
4341
+ }
4342
+ if (value.checkedBy && typeof value.checkedBy === "string") {
4343
+ try {
4344
+ value.checkedBy = new import_mongodb14.ObjectId(value.checkedBy);
4345
+ } catch (error) {
4346
+ throw new import_node_server_utils25.BadRequestError("Invalid checkedBy ID format.");
4347
+ }
4348
+ }
4349
+ if ("metadata" in value && value.metadata?.workOrder) {
4350
+ const workOrder = value.metadata.workOrder;
4351
+ if (workOrder.category && typeof workOrder.category === "string") {
4352
+ try {
4353
+ workOrder.category = new import_mongodb14.ObjectId(workOrder.category);
4354
+ } catch (error) {
4355
+ throw new import_node_server_utils25.BadRequestError("Invalid category ID format.");
4356
+ }
4357
+ }
4358
+ if (workOrder.createdBy && typeof workOrder.createdBy === "string") {
4359
+ try {
4360
+ workOrder.createdBy = new import_mongodb14.ObjectId(workOrder.createdBy);
4361
+ } catch (error) {
4362
+ throw new import_node_server_utils25.BadRequestError("Invalid createdBy ID format.");
4363
+ }
4364
+ }
4365
+ if (workOrder.serviceProvider && typeof workOrder.serviceProvider === "string") {
4366
+ try {
4367
+ workOrder.serviceProvider = new import_mongodb14.ObjectId(workOrder.serviceProvider);
4368
+ } catch (error) {
4369
+ throw new import_node_server_utils25.BadRequestError("Invalid serviceProvider ID format.");
4370
+ }
4371
+ }
4372
+ if (workOrder.organization && typeof workOrder.organization === "string") {
4373
+ try {
4374
+ workOrder.organization = new import_mongodb14.ObjectId(workOrder.organization);
4375
+ } catch (error) {
4376
+ throw new import_node_server_utils25.BadRequestError("Invalid organization ID format.");
4377
+ }
4378
+ }
4379
+ if (workOrder.site && typeof workOrder.site === "string") {
4380
+ try {
4381
+ workOrder.site = new import_mongodb14.ObjectId(workOrder.site);
4382
+ } catch (error) {
4383
+ throw new import_node_server_utils25.BadRequestError("Invalid site ID format.");
4384
+ }
4385
+ }
4386
+ }
4387
+ try {
4388
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
4389
+ const res = await collection.updateOne(
4390
+ { _id },
4391
+ { $set: updateValue },
4392
+ { session }
4393
+ );
4394
+ if (res.modifiedCount === 0) {
4395
+ throw new import_node_server_utils25.InternalServerError("Unable to update unit checklist.");
4396
+ }
4397
+ delNamespace().then(() => {
4398
+ import_node_server_utils25.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
4399
+ }).catch((err) => {
4400
+ import_node_server_utils25.logger.error(
4401
+ `Failed to clear cache for namespace: ${namespace_collection}`,
4402
+ err
4403
+ );
4404
+ });
4405
+ return res.modifiedCount;
4406
+ } catch (error) {
4407
+ throw error;
4408
+ }
4409
+ }
4410
+ return {
4411
+ createIndex,
4412
+ createTextIndex,
4413
+ createUnitChecklist,
4414
+ getAllUnitChecklist,
4415
+ getUnitChecklistById,
4416
+ updateUnitChecklist
4417
+ };
4418
+ }
4419
+
4420
+ // src/controllers/hygiene-unit-checklist.controller.ts
4421
+ var import_node_server_utils27 = require("@iservice365/node-server-utils");
4422
+ var import_joi14 = __toESM(require("joi"));
4423
+
4424
+ // src/services/hygiene-unit-checklist.service.ts
4425
+ var import_node_server_utils26 = require("@iservice365/node-server-utils");
4426
+ var import_core = require("@iservice365/core");
4427
+ function useUnitChecklistService() {
4428
+ const {
4429
+ getUnitChecklistById: _getUnitChecklistById,
4430
+ updateUnitChecklist: _updateUnitChecklist
4431
+ } = useUnitChecklistRepo();
4432
+ const { getSiteById } = (0, import_core.useSiteRepo)();
4433
+ const { createWorkOrder } = (0, import_core.useWorkOrderService)();
4434
+ async function approveUnitChecklist(id, value) {
4435
+ try {
4436
+ value.approve = true;
4437
+ value.reject = false;
4438
+ const result = await _updateUnitChecklist(id, value);
4439
+ return result;
4440
+ } catch (error) {
4441
+ import_node_server_utils26.logger.error(`Error updating unit checklist with id ${id}:`, error);
4442
+ throw error;
4443
+ }
4444
+ }
4445
+ async function rejectUnitChecklist(id, value, fullHost) {
4446
+ try {
4447
+ value.reject = true;
4448
+ value.approve = false;
4449
+ if (value.metadata?.workOrder) {
4450
+ const existingChecklist = await _getUnitChecklistById(id);
4451
+ if (!existingChecklist)
4452
+ throw new import_node_server_utils26.NotFoundError("Unit checklist not found.");
4453
+ const site = await getSiteById(
4454
+ existingChecklist.site
4455
+ );
4456
+ if (!site)
4457
+ throw new import_node_server_utils26.NotFoundError("Site not found.");
4458
+ const workOrderData = {
4459
+ ...value.metadata.workOrder,
4460
+ attachments: value.metadata.attachments,
4461
+ createdBy: value.checkedBy,
4462
+ organization: site.orgId,
4463
+ site: site._id
4464
+ };
4465
+ const workOrder = await createWorkOrder(
4466
+ workOrderData,
4467
+ fullHost
4468
+ );
4469
+ if (!workOrder)
4470
+ throw new import_node_server_utils26.NotFoundError("Failed to create work order.");
4471
+ }
4472
+ const result = await _updateUnitChecklist(id, value);
4473
+ return result;
4474
+ } catch (error) {
4475
+ import_node_server_utils26.logger.error(`Error updating unit checklist with id ${id}:`, error);
4476
+ throw error;
4477
+ }
4478
+ }
4479
+ return {
4480
+ approveUnitChecklist,
4481
+ rejectUnitChecklist
4482
+ };
4483
+ }
4484
+
4485
+ // src/controllers/hygiene-unit-checklist.controller.ts
4486
+ var import_core2 = require("@iservice365/core");
4487
+ function useUnitChecklistController() {
4488
+ const {
4489
+ createUnitChecklist: _createUnitChecklist,
4490
+ getAllUnitChecklist: _getAllUnitChecklist
4491
+ } = useUnitChecklistRepo();
4492
+ const {
4493
+ approveUnitChecklist: _approveUnitChecklist,
4494
+ rejectUnitChecklist: _rejectUnitChecklist
4495
+ } = useUnitChecklistService();
4496
+ async function createUnitChecklist(req, res, next) {
4497
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
4498
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
4499
+ {}
4500
+ ) : {};
4501
+ const createdBy = cookies["user"] || "";
4502
+ const payload = { ...req.body, ...req.params, createdBy };
4503
+ const { error } = unitChecklistSchema.validate(payload);
4504
+ if (error) {
4505
+ import_node_server_utils27.logger.log({ level: "error", message: error.message });
4506
+ next(new import_node_server_utils27.BadRequestError(error.message));
4507
+ return;
4508
+ }
4509
+ try {
4510
+ const id = await _createUnitChecklist(payload);
4511
+ res.status(201).json({ message: "Unit checklist created successfully.", id });
4512
+ return;
4513
+ } catch (error2) {
4514
+ import_node_server_utils27.logger.log({ level: "error", message: error2.message });
4515
+ next(error2);
4516
+ return;
4517
+ }
4518
+ }
4519
+ async function getAllUnitChecklist(req, res, next) {
4520
+ const query = { ...req.query, ...req.params };
4521
+ const validation = import_joi14.default.object({
4522
+ page: import_joi14.default.number().min(1).optional().allow("", null),
4523
+ limit: import_joi14.default.number().min(1).optional().allow("", null),
4524
+ search: import_joi14.default.string().optional().allow("", null),
4525
+ site: import_joi14.default.string().hex().required(),
4526
+ type: import_joi14.default.string().valid(...allowedTypes).required(),
4527
+ parentChecklist: import_joi14.default.string().hex().required(),
4528
+ areaChecklist: import_joi14.default.string().hex().required()
4529
+ });
4530
+ const { error } = validation.validate(query);
4531
+ if (error) {
4532
+ import_node_server_utils27.logger.log({ level: "error", message: error.message });
4533
+ next(new import_node_server_utils27.BadRequestError(error.message));
4534
+ return;
4535
+ }
4536
+ const page = parseInt(req.query.page) ?? 1;
4537
+ const limit = parseInt(req.query.limit) ?? 20;
4538
+ const search = req.query.search ?? "";
4539
+ const site = req.params.site ?? "";
4540
+ const type = req.params.type ?? "";
4541
+ const parentChecklist = req.params.parentChecklist ?? "";
4542
+ const areaChecklist = req.params.areaChecklist ?? "";
4543
+ try {
4544
+ const data = await _getAllUnitChecklist({
4545
+ page,
4546
+ limit,
4547
+ search,
4548
+ site,
4549
+ type,
4550
+ parentChecklist,
4551
+ areaChecklist
4552
+ });
4553
+ res.json(data);
4554
+ return;
4555
+ } catch (error2) {
4556
+ import_node_server_utils27.logger.log({ level: "error", message: error2.message });
4557
+ next(error2);
4558
+ return;
4559
+ }
4560
+ }
4561
+ async function approveUnitChecklist(req, res, next) {
4562
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
4563
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
4564
+ {}
4565
+ ) : {};
4566
+ const checkedBy = cookies["user"] || "";
4567
+ const payload = { id: req.params.id, checkedBy };
4568
+ const validation = import_joi14.default.object({
4569
+ id: import_joi14.default.string().hex().required(),
4570
+ checkedBy: import_joi14.default.string().hex().required()
4571
+ });
4572
+ const { error } = validation.validate(payload);
4573
+ if (error) {
4574
+ import_node_server_utils27.logger.log({ level: "error", message: error.message });
4575
+ next(new import_node_server_utils27.BadRequestError(error.message));
4576
+ return;
4577
+ }
4578
+ try {
4579
+ const { id, ...value } = payload;
4580
+ await _approveUnitChecklist(id, value);
4581
+ res.json({ message: "Unit checklist approved successfully." });
4582
+ return;
4583
+ } catch (error2) {
4584
+ import_node_server_utils27.logger.log({ level: "error", message: error2.message });
4585
+ next(error2);
4586
+ return;
4587
+ }
4588
+ }
4589
+ async function rejectUnitChecklist(req, res, next) {
4590
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
4591
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
4592
+ {}
4593
+ ) : {};
4594
+ const checkedBy = cookies["user"] || "";
4595
+ if (req.body.workOrder) {
4596
+ req.body.workOrder.createdBy = checkedBy;
4597
+ }
4598
+ const payload = { id: req.params.id, checkedBy, ...req.body };
4599
+ const validation = import_joi14.default.object({
4600
+ id: import_joi14.default.string().hex().required(),
4601
+ attachments: import_joi14.default.array().items(import_joi14.default.string()).optional(),
4602
+ remarks: import_joi14.default.string().required(),
4603
+ workOrder: import_core2.workOrderSchema.optional(),
4604
+ checkedBy: import_joi14.default.string().hex().required()
4605
+ });
4606
+ const { error } = validation.validate(payload);
4607
+ if (error) {
4608
+ import_node_server_utils27.logger.log({ level: "error", message: error.message });
4609
+ next(new import_node_server_utils27.BadRequestError(error.message));
4610
+ return;
4611
+ }
4612
+ try {
4613
+ const { id, attachments, remarks, workOrder, ...value } = payload;
4614
+ value.metadata = {
4615
+ attachments: attachments || [],
4616
+ remarks: remarks || "",
4617
+ workOrder
4618
+ };
4619
+ if (value.metadata.workOrder) {
4620
+ if (value.metadata.workOrder.category) {
4621
+ value.metadata.workOrder.category = value.metadata.workOrder.category;
4622
+ }
4623
+ if (value.metadata.workOrder.serviceProvider) {
4624
+ value.metadata.workOrder.serviceProvider = value.metadata.workOrder.serviceProvider;
4625
+ }
4626
+ value.metadata.workOrder.createdBy = checkedBy;
4627
+ }
4628
+ const fullHost = req.headers.origin;
4629
+ await _rejectUnitChecklist(id, value, fullHost);
4630
+ res.json({ message: "Unit checklist rejected successfully." });
4631
+ return;
4632
+ } catch (error2) {
4633
+ import_node_server_utils27.logger.log({ level: "error", message: error2.message });
4634
+ next(error2);
4635
+ return;
4636
+ }
4637
+ }
4638
+ return {
4639
+ createUnitChecklist,
4640
+ getAllUnitChecklist,
4641
+ approveUnitChecklist,
4642
+ rejectUnitChecklist
4643
+ };
4644
+ }
4645
+ // Annotate the CommonJS export names for ESM import in node:
4646
+ 0 && (module.exports = {
4647
+ MArea,
4648
+ MAreaChecklist,
4649
+ MParentChecklist,
4650
+ MScheduleTaskArea,
4651
+ MToiletLocation,
4652
+ MUnit,
4653
+ MUnitChecklist,
4654
+ allowedStatus,
4655
+ allowedTypes,
4656
+ areaChecklistSchema,
4657
+ areaSchema,
4658
+ parentChecklistSchema,
4659
+ scheduleTaskAreaSchema,
4660
+ toiletLocationSchema,
4661
+ unitChecklistSchema,
4662
+ unitSchema,
4663
+ useAreaChecklistController,
4664
+ useAreaChecklistRepo,
4665
+ useAreaController,
4666
+ useAreaRepository,
4667
+ useAreaService,
4668
+ useParentChecklistController,
4669
+ useParentChecklistRepo,
4670
+ useScheduleTaskAreaController,
4671
+ useScheduleTaskAreaRepository,
4672
+ useScheduleTaskAreaService,
4673
+ useToiletLocationController,
4674
+ useToiletLocationRepository,
4675
+ useToiletLocationService,
4676
+ useUnitChecklistController,
4677
+ useUnitChecklistRepo,
4678
+ useUnitController,
4679
+ useUnitRepository,
4680
+ useUnitService
4681
+ });
2
4682
  //# sourceMappingURL=index.js.map