@iservice365/module-hygiene 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +193 -21
- package/dist/index.js +2183 -284
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2245 -332
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1436,42 +1436,66 @@ function useToiletLocationController() {
|
|
|
1436
1436
|
}
|
|
1437
1437
|
|
|
1438
1438
|
// src/models/hygiene-parent-checklist.model.ts
|
|
1439
|
-
import { BadRequestError as BadRequestError9 } from "@iservice365/node-server-utils";
|
|
1439
|
+
import { BadRequestError as BadRequestError9, logger as logger8 } from "@iservice365/node-server-utils";
|
|
1440
1440
|
import Joi5 from "joi";
|
|
1441
|
+
import { ObjectId as ObjectId5 } from "mongodb";
|
|
1442
|
+
var allowedTypes = ["cleaner", "toilet"];
|
|
1443
|
+
var allowedStatus = [
|
|
1444
|
+
"To Do",
|
|
1445
|
+
"Pending",
|
|
1446
|
+
"In Progress",
|
|
1447
|
+
"Completed",
|
|
1448
|
+
"Expired"
|
|
1449
|
+
];
|
|
1441
1450
|
var parentChecklistSchema = Joi5.object({
|
|
1442
1451
|
date: Joi5.date().required(),
|
|
1443
1452
|
status: Joi5.array().items(
|
|
1444
1453
|
Joi5.object({
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
completedAt: Joi5.date().required(),
|
|
1448
|
-
type: Joi5.string().required()
|
|
1454
|
+
type: Joi5.string().required().valid(...allowedTypes),
|
|
1455
|
+
site: Joi5.string().hex().required()
|
|
1449
1456
|
})
|
|
1450
|
-
).optional()
|
|
1451
|
-
updatedAt: Joi5.date().optional().allow("", null)
|
|
1457
|
+
).optional()
|
|
1452
1458
|
});
|
|
1453
1459
|
function MParentChecklist(value) {
|
|
1454
1460
|
const { error } = parentChecklistSchema.validate(value);
|
|
1455
1461
|
if (error) {
|
|
1462
|
+
logger8.info(`Hygiene Parent Checklist Model: ${error.message}`);
|
|
1456
1463
|
throw new BadRequestError9(error.message);
|
|
1457
1464
|
}
|
|
1465
|
+
if (value.status && Array.isArray(value.status)) {
|
|
1466
|
+
value.status = value.status.map((item) => {
|
|
1467
|
+
try {
|
|
1468
|
+
return {
|
|
1469
|
+
...item,
|
|
1470
|
+
site: new ObjectId5(item.site),
|
|
1471
|
+
status: item.status || "To Do",
|
|
1472
|
+
completedAt: item.completedAt || ""
|
|
1473
|
+
};
|
|
1474
|
+
} catch (error2) {
|
|
1475
|
+
throw new BadRequestError9(
|
|
1476
|
+
`Invalid status site ID format: ${item.site}`
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1458
1481
|
return {
|
|
1459
|
-
date: value.date,
|
|
1482
|
+
date: new Date(value.date),
|
|
1460
1483
|
status: value.status,
|
|
1461
1484
|
createdAt: /* @__PURE__ */ new Date(),
|
|
1462
|
-
updatedAt: value.updatedAt ?? ""
|
|
1463
|
-
deletedAt: value.deletedAt ?? ""
|
|
1485
|
+
updatedAt: value.updatedAt ?? ""
|
|
1464
1486
|
};
|
|
1465
1487
|
}
|
|
1466
1488
|
|
|
1467
1489
|
// src/repositories/hygiene-parent-checklist.repository.ts
|
|
1490
|
+
import { ObjectId as ObjectId6 } from "mongodb";
|
|
1468
1491
|
import {
|
|
1469
1492
|
useAtlas as useAtlas5,
|
|
1470
1493
|
InternalServerError as InternalServerError3,
|
|
1471
1494
|
paginate as paginate3,
|
|
1472
1495
|
useCache as useCache3,
|
|
1473
|
-
logger as
|
|
1474
|
-
makeCacheKey as makeCacheKey3
|
|
1496
|
+
logger as logger9,
|
|
1497
|
+
makeCacheKey as makeCacheKey3,
|
|
1498
|
+
BadRequestError as BadRequestError10
|
|
1475
1499
|
} from "@iservice365/node-server-utils";
|
|
1476
1500
|
function useParentChecklistRepo() {
|
|
1477
1501
|
const db = useAtlas5.getDb();
|
|
@@ -1480,219 +1504,420 @@ function useParentChecklistRepo() {
|
|
|
1480
1504
|
}
|
|
1481
1505
|
const namespace_collection = "hygiene-parent-checklist";
|
|
1482
1506
|
const collection = db.collection(namespace_collection);
|
|
1483
|
-
const { delNamespace, setCache, getCache
|
|
1484
|
-
async function
|
|
1507
|
+
const { delNamespace, setCache, getCache } = useCache3(namespace_collection);
|
|
1508
|
+
async function createIndex() {
|
|
1485
1509
|
try {
|
|
1486
1510
|
await collection.createIndexes([
|
|
1487
|
-
{ key: { date:
|
|
1511
|
+
{ key: { date: 1 } },
|
|
1512
|
+
{ key: { "status.type": 1, "status.site": 1 } }
|
|
1488
1513
|
]);
|
|
1489
1514
|
} catch (error) {
|
|
1490
|
-
throw new InternalServerError3(
|
|
1515
|
+
throw new InternalServerError3(
|
|
1516
|
+
"Failed to create index on hygiene parent checklist."
|
|
1517
|
+
);
|
|
1491
1518
|
}
|
|
1492
1519
|
}
|
|
1493
|
-
async function
|
|
1520
|
+
async function createParentChecklist(value, session) {
|
|
1494
1521
|
try {
|
|
1495
|
-
const currentDate = /* @__PURE__ */ new Date();
|
|
1522
|
+
const currentDate = value.date ? new Date(value.date) : /* @__PURE__ */ new Date();
|
|
1496
1523
|
const startOfDay = new Date(currentDate);
|
|
1497
|
-
startOfDay.setDate(currentDate.getDate());
|
|
1498
1524
|
startOfDay.setUTCHours(0, 0, 0, 0);
|
|
1499
1525
|
const endOfDay = new Date(currentDate);
|
|
1500
|
-
endOfDay.setDate(currentDate.getDate());
|
|
1501
1526
|
endOfDay.setUTCHours(23, 59, 59, 999);
|
|
1502
|
-
const
|
|
1527
|
+
const existingChecklist = await collection.findOne({
|
|
1503
1528
|
date: {
|
|
1504
1529
|
$gte: startOfDay,
|
|
1505
1530
|
$lte: endOfDay
|
|
1506
1531
|
}
|
|
1507
1532
|
});
|
|
1533
|
+
if (existingChecklist) {
|
|
1534
|
+
const dateStr2 = currentDate.toISOString().split("T")[0];
|
|
1535
|
+
logger9.info(`Parent checklist already exists for today: ${dateStr2}`);
|
|
1536
|
+
return existingChecklist._id;
|
|
1537
|
+
}
|
|
1538
|
+
const processedValue = MParentChecklist(value);
|
|
1539
|
+
const result = await collection.insertOne(processedValue, { session });
|
|
1508
1540
|
delNamespace().then(() => {
|
|
1509
|
-
|
|
1541
|
+
logger9.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1510
1542
|
}).catch((err) => {
|
|
1511
|
-
|
|
1543
|
+
logger9.error(
|
|
1512
1544
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1513
1545
|
err
|
|
1514
1546
|
);
|
|
1515
1547
|
});
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
return checklistRecord;
|
|
1548
|
+
const dateStr = currentDate.toISOString().split("T")[0];
|
|
1549
|
+
logger9.info(
|
|
1550
|
+
`Created new parent checklist ${result.insertedId} for today: ${dateStr}`
|
|
1551
|
+
);
|
|
1552
|
+
return result.insertedId;
|
|
1522
1553
|
} catch (error) {
|
|
1554
|
+
logger9.error("Failed to create daily parent checklist", error);
|
|
1523
1555
|
throw error;
|
|
1524
1556
|
}
|
|
1525
1557
|
}
|
|
1526
|
-
async function
|
|
1558
|
+
async function getAllParentChecklist({
|
|
1527
1559
|
page = 1,
|
|
1528
1560
|
limit = 10,
|
|
1529
1561
|
search = "",
|
|
1530
|
-
|
|
1562
|
+
site,
|
|
1563
|
+
type,
|
|
1531
1564
|
startDate = "",
|
|
1532
1565
|
endDate = ""
|
|
1533
1566
|
}) {
|
|
1534
1567
|
page = page > 0 ? page - 1 : 0;
|
|
1535
|
-
|
|
1536
|
-
const query = {
|
|
1537
|
-
createdAt: {
|
|
1538
|
-
createdAt: {
|
|
1539
|
-
$gte: new Date(startDate),
|
|
1540
|
-
$lte: new Date(endDate)
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1568
|
+
const query = {};
|
|
1544
1569
|
const cacheOptions = {
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1570
|
+
page,
|
|
1571
|
+
limit
|
|
1572
|
+
};
|
|
1573
|
+
try {
|
|
1574
|
+
site = new ObjectId6(site);
|
|
1575
|
+
cacheOptions.site = site.toString();
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
throw new BadRequestError10("Invalid site ID format.");
|
|
1578
|
+
}
|
|
1579
|
+
cacheOptions.type = type;
|
|
1580
|
+
query.status = {
|
|
1581
|
+
$elemMatch: {
|
|
1582
|
+
site: new ObjectId6(site),
|
|
1583
|
+
type
|
|
1550
1584
|
}
|
|
1551
1585
|
};
|
|
1552
|
-
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
1553
|
-
cacheOptions.sort = JSON.stringify(sort);
|
|
1554
1586
|
if (search) {
|
|
1555
1587
|
query.$or = [{ name: { $regex: search, $options: "i" } }];
|
|
1556
1588
|
cacheOptions.search = search;
|
|
1557
1589
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
);
|
|
1565
|
-
})
|
|
1590
|
+
if (startDate && endDate) {
|
|
1591
|
+
query.createdAt = {
|
|
1592
|
+
$gte: new Date(startDate),
|
|
1593
|
+
$lte: new Date(endDate)
|
|
1594
|
+
};
|
|
1595
|
+
cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
|
|
1596
|
+
cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
|
|
1597
|
+
} else if (startDate) {
|
|
1598
|
+
query.createdAt = { $gte: new Date(startDate) };
|
|
1599
|
+
cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
|
|
1600
|
+
} else if (endDate) {
|
|
1601
|
+
query.createdAt = { $lte: new Date(endDate) };
|
|
1602
|
+
cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
|
|
1603
|
+
}
|
|
1566
1604
|
const cacheKey = makeCacheKey3(namespace_collection, cacheOptions);
|
|
1567
1605
|
const cachedData = await getCache(cacheKey);
|
|
1568
1606
|
if (cachedData) {
|
|
1569
|
-
|
|
1607
|
+
logger9.info(`Cache hit for key: ${cacheKey}`);
|
|
1570
1608
|
return cachedData;
|
|
1571
1609
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1610
|
+
try {
|
|
1611
|
+
const pipeline = [{ $match: query }];
|
|
1612
|
+
const filterConditions = [];
|
|
1613
|
+
filterConditions.push({ $eq: ["$$this.site", new ObjectId6(site)] });
|
|
1614
|
+
filterConditions.push({ $eq: ["$$this.type", type] });
|
|
1615
|
+
pipeline.push({
|
|
1616
|
+
$addFields: {
|
|
1617
|
+
filteredStatus: {
|
|
1618
|
+
$filter: {
|
|
1619
|
+
input: "$status",
|
|
1620
|
+
cond: { $and: filterConditions }
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1577
1623
|
}
|
|
1578
|
-
};
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1624
|
+
});
|
|
1625
|
+
pipeline.push({
|
|
1626
|
+
$match: {
|
|
1627
|
+
filteredStatus: { $ne: [] }
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
pipeline.push({
|
|
1631
|
+
$addFields: {
|
|
1632
|
+
statusObj: { $arrayElemAt: ["$filteredStatus", 0] }
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
pipeline.push({
|
|
1636
|
+
$project: {
|
|
1637
|
+
_id: 1,
|
|
1638
|
+
date: 1,
|
|
1639
|
+
status: "$statusObj.status",
|
|
1640
|
+
completedAt: "$statusObj.completedAt",
|
|
1641
|
+
createdAt: 1
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
pipeline.push(
|
|
1645
|
+
{ $sort: { _id: -1 } },
|
|
1646
|
+
{ $skip: page * limit },
|
|
1647
|
+
{ $limit: limit }
|
|
1648
|
+
);
|
|
1649
|
+
const items = await collection.aggregate(pipeline).toArray();
|
|
1650
|
+
const length = await collection.countDocuments(query);
|
|
1651
|
+
const data = paginate3(items, page, limit, length);
|
|
1652
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
1653
|
+
logger9.info(`Cache set for key: ${cacheKey}`);
|
|
1654
|
+
}).catch((err) => {
|
|
1655
|
+
logger9.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
1656
|
+
});
|
|
1657
|
+
return data;
|
|
1658
|
+
} catch (error) {
|
|
1659
|
+
throw error;
|
|
1583
1660
|
}
|
|
1661
|
+
}
|
|
1662
|
+
async function updateParentChecklistStatuses(date) {
|
|
1584
1663
|
try {
|
|
1585
|
-
const
|
|
1664
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
1665
|
+
const dateToUpdate = date || new Date(currentDate.getTime() - 24 * 60 * 60 * 1e3);
|
|
1666
|
+
const startOfDay = new Date(dateToUpdate);
|
|
1667
|
+
startOfDay.setUTCHours(0, 0, 0, 0);
|
|
1668
|
+
const endOfDay = new Date(dateToUpdate);
|
|
1669
|
+
endOfDay.setUTCHours(23, 59, 59, 999);
|
|
1670
|
+
logger9.info(
|
|
1671
|
+
`Updating parent checklist statuses for date: ${dateToUpdate.toISOString().split("T")[0]}`
|
|
1672
|
+
);
|
|
1673
|
+
const statusUpdates = await collection.aggregate([
|
|
1586
1674
|
{
|
|
1587
1675
|
$match: {
|
|
1588
|
-
|
|
1676
|
+
createdAt: {
|
|
1677
|
+
$gte: startOfDay,
|
|
1678
|
+
$lte: endOfDay
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
$lookup: {
|
|
1684
|
+
from: "hygiene-checklist.areas",
|
|
1685
|
+
localField: "_id",
|
|
1686
|
+
foreignField: "parentChecklist",
|
|
1687
|
+
pipeline: [
|
|
1688
|
+
{
|
|
1689
|
+
$group: {
|
|
1690
|
+
_id: {
|
|
1691
|
+
site: "$site",
|
|
1692
|
+
type: "$type"
|
|
1693
|
+
},
|
|
1694
|
+
completedCount: {
|
|
1695
|
+
$sum: {
|
|
1696
|
+
$cond: [{ $eq: ["$status", "Completed"] }, 1, 0]
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
inProgressCount: {
|
|
1700
|
+
$sum: {
|
|
1701
|
+
$cond: [{ $eq: ["$status", "In Progress"] }, 1, 0]
|
|
1702
|
+
}
|
|
1703
|
+
},
|
|
1704
|
+
toDoCount: {
|
|
1705
|
+
$sum: {
|
|
1706
|
+
$cond: [
|
|
1707
|
+
{
|
|
1708
|
+
$or: [
|
|
1709
|
+
{ $eq: ["$status", "To Do"] },
|
|
1710
|
+
{ $eq: ["$status", "Pending"] }
|
|
1711
|
+
]
|
|
1712
|
+
},
|
|
1713
|
+
1,
|
|
1714
|
+
0
|
|
1715
|
+
]
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
totalCount: { $sum: 1 }
|
|
1719
|
+
}
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
$addFields: {
|
|
1723
|
+
finalStatus: {
|
|
1724
|
+
$cond: {
|
|
1725
|
+
if: {
|
|
1726
|
+
$and: [
|
|
1727
|
+
{ $gt: ["$completedCount", 0] },
|
|
1728
|
+
{ $eq: ["$inProgressCount", 0] },
|
|
1729
|
+
{ $eq: ["$toDoCount", 0] }
|
|
1730
|
+
]
|
|
1731
|
+
},
|
|
1732
|
+
then: "Completed",
|
|
1733
|
+
else: {
|
|
1734
|
+
$cond: {
|
|
1735
|
+
if: {
|
|
1736
|
+
$and: [
|
|
1737
|
+
{ $eq: ["$completedCount", 0] },
|
|
1738
|
+
{ $eq: ["$inProgressCount", 0] }
|
|
1739
|
+
]
|
|
1740
|
+
},
|
|
1741
|
+
then: "Expired",
|
|
1742
|
+
else: "In Progress"
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
},
|
|
1747
|
+
completedAt: {
|
|
1748
|
+
$cond: {
|
|
1749
|
+
if: {
|
|
1750
|
+
$and: [
|
|
1751
|
+
{ $gt: ["$completedCount", 0] },
|
|
1752
|
+
{ $eq: ["$inProgressCount", 0] },
|
|
1753
|
+
{ $eq: ["$toDoCount", 0] }
|
|
1754
|
+
]
|
|
1755
|
+
},
|
|
1756
|
+
then: /* @__PURE__ */ new Date(),
|
|
1757
|
+
else: null
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
],
|
|
1763
|
+
as: "areaStats"
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
$addFields: {
|
|
1768
|
+
newStatus: {
|
|
1769
|
+
$map: {
|
|
1770
|
+
input: "$areaStats",
|
|
1771
|
+
as: "stat",
|
|
1772
|
+
in: {
|
|
1773
|
+
site: "$$stat._id.site",
|
|
1774
|
+
type: "$$stat._id.type",
|
|
1775
|
+
status: "$$stat.finalStatus",
|
|
1776
|
+
completedAt: "$$stat.completedAt"
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1589
1780
|
}
|
|
1590
1781
|
},
|
|
1782
|
+
{ $match: { newStatus: { $ne: [] } } },
|
|
1591
1783
|
{
|
|
1592
|
-
$
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
{ $sort: { createdAt: -1 } },
|
|
1596
|
-
{ $skip: page * limit },
|
|
1597
|
-
{ $limit: limit }
|
|
1598
|
-
]
|
|
1784
|
+
$project: {
|
|
1785
|
+
_id: 1,
|
|
1786
|
+
newStatus: 1
|
|
1599
1787
|
}
|
|
1600
1788
|
}
|
|
1601
1789
|
]).toArray();
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1790
|
+
logger9.info(
|
|
1791
|
+
`Found ${statusUpdates.length} parent checklists to potentially update`
|
|
1792
|
+
);
|
|
1793
|
+
if (statusUpdates.length === 0) {
|
|
1794
|
+
logger9.info(
|
|
1795
|
+
`No parent checklists found for date range: ${startOfDay.toISOString()} to ${endOfDay.toISOString()}`
|
|
1796
|
+
);
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
const bulkOps = statusUpdates.map((update) => {
|
|
1800
|
+
const statusTypes = update.newStatus.map((s) => `${s.type}(${s.status})`).join(", ");
|
|
1801
|
+
logger9.info(
|
|
1802
|
+
`Updating parent checklist ${update._id} with ${update.newStatus.length} status entries: [${statusTypes}]`
|
|
1803
|
+
);
|
|
1804
|
+
return {
|
|
1805
|
+
updateOne: {
|
|
1806
|
+
filter: { _id: update._id },
|
|
1807
|
+
update: {
|
|
1808
|
+
$set: {
|
|
1809
|
+
status: update.newStatus,
|
|
1810
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1608
1815
|
});
|
|
1609
|
-
|
|
1816
|
+
let result = null;
|
|
1817
|
+
if (bulkOps.length > 0) {
|
|
1818
|
+
result = await collection.bulkWrite(bulkOps);
|
|
1819
|
+
delNamespace().then(() => {
|
|
1820
|
+
logger9.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1821
|
+
}).catch((err) => {
|
|
1822
|
+
logger9.error(
|
|
1823
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1824
|
+
err
|
|
1825
|
+
);
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
logger9.info(`Updated statuses for ${bulkOps.length} parent checklists.`);
|
|
1829
|
+
return result;
|
|
1610
1830
|
} catch (error) {
|
|
1831
|
+
logger9.error("Failed to update parent checklist statuses", error);
|
|
1611
1832
|
throw error;
|
|
1612
1833
|
}
|
|
1613
1834
|
}
|
|
1614
1835
|
return {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1836
|
+
createIndex,
|
|
1837
|
+
createParentChecklist,
|
|
1838
|
+
getAllParentChecklist,
|
|
1839
|
+
updateParentChecklistStatuses
|
|
1618
1840
|
};
|
|
1619
1841
|
}
|
|
1620
1842
|
|
|
1621
1843
|
// src/controllers/hygiene-parent-checklist.controller.ts
|
|
1622
|
-
import { BadRequestError as BadRequestError11, logger as
|
|
1844
|
+
import { BadRequestError as BadRequestError11, logger as logger10 } from "@iservice365/node-server-utils";
|
|
1623
1845
|
import Joi6 from "joi";
|
|
1624
|
-
function
|
|
1846
|
+
function useParentChecklistController() {
|
|
1625
1847
|
const {
|
|
1626
|
-
|
|
1627
|
-
|
|
1848
|
+
createParentChecklist: _createParentChecklist,
|
|
1849
|
+
getAllParentChecklist: _getAllParentChecklist
|
|
1628
1850
|
} = useParentChecklistRepo();
|
|
1629
|
-
async function
|
|
1630
|
-
const
|
|
1631
|
-
const
|
|
1632
|
-
page: Joi6.number().min(1).optional().allow("", null),
|
|
1633
|
-
limit: Joi6.number().min(1).optional().allow("", null)
|
|
1634
|
-
});
|
|
1635
|
-
const { error } = validation.validate(query);
|
|
1851
|
+
async function createParentChecklist(req, res, next) {
|
|
1852
|
+
const payload = req.body;
|
|
1853
|
+
const { error } = parentChecklistSchema.validate(payload);
|
|
1636
1854
|
if (error) {
|
|
1855
|
+
logger10.log({ level: "error", message: error.message });
|
|
1637
1856
|
next(new BadRequestError11(error.message));
|
|
1638
1857
|
return;
|
|
1639
1858
|
}
|
|
1640
|
-
const page = parseInt(req.query.page) ?? 1;
|
|
1641
|
-
let limit = parseInt(req.query.limit) ?? 20;
|
|
1642
|
-
limit = isNaN(limit) ? 20 : limit;
|
|
1643
|
-
const sort = req.query.sort ? String(req.query.sort).split(",") : "";
|
|
1644
|
-
const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
|
|
1645
|
-
const sortObj = {};
|
|
1646
|
-
if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
|
|
1647
|
-
sort.forEach((field, index) => {
|
|
1648
|
-
sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
const site = req.query.site ?? "";
|
|
1652
|
-
const search = req.query.search ?? "";
|
|
1653
1859
|
try {
|
|
1654
|
-
const
|
|
1655
|
-
|
|
1656
|
-
limit,
|
|
1657
|
-
sort: sortObj,
|
|
1658
|
-
site,
|
|
1659
|
-
search
|
|
1660
|
-
});
|
|
1661
|
-
res.json(buildings);
|
|
1860
|
+
const id = await _createParentChecklist(payload);
|
|
1861
|
+
res.status(201).json({ message: "Parent checklist created successfully.", id });
|
|
1662
1862
|
return;
|
|
1663
1863
|
} catch (error2) {
|
|
1864
|
+
logger10.log({ level: "error", message: error2.message });
|
|
1664
1865
|
next(error2);
|
|
1866
|
+
return;
|
|
1665
1867
|
}
|
|
1666
1868
|
}
|
|
1667
|
-
async function
|
|
1668
|
-
const
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1869
|
+
async function getAllParentChecklist(req, res, next) {
|
|
1870
|
+
const query = { ...req.query, ...req.params };
|
|
1871
|
+
const validation = Joi6.object({
|
|
1872
|
+
page: Joi6.number().min(1).optional().allow("", null),
|
|
1873
|
+
limit: Joi6.number().min(1).optional().allow("", null),
|
|
1874
|
+
search: Joi6.string().optional().allow("", null),
|
|
1875
|
+
site: Joi6.string().hex().required(),
|
|
1876
|
+
type: Joi6.string().required().valid(...allowedTypes),
|
|
1877
|
+
startDate: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null),
|
|
1878
|
+
endDate: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null)
|
|
1671
1879
|
});
|
|
1672
|
-
const { error } =
|
|
1880
|
+
const { error } = validation.validate(query);
|
|
1673
1881
|
if (error) {
|
|
1882
|
+
logger10.log({ level: "error", message: error.message });
|
|
1674
1883
|
next(new BadRequestError11(error.message));
|
|
1675
|
-
logger9.info(`Controller: ${error.message}`);
|
|
1676
1884
|
return;
|
|
1677
1885
|
}
|
|
1886
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
1887
|
+
const limit = parseInt(req.query.limit) ?? 20;
|
|
1888
|
+
const search = req.query.search ?? "";
|
|
1889
|
+
const site = req.params.site ?? "";
|
|
1890
|
+
const type = req.params.type ?? "";
|
|
1891
|
+
const startDate = req.query.startDate ?? "";
|
|
1892
|
+
const endDate = req.query.endDate ?? "";
|
|
1678
1893
|
try {
|
|
1679
|
-
const
|
|
1680
|
-
|
|
1894
|
+
const data = await _getAllParentChecklist({
|
|
1895
|
+
page,
|
|
1896
|
+
limit,
|
|
1897
|
+
search,
|
|
1898
|
+
site,
|
|
1899
|
+
type,
|
|
1900
|
+
startDate,
|
|
1901
|
+
endDate
|
|
1902
|
+
});
|
|
1903
|
+
res.json(data);
|
|
1681
1904
|
return;
|
|
1682
1905
|
} catch (error2) {
|
|
1906
|
+
logger10.log({ level: "error", message: error2.message });
|
|
1683
1907
|
next(error2);
|
|
1908
|
+
return;
|
|
1684
1909
|
}
|
|
1685
1910
|
}
|
|
1686
1911
|
return {
|
|
1687
|
-
|
|
1688
|
-
|
|
1912
|
+
createParentChecklist,
|
|
1913
|
+
getAllParentChecklist
|
|
1689
1914
|
};
|
|
1690
1915
|
}
|
|
1691
1916
|
|
|
1692
1917
|
// src/models/hygiene-unit.model.ts
|
|
1693
|
-
import { BadRequestError as BadRequestError12, logger as
|
|
1918
|
+
import { BadRequestError as BadRequestError12, logger as logger11 } from "@iservice365/node-server-utils";
|
|
1694
1919
|
import Joi7 from "joi";
|
|
1695
|
-
import { ObjectId as
|
|
1920
|
+
import { ObjectId as ObjectId7 } from "mongodb";
|
|
1696
1921
|
var unitSchema = Joi7.object({
|
|
1697
1922
|
name: Joi7.string().required(),
|
|
1698
1923
|
site: Joi7.string().hex().required(),
|
|
@@ -1701,19 +1926,19 @@ var unitSchema = Joi7.object({
|
|
|
1701
1926
|
function MUnit(value) {
|
|
1702
1927
|
const { error } = unitSchema.validate(value);
|
|
1703
1928
|
if (error) {
|
|
1704
|
-
|
|
1929
|
+
logger11.info(`Hygiene Unit Model: ${error.message}`);
|
|
1705
1930
|
throw new BadRequestError12(error.message);
|
|
1706
1931
|
}
|
|
1707
1932
|
if (value.site) {
|
|
1708
1933
|
try {
|
|
1709
|
-
value.site = new
|
|
1934
|
+
value.site = new ObjectId7(value.site);
|
|
1710
1935
|
} catch (error2) {
|
|
1711
1936
|
throw new BadRequestError12("Invalid site ID format.");
|
|
1712
1937
|
}
|
|
1713
1938
|
}
|
|
1714
1939
|
if (value.createdBy) {
|
|
1715
1940
|
try {
|
|
1716
|
-
value.createdBy = new
|
|
1941
|
+
value.createdBy = new ObjectId7(value.createdBy);
|
|
1717
1942
|
} catch (error2) {
|
|
1718
1943
|
throw new BadRequestError12("Invalid createdBy ID format.");
|
|
1719
1944
|
}
|
|
@@ -1732,20 +1957,20 @@ function MUnit(value) {
|
|
|
1732
1957
|
// src/services/hygiene-unit.service.ts
|
|
1733
1958
|
import {
|
|
1734
1959
|
BadRequestError as BadRequestError14,
|
|
1735
|
-
logger as
|
|
1736
|
-
NotFoundError as
|
|
1960
|
+
logger as logger13,
|
|
1961
|
+
NotFoundError as NotFoundError5,
|
|
1737
1962
|
useAtlas as useAtlas7
|
|
1738
1963
|
} from "@iservice365/node-server-utils";
|
|
1739
1964
|
|
|
1740
1965
|
// src/repositories/hygiene-unit.repository.ts
|
|
1741
|
-
import { ObjectId as
|
|
1966
|
+
import { ObjectId as ObjectId8 } from "mongodb";
|
|
1742
1967
|
import {
|
|
1743
1968
|
useAtlas as useAtlas6,
|
|
1744
1969
|
InternalServerError as InternalServerError4,
|
|
1745
1970
|
paginate as paginate4,
|
|
1746
1971
|
BadRequestError as BadRequestError13,
|
|
1747
1972
|
useCache as useCache4,
|
|
1748
|
-
logger as
|
|
1973
|
+
logger as logger12,
|
|
1749
1974
|
makeCacheKey as makeCacheKey4
|
|
1750
1975
|
} from "@iservice365/node-server-utils";
|
|
1751
1976
|
function useUnitRepository() {
|
|
@@ -1793,9 +2018,9 @@ function useUnitRepository() {
|
|
|
1793
2018
|
value = MUnit(value);
|
|
1794
2019
|
const res = await collection.insertOne(value, { session });
|
|
1795
2020
|
delNamespace().then(() => {
|
|
1796
|
-
|
|
2021
|
+
logger12.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1797
2022
|
}).catch((err) => {
|
|
1798
|
-
|
|
2023
|
+
logger12.error(
|
|
1799
2024
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1800
2025
|
err
|
|
1801
2026
|
);
|
|
@@ -1819,7 +2044,7 @@ function useUnitRepository() {
|
|
|
1819
2044
|
}) {
|
|
1820
2045
|
page = page > 0 ? page - 1 : 0;
|
|
1821
2046
|
try {
|
|
1822
|
-
site = new
|
|
2047
|
+
site = new ObjectId8(site);
|
|
1823
2048
|
} catch (error) {
|
|
1824
2049
|
throw new BadRequestError13("Invalid site ID format.");
|
|
1825
2050
|
}
|
|
@@ -1853,7 +2078,7 @@ function useUnitRepository() {
|
|
|
1853
2078
|
const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
|
|
1854
2079
|
const cachedData = await getCache(cacheKey);
|
|
1855
2080
|
if (cachedData) {
|
|
1856
|
-
|
|
2081
|
+
logger12.info(`Cache hit for key: ${cacheKey}`);
|
|
1857
2082
|
return cachedData;
|
|
1858
2083
|
}
|
|
1859
2084
|
try {
|
|
@@ -1907,9 +2132,9 @@ function useUnitRepository() {
|
|
|
1907
2132
|
const length = await collection.countDocuments(query);
|
|
1908
2133
|
const data = paginate4(items, page, limit, length);
|
|
1909
2134
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
1910
|
-
|
|
2135
|
+
logger12.info(`Cache set for key: ${cacheKey}`);
|
|
1911
2136
|
}).catch((err) => {
|
|
1912
|
-
|
|
2137
|
+
logger12.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
1913
2138
|
});
|
|
1914
2139
|
return data;
|
|
1915
2140
|
} catch (error) {
|
|
@@ -1919,7 +2144,7 @@ function useUnitRepository() {
|
|
|
1919
2144
|
async function getUnitByName(name, site) {
|
|
1920
2145
|
try {
|
|
1921
2146
|
if (site)
|
|
1922
|
-
site = new
|
|
2147
|
+
site = new ObjectId8(site);
|
|
1923
2148
|
} catch (error) {
|
|
1924
2149
|
throw new BadRequestError13("Invalid site ID format.");
|
|
1925
2150
|
}
|
|
@@ -1934,13 +2159,13 @@ function useUnitRepository() {
|
|
|
1934
2159
|
}
|
|
1935
2160
|
async function getUnitById(id, site) {
|
|
1936
2161
|
try {
|
|
1937
|
-
id = typeof id === "string" ? new
|
|
2162
|
+
id = typeof id === "string" ? new ObjectId8(id) : id;
|
|
1938
2163
|
} catch (error) {
|
|
1939
2164
|
throw new BadRequestError13("Invalid unit ID format.");
|
|
1940
2165
|
}
|
|
1941
2166
|
try {
|
|
1942
2167
|
if (site)
|
|
1943
|
-
site = new
|
|
2168
|
+
site = new ObjectId8(site);
|
|
1944
2169
|
} catch (error) {
|
|
1945
2170
|
throw new BadRequestError13(
|
|
1946
2171
|
"Unable to fetch unit by ID, Invalid site ID format."
|
|
@@ -1954,7 +2179,7 @@ function useUnitRepository() {
|
|
|
1954
2179
|
}
|
|
1955
2180
|
async function updateUnit(_id, value) {
|
|
1956
2181
|
try {
|
|
1957
|
-
_id = new
|
|
2182
|
+
_id = new ObjectId8(_id);
|
|
1958
2183
|
} catch (error) {
|
|
1959
2184
|
throw new BadRequestError13("Invalid unit ID format.");
|
|
1960
2185
|
}
|
|
@@ -1965,9 +2190,9 @@ function useUnitRepository() {
|
|
|
1965
2190
|
throw new InternalServerError4("Unable to update cleaning unit.");
|
|
1966
2191
|
}
|
|
1967
2192
|
delNamespace().then(() => {
|
|
1968
|
-
|
|
2193
|
+
logger12.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
1969
2194
|
}).catch((err) => {
|
|
1970
|
-
|
|
2195
|
+
logger12.error(
|
|
1971
2196
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
1972
2197
|
err
|
|
1973
2198
|
);
|
|
@@ -1983,7 +2208,7 @@ function useUnitRepository() {
|
|
|
1983
2208
|
}
|
|
1984
2209
|
async function deleteUnit(_id, session) {
|
|
1985
2210
|
try {
|
|
1986
|
-
_id = new
|
|
2211
|
+
_id = new ObjectId8(_id);
|
|
1987
2212
|
} catch (error) {
|
|
1988
2213
|
throw new BadRequestError13("Invalid unit ID format.");
|
|
1989
2214
|
}
|
|
@@ -2002,9 +2227,9 @@ function useUnitRepository() {
|
|
|
2002
2227
|
throw new InternalServerError4("Unable to delete unit.");
|
|
2003
2228
|
}
|
|
2004
2229
|
delNamespace().then(() => {
|
|
2005
|
-
|
|
2230
|
+
logger12.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2006
2231
|
}).catch((err) => {
|
|
2007
|
-
|
|
2232
|
+
logger12.error(
|
|
2008
2233
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2009
2234
|
err
|
|
2010
2235
|
);
|
|
@@ -2042,7 +2267,7 @@ function useUnitService() {
|
|
|
2042
2267
|
throw new BadRequestError14("Invalid JSON format for data in excel");
|
|
2043
2268
|
}
|
|
2044
2269
|
if (!dataArray || dataArray.length === 0) {
|
|
2045
|
-
throw new
|
|
2270
|
+
throw new NotFoundError5("No data found in the uploaded file");
|
|
2046
2271
|
}
|
|
2047
2272
|
const session = useAtlas7.getClient()?.startSession();
|
|
2048
2273
|
const insertedUnitIds = [];
|
|
@@ -2050,7 +2275,7 @@ function useUnitService() {
|
|
|
2050
2275
|
session?.startTransaction();
|
|
2051
2276
|
for (const row of dataArray) {
|
|
2052
2277
|
if (!row?.UNIT_NAME) {
|
|
2053
|
-
|
|
2278
|
+
logger13.warn("Skipping row with missing UNIT_NAME:", row);
|
|
2054
2279
|
continue;
|
|
2055
2280
|
}
|
|
2056
2281
|
try {
|
|
@@ -2064,7 +2289,7 @@ function useUnitService() {
|
|
|
2064
2289
|
);
|
|
2065
2290
|
insertedUnitIds.push(insertedId);
|
|
2066
2291
|
} catch (error) {
|
|
2067
|
-
|
|
2292
|
+
logger13.error(
|
|
2068
2293
|
`Error creating unit "${row.UNIT_NAME}":`,
|
|
2069
2294
|
error.message
|
|
2070
2295
|
);
|
|
@@ -2072,13 +2297,13 @@ function useUnitService() {
|
|
|
2072
2297
|
}
|
|
2073
2298
|
}
|
|
2074
2299
|
await session?.commitTransaction();
|
|
2075
|
-
|
|
2300
|
+
logger13.info(`Successfully uploaded ${insertedUnitIds.length} units`);
|
|
2076
2301
|
return {
|
|
2077
2302
|
message: `Successfully uploaded ${insertedUnitIds.length} units`
|
|
2078
2303
|
};
|
|
2079
2304
|
} catch (error) {
|
|
2080
2305
|
await session?.abortTransaction();
|
|
2081
|
-
|
|
2306
|
+
logger13.error("Error while uploading unit information", error);
|
|
2082
2307
|
throw error;
|
|
2083
2308
|
} finally {
|
|
2084
2309
|
session?.endSession();
|
|
@@ -2090,7 +2315,7 @@ function useUnitService() {
|
|
|
2090
2315
|
}
|
|
2091
2316
|
|
|
2092
2317
|
// src/controllers/hygiene-unit.controller.ts
|
|
2093
|
-
import { BadRequestError as BadRequestError15, logger as
|
|
2318
|
+
import { BadRequestError as BadRequestError15, logger as logger14 } from "@iservice365/node-server-utils";
|
|
2094
2319
|
import Joi8 from "joi";
|
|
2095
2320
|
function useUnitController() {
|
|
2096
2321
|
const {
|
|
@@ -2109,7 +2334,7 @@ function useUnitController() {
|
|
|
2109
2334
|
const payload = { ...req.body, createdBy };
|
|
2110
2335
|
const { error } = unitSchema.validate(payload);
|
|
2111
2336
|
if (error) {
|
|
2112
|
-
|
|
2337
|
+
logger14.log({ level: "error", message: error.message });
|
|
2113
2338
|
next(new BadRequestError15(error.message));
|
|
2114
2339
|
return;
|
|
2115
2340
|
}
|
|
@@ -2118,7 +2343,7 @@ function useUnitController() {
|
|
|
2118
2343
|
res.status(201).json({ message: "Unit created successfully.", id });
|
|
2119
2344
|
return;
|
|
2120
2345
|
} catch (error2) {
|
|
2121
|
-
|
|
2346
|
+
logger14.log({ level: "error", message: error2.message });
|
|
2122
2347
|
next(error2);
|
|
2123
2348
|
return;
|
|
2124
2349
|
}
|
|
@@ -2135,7 +2360,7 @@ function useUnitController() {
|
|
|
2135
2360
|
});
|
|
2136
2361
|
const { error } = validation.validate(query);
|
|
2137
2362
|
if (error) {
|
|
2138
|
-
|
|
2363
|
+
logger14.log({ level: "error", message: error.message });
|
|
2139
2364
|
next(new BadRequestError15(error.message));
|
|
2140
2365
|
return;
|
|
2141
2366
|
}
|
|
@@ -2157,7 +2382,7 @@ function useUnitController() {
|
|
|
2157
2382
|
res.json(data);
|
|
2158
2383
|
return;
|
|
2159
2384
|
} catch (error2) {
|
|
2160
|
-
|
|
2385
|
+
logger14.log({ level: "error", message: error2.message });
|
|
2161
2386
|
next(error2);
|
|
2162
2387
|
return;
|
|
2163
2388
|
}
|
|
@@ -2170,7 +2395,7 @@ function useUnitController() {
|
|
|
2170
2395
|
});
|
|
2171
2396
|
const { error } = schema.validate(payload);
|
|
2172
2397
|
if (error) {
|
|
2173
|
-
|
|
2398
|
+
logger14.log({ level: "error", message: error.message });
|
|
2174
2399
|
next(new BadRequestError15(error.message));
|
|
2175
2400
|
return;
|
|
2176
2401
|
}
|
|
@@ -2180,7 +2405,7 @@ function useUnitController() {
|
|
|
2180
2405
|
res.json({ message: "Unit updated successfully." });
|
|
2181
2406
|
return;
|
|
2182
2407
|
} catch (error2) {
|
|
2183
|
-
|
|
2408
|
+
logger14.log({ level: "error", message: error2.message });
|
|
2184
2409
|
next(error2);
|
|
2185
2410
|
return;
|
|
2186
2411
|
}
|
|
@@ -2192,7 +2417,7 @@ function useUnitController() {
|
|
|
2192
2417
|
});
|
|
2193
2418
|
const { error } = validation.validate({ id });
|
|
2194
2419
|
if (error) {
|
|
2195
|
-
|
|
2420
|
+
logger14.log({ level: "error", message: error.message });
|
|
2196
2421
|
next(new BadRequestError15(error.message));
|
|
2197
2422
|
return;
|
|
2198
2423
|
}
|
|
@@ -2201,7 +2426,7 @@ function useUnitController() {
|
|
|
2201
2426
|
res.json({ message: "Unit deleted successfully." });
|
|
2202
2427
|
return;
|
|
2203
2428
|
} catch (error2) {
|
|
2204
|
-
|
|
2429
|
+
logger14.log({ level: "error", message: error2.message });
|
|
2205
2430
|
next(error2);
|
|
2206
2431
|
return;
|
|
2207
2432
|
}
|
|
@@ -2223,7 +2448,7 @@ function useUnitController() {
|
|
|
2223
2448
|
});
|
|
2224
2449
|
const { error } = schema.validate({ site, createdBy });
|
|
2225
2450
|
if (error) {
|
|
2226
|
-
|
|
2451
|
+
logger14.log({ level: "error", message: error.message });
|
|
2227
2452
|
next(new BadRequestError15(error.message));
|
|
2228
2453
|
return;
|
|
2229
2454
|
}
|
|
@@ -2233,7 +2458,7 @@ function useUnitController() {
|
|
|
2233
2458
|
const result = await _uploadByFile({ dataJson, createdBy, site });
|
|
2234
2459
|
return res.status(201).json(result);
|
|
2235
2460
|
} catch (error2) {
|
|
2236
|
-
|
|
2461
|
+
logger14.log({ level: "error", message: error2.message });
|
|
2237
2462
|
next(error2);
|
|
2238
2463
|
return;
|
|
2239
2464
|
}
|
|
@@ -2250,7 +2475,7 @@ function useUnitController() {
|
|
|
2250
2475
|
// src/models/hygiene-schedule-task-area.model.ts
|
|
2251
2476
|
import { BadRequestError as BadRequestError16 } from "@iservice365/node-server-utils";
|
|
2252
2477
|
import Joi9 from "joi";
|
|
2253
|
-
import { ObjectId as
|
|
2478
|
+
import { ObjectId as ObjectId9 } from "mongodb";
|
|
2254
2479
|
var scheduleTaskAreaSchema = Joi9.object({
|
|
2255
2480
|
name: Joi9.string().required(),
|
|
2256
2481
|
site: Joi9.string().hex().required(),
|
|
@@ -2271,14 +2496,14 @@ function MScheduleTaskArea(value) {
|
|
|
2271
2496
|
}
|
|
2272
2497
|
if (value.site) {
|
|
2273
2498
|
try {
|
|
2274
|
-
value.site = new
|
|
2499
|
+
value.site = new ObjectId9(value.site);
|
|
2275
2500
|
} catch (error2) {
|
|
2276
2501
|
throw new BadRequestError16("Invalid site ID format.");
|
|
2277
2502
|
}
|
|
2278
2503
|
}
|
|
2279
2504
|
if (value.createdBy) {
|
|
2280
2505
|
try {
|
|
2281
|
-
value.createdBy = new
|
|
2506
|
+
value.createdBy = new ObjectId9(value.createdBy);
|
|
2282
2507
|
} catch (error2) {
|
|
2283
2508
|
throw new BadRequestError16("Invalid createdBy ID format.");
|
|
2284
2509
|
}
|
|
@@ -2288,7 +2513,7 @@ function MScheduleTaskArea(value) {
|
|
|
2288
2513
|
try {
|
|
2289
2514
|
return {
|
|
2290
2515
|
...item,
|
|
2291
|
-
_id: new
|
|
2516
|
+
_id: new ObjectId9(item._id)
|
|
2292
2517
|
};
|
|
2293
2518
|
} catch (error2) {
|
|
2294
2519
|
throw new BadRequestError16(
|
|
@@ -2310,14 +2535,15 @@ function MScheduleTaskArea(value) {
|
|
|
2310
2535
|
}
|
|
2311
2536
|
|
|
2312
2537
|
// src/repositories/hygiene-schedule-task-area.repository.ts
|
|
2313
|
-
import { ObjectId as
|
|
2538
|
+
import { ObjectId as ObjectId10 } from "mongodb";
|
|
2314
2539
|
import {
|
|
2315
2540
|
useAtlas as useAtlas8,
|
|
2316
2541
|
InternalServerError as InternalServerError5,
|
|
2317
2542
|
paginate as paginate5,
|
|
2318
2543
|
BadRequestError as BadRequestError17,
|
|
2544
|
+
NotFoundError as NotFoundError6,
|
|
2319
2545
|
useCache as useCache5,
|
|
2320
|
-
logger as
|
|
2546
|
+
logger as logger15,
|
|
2321
2547
|
makeCacheKey as makeCacheKey5
|
|
2322
2548
|
} from "@iservice365/node-server-utils";
|
|
2323
2549
|
function useScheduleTaskAreaRepository() {
|
|
@@ -2338,51 +2564,71 @@ function useScheduleTaskAreaRepository() {
|
|
|
2338
2564
|
throw new InternalServerError5("Failed to create index on site.");
|
|
2339
2565
|
}
|
|
2340
2566
|
}
|
|
2567
|
+
async function createUniqueIndex() {
|
|
2568
|
+
try {
|
|
2569
|
+
await collection.createIndex(
|
|
2570
|
+
{ name: 1, site: 1, deletedAt: 1 },
|
|
2571
|
+
{ unique: true }
|
|
2572
|
+
);
|
|
2573
|
+
} catch (error) {
|
|
2574
|
+
throw new InternalServerError5(
|
|
2575
|
+
"Failed to create unique index on hygiene area."
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2341
2579
|
async function createScheduleTaskArea(value, session) {
|
|
2342
2580
|
try {
|
|
2343
2581
|
value = MScheduleTaskArea(value);
|
|
2344
2582
|
const res = await collection.insertOne(value, { session });
|
|
2345
2583
|
delNamespace().then(() => {
|
|
2346
|
-
|
|
2584
|
+
logger15.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2347
2585
|
}).catch((err) => {
|
|
2348
|
-
|
|
2586
|
+
logger15.error(
|
|
2349
2587
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2350
2588
|
err
|
|
2351
2589
|
);
|
|
2352
2590
|
});
|
|
2353
2591
|
return res.insertedId;
|
|
2354
2592
|
} catch (error) {
|
|
2593
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
2594
|
+
if (isDuplicated) {
|
|
2595
|
+
throw new BadRequestError17("Area already exists.");
|
|
2596
|
+
}
|
|
2355
2597
|
throw error;
|
|
2356
2598
|
}
|
|
2357
2599
|
}
|
|
2358
2600
|
async function updateScheduleTaskArea(_id, params) {
|
|
2359
2601
|
try {
|
|
2360
|
-
_id = new
|
|
2602
|
+
_id = new ObjectId10(_id);
|
|
2361
2603
|
} catch (error) {
|
|
2362
2604
|
throw new BadRequestError17("Invalid area ID format.");
|
|
2363
2605
|
}
|
|
2364
2606
|
try {
|
|
2365
|
-
const
|
|
2366
|
-
const res = await collection.updateOne({ _id }, { $set:
|
|
2607
|
+
const updateValue = { ...params, updatedAt: /* @__PURE__ */ new Date() };
|
|
2608
|
+
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
2367
2609
|
if (res.modifiedCount === 0) {
|
|
2368
2610
|
throw new InternalServerError5("Unable to update cleaning area.");
|
|
2369
2611
|
}
|
|
2370
2612
|
delNamespace().then(() => {
|
|
2371
|
-
|
|
2613
|
+
logger15.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2372
2614
|
}).catch((err) => {
|
|
2373
|
-
|
|
2615
|
+
logger15.error(
|
|
2374
2616
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2375
2617
|
err
|
|
2376
2618
|
);
|
|
2377
2619
|
});
|
|
2378
2620
|
return res.modifiedCount;
|
|
2379
2621
|
} catch (error) {
|
|
2622
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
2623
|
+
if (isDuplicated) {
|
|
2624
|
+
throw new BadRequestError17("Area already exists.");
|
|
2625
|
+
}
|
|
2380
2626
|
throw error;
|
|
2381
2627
|
}
|
|
2382
2628
|
}
|
|
2383
2629
|
async function deleteScheduleTaskArea(_id, session) {
|
|
2384
2630
|
try {
|
|
2385
|
-
_id = new
|
|
2631
|
+
_id = new ObjectId10(_id);
|
|
2386
2632
|
} catch (error) {
|
|
2387
2633
|
throw new BadRequestError17("Invalid area ID format.");
|
|
2388
2634
|
}
|
|
@@ -2397,13 +2643,16 @@ function useScheduleTaskAreaRepository() {
|
|
|
2397
2643
|
{ $set: updateValue },
|
|
2398
2644
|
{ session }
|
|
2399
2645
|
);
|
|
2400
|
-
if (res.modifiedCount === 0)
|
|
2646
|
+
if (res.modifiedCount === 0) {
|
|
2401
2647
|
throw new InternalServerError5("Unable to delete area.");
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2648
|
+
}
|
|
2649
|
+
delNamespace().then(() => {
|
|
2650
|
+
logger15.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2405
2651
|
}).catch((err) => {
|
|
2406
|
-
|
|
2652
|
+
logger15.error(
|
|
2653
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2654
|
+
err
|
|
2655
|
+
);
|
|
2407
2656
|
});
|
|
2408
2657
|
return res.modifiedCount;
|
|
2409
2658
|
} catch (error) {
|
|
@@ -2414,63 +2663,54 @@ function useScheduleTaskAreaRepository() {
|
|
|
2414
2663
|
page = 1,
|
|
2415
2664
|
limit = 10,
|
|
2416
2665
|
search = "",
|
|
2417
|
-
sort = {},
|
|
2418
2666
|
startDate = "",
|
|
2419
2667
|
endDate = "",
|
|
2420
2668
|
site = ""
|
|
2421
2669
|
}) {
|
|
2422
2670
|
page = page > 0 ? page - 1 : 0;
|
|
2423
|
-
let dateFilter = {};
|
|
2424
|
-
try {
|
|
2425
|
-
site = new ObjectId8(site);
|
|
2426
|
-
} catch (error) {
|
|
2427
|
-
throw new BadRequestError17("Invalid site ID format.");
|
|
2428
|
-
}
|
|
2429
2671
|
const query = {
|
|
2430
2672
|
status: { $ne: "deleted" }
|
|
2431
2673
|
};
|
|
2432
2674
|
const cacheOptions = {
|
|
2433
|
-
|
|
2675
|
+
page,
|
|
2676
|
+
limit
|
|
2434
2677
|
};
|
|
2435
|
-
|
|
2436
|
-
|
|
2678
|
+
if (site) {
|
|
2679
|
+
try {
|
|
2680
|
+
site = new ObjectId10(site);
|
|
2681
|
+
cacheOptions.site = site.toString();
|
|
2682
|
+
} catch (error) {
|
|
2683
|
+
throw new BadRequestError17("Invalid site ID format.");
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2437
2686
|
if (search) {
|
|
2438
2687
|
query.$or = [{ name: { $regex: search, $options: "i" } }];
|
|
2439
2688
|
cacheOptions.search = search;
|
|
2440
2689
|
}
|
|
2441
|
-
delNamespace().then(() => {
|
|
2442
|
-
logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
2443
|
-
}).catch((err) => {
|
|
2444
|
-
logger14.error(
|
|
2445
|
-
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
2446
|
-
err
|
|
2447
|
-
);
|
|
2448
|
-
});
|
|
2449
|
-
const cacheKey = makeCacheKey5(namespace_collection, cacheOptions);
|
|
2450
|
-
const cachedData = await getCache(cacheKey);
|
|
2451
|
-
if (cachedData) {
|
|
2452
|
-
logger14.info(`Cache hit for key: ${cacheKey}`);
|
|
2453
|
-
return cachedData;
|
|
2454
|
-
}
|
|
2455
2690
|
if (startDate && endDate) {
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
} else if (startDate) {
|
|
2463
|
-
|
|
2691
|
+
query.createdAt = {
|
|
2692
|
+
$gte: new Date(startDate),
|
|
2693
|
+
$lte: new Date(endDate)
|
|
2694
|
+
};
|
|
2695
|
+
cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
|
|
2696
|
+
cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
|
|
2697
|
+
} else if (startDate) {
|
|
2698
|
+
query.createdAt = { $gte: new Date(startDate) };
|
|
2699
|
+
cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
|
|
2464
2700
|
} else if (endDate) {
|
|
2465
|
-
|
|
2701
|
+
query.createdAt = { $lte: new Date(endDate) };
|
|
2702
|
+
cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
|
|
2703
|
+
}
|
|
2704
|
+
const cacheKey = makeCacheKey5(namespace_collection, cacheOptions);
|
|
2705
|
+
const cachedData = await getCache(cacheKey);
|
|
2706
|
+
if (cachedData) {
|
|
2707
|
+
logger15.info(`Cache hit for key: ${cacheKey}`);
|
|
2708
|
+
return cachedData;
|
|
2466
2709
|
}
|
|
2467
2710
|
try {
|
|
2468
2711
|
const items = await collection.aggregate([
|
|
2469
2712
|
{
|
|
2470
|
-
$match:
|
|
2471
|
-
...dateFilter,
|
|
2472
|
-
...query
|
|
2473
|
-
}
|
|
2713
|
+
$match: query
|
|
2474
2714
|
},
|
|
2475
2715
|
{
|
|
2476
2716
|
$lookup: {
|
|
@@ -2503,22 +2743,26 @@ function useScheduleTaskAreaRepository() {
|
|
|
2503
2743
|
}
|
|
2504
2744
|
},
|
|
2505
2745
|
{
|
|
2506
|
-
$
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2746
|
+
$project: {
|
|
2747
|
+
name: 1,
|
|
2748
|
+
site: "$site._id",
|
|
2749
|
+
siteName: "$site.name",
|
|
2750
|
+
createdByName: "$createdBy.name",
|
|
2751
|
+
checklist: 1,
|
|
2752
|
+
status: 1,
|
|
2753
|
+
createdAt: 1
|
|
2513
2754
|
}
|
|
2514
|
-
}
|
|
2755
|
+
},
|
|
2756
|
+
{ $sort: { _id: -1 } },
|
|
2757
|
+
{ $skip: page * limit },
|
|
2758
|
+
{ $limit: limit }
|
|
2515
2759
|
]).toArray();
|
|
2516
2760
|
const length = await collection.countDocuments(query);
|
|
2517
2761
|
const data = paginate5(items, page, limit, length);
|
|
2518
2762
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2519
|
-
|
|
2763
|
+
logger15.info(`Cache set for key: ${cacheKey}`);
|
|
2520
2764
|
}).catch((err) => {
|
|
2521
|
-
|
|
2765
|
+
logger15.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2522
2766
|
});
|
|
2523
2767
|
return data;
|
|
2524
2768
|
} catch (error) {
|
|
@@ -2528,7 +2772,7 @@ function useScheduleTaskAreaRepository() {
|
|
|
2528
2772
|
async function getScheduleTaskAreaByName(name, site) {
|
|
2529
2773
|
try {
|
|
2530
2774
|
if (site)
|
|
2531
|
-
site = new
|
|
2775
|
+
site = new ObjectId10(site);
|
|
2532
2776
|
} catch (error) {
|
|
2533
2777
|
throw new BadRequestError17("Invalid site ID format.");
|
|
2534
2778
|
}
|
|
@@ -2543,13 +2787,13 @@ function useScheduleTaskAreaRepository() {
|
|
|
2543
2787
|
}
|
|
2544
2788
|
async function getScheduleTaskAreaById(id, site) {
|
|
2545
2789
|
try {
|
|
2546
|
-
id = typeof id === "string" ? new
|
|
2790
|
+
id = typeof id === "string" ? new ObjectId10(id) : id;
|
|
2547
2791
|
} catch (error) {
|
|
2548
2792
|
throw new BadRequestError17("Invalid unit ID format.");
|
|
2549
2793
|
}
|
|
2550
2794
|
try {
|
|
2551
2795
|
if (site)
|
|
2552
|
-
site = new
|
|
2796
|
+
site = new ObjectId10(site);
|
|
2553
2797
|
} catch (error) {
|
|
2554
2798
|
throw new BadRequestError17(
|
|
2555
2799
|
"Unable to fetch schedule task area by ID, Invalid site ID format."
|
|
@@ -2561,84 +2805,148 @@ function useScheduleTaskAreaRepository() {
|
|
|
2561
2805
|
throw new BadRequestError17("Unable to fetch schedule task area by id.");
|
|
2562
2806
|
}
|
|
2563
2807
|
}
|
|
2808
|
+
async function getAreaById(_id) {
|
|
2809
|
+
try {
|
|
2810
|
+
_id = new ObjectId10(_id);
|
|
2811
|
+
} catch (error) {
|
|
2812
|
+
throw new BadRequestError17("Invalid area ID format.");
|
|
2813
|
+
}
|
|
2814
|
+
const query = {
|
|
2815
|
+
_id,
|
|
2816
|
+
status: { $ne: "deleted" }
|
|
2817
|
+
};
|
|
2818
|
+
const cacheKey = makeCacheKey5(namespace_collection, {
|
|
2819
|
+
_id: _id.toString()
|
|
2820
|
+
});
|
|
2821
|
+
const cachedData = await getCache(cacheKey);
|
|
2822
|
+
if (cachedData) {
|
|
2823
|
+
logger15.info(`Cache hit for key: ${cacheKey}`);
|
|
2824
|
+
return cachedData;
|
|
2825
|
+
}
|
|
2826
|
+
try {
|
|
2827
|
+
const data = await collection.aggregate([
|
|
2828
|
+
{ $match: query },
|
|
2829
|
+
{
|
|
2830
|
+
$project: {
|
|
2831
|
+
name: 1,
|
|
2832
|
+
checklist: 1
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
]).toArray();
|
|
2836
|
+
if (!data || !data.length) {
|
|
2837
|
+
throw new NotFoundError6("Area not found.");
|
|
2838
|
+
}
|
|
2839
|
+
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
2840
|
+
logger15.info(`Cache set for key: ${cacheKey}`);
|
|
2841
|
+
}).catch((err) => {
|
|
2842
|
+
logger15.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
2843
|
+
});
|
|
2844
|
+
return data[0];
|
|
2845
|
+
} catch (error) {
|
|
2846
|
+
throw error;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2564
2849
|
return {
|
|
2850
|
+
createUniqueIndex,
|
|
2565
2851
|
createScheduleTaskArea,
|
|
2566
2852
|
updateScheduleTaskArea,
|
|
2567
2853
|
deleteScheduleTaskArea,
|
|
2568
2854
|
getScheduleTaskAreas,
|
|
2569
2855
|
createIndexes,
|
|
2570
2856
|
getScheduleTaskAreaByName,
|
|
2571
|
-
getScheduleTaskAreaById
|
|
2857
|
+
getScheduleTaskAreaById,
|
|
2858
|
+
getAreaById
|
|
2572
2859
|
};
|
|
2573
2860
|
}
|
|
2574
2861
|
|
|
2575
2862
|
// src/services/hygiene-schedule-task-area.service.ts
|
|
2576
2863
|
import {
|
|
2577
|
-
|
|
2864
|
+
BadRequestError as BadRequestError18,
|
|
2865
|
+
logger as logger16,
|
|
2866
|
+
NotFoundError as NotFoundError7,
|
|
2867
|
+
useAtlas as useAtlas9
|
|
2578
2868
|
} from "@iservice365/node-server-utils";
|
|
2579
2869
|
function useScheduleTaskAreaService() {
|
|
2580
|
-
const {
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
);
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
)
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2870
|
+
const { createScheduleTaskArea: _create } = useScheduleTaskAreaRepository();
|
|
2871
|
+
async function uploadByFile({
|
|
2872
|
+
dataJson,
|
|
2873
|
+
createdBy,
|
|
2874
|
+
site
|
|
2875
|
+
}) {
|
|
2876
|
+
let dataArray;
|
|
2877
|
+
try {
|
|
2878
|
+
dataArray = JSON.parse(dataJson);
|
|
2879
|
+
} catch (error) {
|
|
2880
|
+
throw new BadRequestError18("Invalid JSON format for data in excel");
|
|
2881
|
+
}
|
|
2882
|
+
if (!dataArray || dataArray.length === 0) {
|
|
2883
|
+
throw new NotFoundError7("No data found in the uploaded file");
|
|
2884
|
+
}
|
|
2885
|
+
const session = useAtlas9.getClient()?.startSession();
|
|
2886
|
+
const insertedAreaIds = [];
|
|
2887
|
+
try {
|
|
2888
|
+
session?.startTransaction();
|
|
2889
|
+
for (const row of dataArray) {
|
|
2890
|
+
if (!row?.AREA_NAME) {
|
|
2891
|
+
logger16.warn("Skipping row with missing AREA_NAME:", row);
|
|
2892
|
+
continue;
|
|
2893
|
+
}
|
|
2894
|
+
try {
|
|
2895
|
+
const insertedId = await _create(
|
|
2896
|
+
{
|
|
2897
|
+
name: String(row.AREA_NAME).trim(),
|
|
2898
|
+
site,
|
|
2899
|
+
createdBy
|
|
2900
|
+
},
|
|
2901
|
+
session
|
|
2902
|
+
);
|
|
2903
|
+
insertedAreaIds.push(insertedId);
|
|
2904
|
+
} catch (error) {
|
|
2905
|
+
logger16.error(
|
|
2906
|
+
`Error creating area "${row.AREA_NAME}":`,
|
|
2907
|
+
error.message
|
|
2908
|
+
);
|
|
2909
|
+
continue;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
await session?.commitTransaction();
|
|
2913
|
+
logger16.info(`Successfully uploaded ${insertedAreaIds.length} areas`);
|
|
2914
|
+
return {
|
|
2915
|
+
message: `Successfully uploaded ${insertedAreaIds.length} areas`
|
|
2916
|
+
};
|
|
2917
|
+
} catch (error) {
|
|
2918
|
+
await session?.abortTransaction();
|
|
2919
|
+
logger16.error("Error while uploading area information", error);
|
|
2920
|
+
throw error;
|
|
2921
|
+
} finally {
|
|
2922
|
+
session?.endSession();
|
|
2612
2923
|
}
|
|
2613
|
-
return await _updateById(id, scheduleTaskArea);
|
|
2614
|
-
}
|
|
2615
|
-
async function deleteById(id) {
|
|
2616
|
-
return await _deleteById(id);
|
|
2617
2924
|
}
|
|
2618
2925
|
return {
|
|
2619
|
-
|
|
2620
|
-
create,
|
|
2621
|
-
updateById,
|
|
2622
|
-
deleteById
|
|
2926
|
+
uploadByFile
|
|
2623
2927
|
};
|
|
2624
2928
|
}
|
|
2625
2929
|
|
|
2626
2930
|
// src/controllers/hygiene-schedule-task-area.controller.ts
|
|
2627
|
-
import { BadRequestError as BadRequestError19, logger as
|
|
2931
|
+
import { BadRequestError as BadRequestError19, logger as logger17 } from "@iservice365/node-server-utils";
|
|
2628
2932
|
import Joi10 from "joi";
|
|
2629
2933
|
function useScheduleTaskAreaController() {
|
|
2934
|
+
const { uploadByFile: _uploadByFile } = useScheduleTaskAreaService();
|
|
2630
2935
|
const {
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2936
|
+
getScheduleTaskAreas: _getAll,
|
|
2937
|
+
createScheduleTaskArea: _createScheduleTaskArea,
|
|
2938
|
+
updateScheduleTaskArea: _updateScheduleTaskArea,
|
|
2939
|
+
deleteScheduleTaskArea: _deleteById,
|
|
2940
|
+
getAreaById: _getAreaById
|
|
2941
|
+
} = useScheduleTaskAreaRepository();
|
|
2636
2942
|
async function getAll(req, res, next) {
|
|
2637
2943
|
const query = req.query;
|
|
2638
2944
|
const validation = Joi10.object({
|
|
2639
2945
|
page: Joi10.number().min(1).optional().allow("", null),
|
|
2640
2946
|
limit: Joi10.number().min(1).optional().allow("", null),
|
|
2641
2947
|
search: Joi10.string().optional().allow("", null),
|
|
2948
|
+
startDate: Joi10.alternatives().try(Joi10.date(), Joi10.string()).optional().allow("", null),
|
|
2949
|
+
endDate: Joi10.alternatives().try(Joi10.date(), Joi10.string()).optional().allow("", null),
|
|
2642
2950
|
site: Joi10.string().hex().optional().allow("", null)
|
|
2643
2951
|
});
|
|
2644
2952
|
const { error } = validation.validate(query);
|
|
@@ -2647,84 +2955,71 @@ function useScheduleTaskAreaController() {
|
|
|
2647
2955
|
return;
|
|
2648
2956
|
}
|
|
2649
2957
|
const page = parseInt(req.query.page) ?? 1;
|
|
2650
|
-
|
|
2651
|
-
limit = isNaN(limit) ? 20 : limit;
|
|
2652
|
-
const sort = req.query.sort ? String(req.query.sort).split(",") : "";
|
|
2653
|
-
const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
|
|
2654
|
-
const sortObj = {};
|
|
2655
|
-
if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
|
|
2656
|
-
sort.forEach((field, index) => {
|
|
2657
|
-
sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
|
|
2658
|
-
});
|
|
2659
|
-
}
|
|
2660
|
-
const site = req.query.site ?? "";
|
|
2958
|
+
const limit = parseInt(req.query.limit) ?? 20;
|
|
2661
2959
|
const search = req.query.search ?? "";
|
|
2960
|
+
const site = req.query.site ?? "";
|
|
2961
|
+
const startDate = req.query.startDate ?? "";
|
|
2962
|
+
const endDate = req.query.endDate ?? "";
|
|
2662
2963
|
try {
|
|
2663
|
-
const
|
|
2964
|
+
const data = await _getAll({
|
|
2664
2965
|
page,
|
|
2665
2966
|
limit,
|
|
2666
|
-
|
|
2967
|
+
search,
|
|
2667
2968
|
site,
|
|
2668
|
-
|
|
2969
|
+
startDate,
|
|
2970
|
+
endDate
|
|
2669
2971
|
});
|
|
2670
|
-
res.json(
|
|
2972
|
+
res.json(data);
|
|
2671
2973
|
return;
|
|
2672
2974
|
} catch (error2) {
|
|
2673
2975
|
next(error2);
|
|
2976
|
+
return;
|
|
2674
2977
|
}
|
|
2675
2978
|
}
|
|
2676
2979
|
async function createScheduleTaskArea(req, res, next) {
|
|
2677
|
-
const
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
}
|
|
2683
|
-
const { error } =
|
|
2980
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
2981
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
2982
|
+
{}
|
|
2983
|
+
) : {};
|
|
2984
|
+
const createdBy = cookies["user"] || "";
|
|
2985
|
+
const payload = { ...req.body, createdBy };
|
|
2986
|
+
const { error } = scheduleTaskAreaSchema.validate(payload);
|
|
2684
2987
|
if (error) {
|
|
2988
|
+
logger17.log({ level: "error", message: error.message });
|
|
2685
2989
|
next(new BadRequestError19(error.message));
|
|
2686
|
-
logger16.info(`Controller: ${error.message}`);
|
|
2687
2990
|
return;
|
|
2688
2991
|
}
|
|
2689
2992
|
try {
|
|
2690
|
-
const
|
|
2691
|
-
res.status(201).json(
|
|
2993
|
+
const id = await _createScheduleTaskArea(payload);
|
|
2994
|
+
res.status(201).json({ message: "Area created successfully.", id });
|
|
2692
2995
|
return;
|
|
2693
2996
|
} catch (error2) {
|
|
2997
|
+
logger17.log({ level: "error", message: error2.message });
|
|
2694
2998
|
next(error2);
|
|
2999
|
+
return;
|
|
2695
3000
|
}
|
|
2696
3001
|
}
|
|
2697
3002
|
async function updateScheduleTaskArea(req, res, next) {
|
|
2698
|
-
const {
|
|
2699
|
-
const id = req.params.id;
|
|
3003
|
+
const payload = { id: req.params.id, ...req.body };
|
|
2700
3004
|
const schema = Joi10.object({
|
|
2701
3005
|
id: Joi10.string().hex().required(),
|
|
2702
|
-
name: Joi10.string().required()
|
|
2703
|
-
site: Joi10.string().required(),
|
|
2704
|
-
checklist: Joi10.array().optional().items(
|
|
2705
|
-
Joi10.object({
|
|
2706
|
-
_id: Joi10.string().optional().required(),
|
|
2707
|
-
name: Joi10.string().optional().required()
|
|
2708
|
-
})
|
|
2709
|
-
),
|
|
2710
|
-
createdBy: Joi10.string().hex().optional().allow("", null)
|
|
3006
|
+
name: Joi10.string().required()
|
|
2711
3007
|
});
|
|
2712
|
-
const { error } = schema.validate(
|
|
3008
|
+
const { error } = schema.validate(payload);
|
|
2713
3009
|
if (error) {
|
|
2714
3010
|
next(new BadRequestError19(error.message));
|
|
2715
|
-
|
|
3011
|
+
logger17.info(`Controller: ${error.message}`);
|
|
2716
3012
|
return;
|
|
2717
3013
|
}
|
|
2718
3014
|
try {
|
|
2719
|
-
const
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
site
|
|
2723
|
-
});
|
|
2724
|
-
res.json(result);
|
|
3015
|
+
const { id, ...value } = payload;
|
|
3016
|
+
await _updateScheduleTaskArea(id, value);
|
|
3017
|
+
res.json({ message: "Area updated successfully." });
|
|
2725
3018
|
return;
|
|
2726
3019
|
} catch (error2) {
|
|
3020
|
+
logger17.log({ level: "error", message: error2.message });
|
|
2727
3021
|
next(error2);
|
|
3022
|
+
return;
|
|
2728
3023
|
}
|
|
2729
3024
|
}
|
|
2730
3025
|
async function deleteScheduleTaskArea(req, res, next) {
|
|
@@ -2734,39 +3029,1655 @@ function useScheduleTaskAreaController() {
|
|
|
2734
3029
|
});
|
|
2735
3030
|
const { error } = validation.validate({ id });
|
|
2736
3031
|
if (error) {
|
|
3032
|
+
logger17.log({ level: "error", message: error.message });
|
|
2737
3033
|
next(new BadRequestError19(error.message));
|
|
2738
3034
|
return;
|
|
2739
3035
|
}
|
|
2740
3036
|
try {
|
|
2741
|
-
|
|
2742
|
-
res.json(message);
|
|
3037
|
+
await _deleteById(id);
|
|
3038
|
+
res.json({ message: "Area deleted successfully." });
|
|
3039
|
+
return;
|
|
3040
|
+
} catch (error2) {
|
|
3041
|
+
logger17.log({ level: "error", message: error2.message });
|
|
3042
|
+
next(error2);
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
async function uploadByFile(req, res, next) {
|
|
3047
|
+
if (!req.file) {
|
|
3048
|
+
next(new BadRequestError19("File is required!"));
|
|
3049
|
+
return;
|
|
3050
|
+
}
|
|
3051
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3052
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3053
|
+
{}
|
|
3054
|
+
) : {};
|
|
3055
|
+
const createdBy = cookies["user"] || "";
|
|
3056
|
+
const { site } = req.body;
|
|
3057
|
+
const schema = Joi10.object({
|
|
3058
|
+
site: Joi10.string().hex().optional().allow("", null),
|
|
3059
|
+
createdBy: Joi10.string().hex().required()
|
|
3060
|
+
});
|
|
3061
|
+
const { error } = schema.validate({ site, createdBy });
|
|
3062
|
+
if (error) {
|
|
3063
|
+
logger17.log({ level: "error", message: error.message });
|
|
3064
|
+
next(new BadRequestError19(error.message));
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
try {
|
|
3068
|
+
const xlsData = await convertBufferFile(req.file.buffer);
|
|
3069
|
+
const dataJson = JSON.stringify(xlsData);
|
|
3070
|
+
const result = await _uploadByFile({ dataJson, createdBy, site });
|
|
3071
|
+
return res.status(201).json(result);
|
|
3072
|
+
} catch (error2) {
|
|
3073
|
+
logger17.log({ level: "error", message: error2.message });
|
|
3074
|
+
next(error2);
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
async function getAreaById(req, res, next) {
|
|
3079
|
+
const validation = Joi10.string().hex().required();
|
|
3080
|
+
const _id = req.params.id;
|
|
3081
|
+
const { error } = validation.validate(_id);
|
|
3082
|
+
if (error) {
|
|
3083
|
+
logger17.log({ level: "error", message: error.message });
|
|
3084
|
+
next(new BadRequestError19(error.message));
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
3087
|
+
try {
|
|
3088
|
+
const data = await _getAreaById(_id);
|
|
3089
|
+
res.json(data);
|
|
2743
3090
|
return;
|
|
2744
3091
|
} catch (error2) {
|
|
3092
|
+
logger17.log({ level: "error", message: error2.message });
|
|
2745
3093
|
next(error2);
|
|
3094
|
+
return;
|
|
2746
3095
|
}
|
|
2747
3096
|
}
|
|
2748
3097
|
return {
|
|
2749
3098
|
getAll,
|
|
3099
|
+
getAreaById,
|
|
2750
3100
|
createScheduleTaskArea,
|
|
2751
3101
|
updateScheduleTaskArea,
|
|
2752
|
-
deleteScheduleTaskArea
|
|
3102
|
+
deleteScheduleTaskArea,
|
|
3103
|
+
uploadByFile
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
// src/models/hygiene-area-checklist.model.ts
|
|
3108
|
+
import { BadRequestError as BadRequestError20, logger as logger18 } from "@iservice365/node-server-utils";
|
|
3109
|
+
import Joi11 from "joi";
|
|
3110
|
+
import { ObjectId as ObjectId11 } from "mongodb";
|
|
3111
|
+
var areaChecklistSchema = Joi11.object({
|
|
3112
|
+
type: Joi11.string().required().valid(...allowedTypes),
|
|
3113
|
+
site: Joi11.string().hex().required(),
|
|
3114
|
+
parentChecklist: Joi11.string().hex().required(),
|
|
3115
|
+
area: Joi11.string().hex().required(),
|
|
3116
|
+
name: Joi11.string().optional().allow("", null),
|
|
3117
|
+
createdBy: Joi11.string().hex().optional().allow("", null)
|
|
3118
|
+
});
|
|
3119
|
+
function MAreaChecklist(value) {
|
|
3120
|
+
const { error } = areaChecklistSchema.validate(value);
|
|
3121
|
+
if (error) {
|
|
3122
|
+
logger18.info(`Hygiene Checklist Area Model: ${error.message}`);
|
|
3123
|
+
throw new BadRequestError20(error.message);
|
|
3124
|
+
}
|
|
3125
|
+
if (value.site) {
|
|
3126
|
+
try {
|
|
3127
|
+
value.site = new ObjectId11(value.site);
|
|
3128
|
+
} catch (error2) {
|
|
3129
|
+
throw new BadRequestError20("Invalid site ID format.");
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
if (value.parentChecklist) {
|
|
3133
|
+
try {
|
|
3134
|
+
value.parentChecklist = new ObjectId11(value.parentChecklist);
|
|
3135
|
+
} catch (error2) {
|
|
3136
|
+
throw new BadRequestError20("Invalid checklist ID format.");
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
if (value.area) {
|
|
3140
|
+
try {
|
|
3141
|
+
value.area = new ObjectId11(value.area);
|
|
3142
|
+
} catch (error2) {
|
|
3143
|
+
throw new BadRequestError20("Invalid area ID format.");
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
if (value.createdBy) {
|
|
3147
|
+
try {
|
|
3148
|
+
value.createdBy = new ObjectId11(value.createdBy);
|
|
3149
|
+
} catch (error2) {
|
|
3150
|
+
throw new BadRequestError20("Invalid createdBy ID format.");
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
if (value.acceptedBy) {
|
|
3154
|
+
try {
|
|
3155
|
+
value.acceptedBy = new ObjectId11(value.acceptedBy);
|
|
3156
|
+
} catch (error2) {
|
|
3157
|
+
throw new BadRequestError20("Invalid acceptedBy ID format.");
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
return {
|
|
3161
|
+
type: value.type,
|
|
3162
|
+
site: value.site,
|
|
3163
|
+
parentChecklist: value.parentChecklist,
|
|
3164
|
+
area: value.area,
|
|
3165
|
+
name: value.name ?? "",
|
|
3166
|
+
status: value.status ?? "To Do",
|
|
3167
|
+
createdBy: value.createdBy ?? "",
|
|
3168
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
3169
|
+
acceptedBy: value.acceptedBy ?? "",
|
|
3170
|
+
acceptedAt: value.acceptedAt ?? "",
|
|
3171
|
+
startedAt: value.startedAt ?? "",
|
|
3172
|
+
endedAt: value.endedAt ?? "",
|
|
3173
|
+
signature: value.signature ?? "",
|
|
3174
|
+
updatedAt: value.updatedAt ?? ""
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
// src/repositories/hygiene-area-checklist.repository.ts
|
|
3179
|
+
import {
|
|
3180
|
+
BadRequestError as BadRequestError21,
|
|
3181
|
+
InternalServerError as InternalServerError7,
|
|
3182
|
+
logger as logger19,
|
|
3183
|
+
makeCacheKey as makeCacheKey6,
|
|
3184
|
+
paginate as paginate6,
|
|
3185
|
+
useAtlas as useAtlas10,
|
|
3186
|
+
useCache as useCache6
|
|
3187
|
+
} from "@iservice365/node-server-utils";
|
|
3188
|
+
import { ObjectId as ObjectId12 } from "mongodb";
|
|
3189
|
+
function useAreaChecklistRepo() {
|
|
3190
|
+
const db = useAtlas10.getDb();
|
|
3191
|
+
if (!db) {
|
|
3192
|
+
throw new InternalServerError7("Unable to connect to server.");
|
|
3193
|
+
}
|
|
3194
|
+
const namespace_collection = "hygiene-checklist.areas";
|
|
3195
|
+
const unit_checklist_collection = "hygiene-checklist.units";
|
|
3196
|
+
const collection = db.collection(namespace_collection);
|
|
3197
|
+
const unitChecklistCollection = db.collection(unit_checklist_collection);
|
|
3198
|
+
const { delNamespace, setCache, getCache } = useCache6(namespace_collection);
|
|
3199
|
+
const { delNamespace: delUnitNamespace } = useCache6(
|
|
3200
|
+
unit_checklist_collection
|
|
3201
|
+
);
|
|
3202
|
+
async function createIndex() {
|
|
3203
|
+
try {
|
|
3204
|
+
await collection.createIndexes([
|
|
3205
|
+
{ key: { type: 1 } },
|
|
3206
|
+
{ key: { site: 1 } },
|
|
3207
|
+
{ key: { parentChecklist: 1 } },
|
|
3208
|
+
{ key: { area: 1 } },
|
|
3209
|
+
{ key: { createdAt: 1 } },
|
|
3210
|
+
{ key: { acceptedBy: 1 } }
|
|
3211
|
+
]);
|
|
3212
|
+
} catch (error) {
|
|
3213
|
+
throw new InternalServerError7(
|
|
3214
|
+
"Failed to create index on hygiene checklist area."
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
async function createTextIndex() {
|
|
3219
|
+
try {
|
|
3220
|
+
await collection.createIndex({ name: "text" });
|
|
3221
|
+
} catch (error) {
|
|
3222
|
+
throw new InternalServerError7(
|
|
3223
|
+
"Failed to create text index on hygiene checklist area."
|
|
3224
|
+
);
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
async function createAreaChecklist(value, session) {
|
|
3228
|
+
try {
|
|
3229
|
+
const siteId = new ObjectId12(value.site);
|
|
3230
|
+
const parentChecklistId = new ObjectId12(value.parentChecklist);
|
|
3231
|
+
const areaId = new ObjectId12(value.area);
|
|
3232
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
3233
|
+
const startOfDay = new Date(currentDate);
|
|
3234
|
+
startOfDay.setUTCHours(0, 0, 0, 0);
|
|
3235
|
+
const endOfDay = new Date(currentDate);
|
|
3236
|
+
endOfDay.setUTCHours(23, 59, 59, 999);
|
|
3237
|
+
const existingChecklist = await collection.findOne({
|
|
3238
|
+
type: value.type,
|
|
3239
|
+
site: siteId,
|
|
3240
|
+
parentChecklist: parentChecklistId,
|
|
3241
|
+
area: areaId,
|
|
3242
|
+
createdAt: {
|
|
3243
|
+
$gte: startOfDay,
|
|
3244
|
+
$lte: endOfDay
|
|
3245
|
+
}
|
|
3246
|
+
});
|
|
3247
|
+
if (existingChecklist) {
|
|
3248
|
+
logger19.info(
|
|
3249
|
+
`Area checklist already exists for area ${areaId} on ${currentDate.toISOString().split("T")[0]}`
|
|
3250
|
+
);
|
|
3251
|
+
return existingChecklist._id;
|
|
3252
|
+
}
|
|
3253
|
+
const processedValue = MAreaChecklist(value);
|
|
3254
|
+
const result = await collection.insertOne(processedValue, { session });
|
|
3255
|
+
delNamespace().then(() => {
|
|
3256
|
+
logger19.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3257
|
+
}).catch((err) => {
|
|
3258
|
+
logger19.error(
|
|
3259
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3260
|
+
err
|
|
3261
|
+
);
|
|
3262
|
+
});
|
|
3263
|
+
return result.insertedId;
|
|
3264
|
+
} catch (error) {
|
|
3265
|
+
throw error;
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
async function getAllAreaChecklist({
|
|
3269
|
+
page = 1,
|
|
3270
|
+
limit = 10,
|
|
3271
|
+
search = "",
|
|
3272
|
+
site,
|
|
3273
|
+
type,
|
|
3274
|
+
parentChecklist
|
|
3275
|
+
}) {
|
|
3276
|
+
page = page > 0 ? page - 1 : 0;
|
|
3277
|
+
const query = { type };
|
|
3278
|
+
const cacheOptions = {
|
|
3279
|
+
page,
|
|
3280
|
+
limit
|
|
3281
|
+
};
|
|
3282
|
+
try {
|
|
3283
|
+
query.site = new ObjectId12(site);
|
|
3284
|
+
cacheOptions.site = site.toString();
|
|
3285
|
+
} catch (error) {
|
|
3286
|
+
throw new BadRequestError21("Invalid site ID format.");
|
|
3287
|
+
}
|
|
3288
|
+
try {
|
|
3289
|
+
query.parentChecklist = new ObjectId12(parentChecklist);
|
|
3290
|
+
cacheOptions.parentChecklist = parentChecklist.toString();
|
|
3291
|
+
} catch (error) {
|
|
3292
|
+
throw new BadRequestError21("Invalid parent checklist ID format.");
|
|
3293
|
+
}
|
|
3294
|
+
if (search) {
|
|
3295
|
+
query.$text = { $search: search };
|
|
3296
|
+
cacheOptions.search = search;
|
|
3297
|
+
}
|
|
3298
|
+
const cacheKey = makeCacheKey6(namespace_collection, cacheOptions);
|
|
3299
|
+
const cachedData = await getCache(cacheKey);
|
|
3300
|
+
if (cachedData) {
|
|
3301
|
+
logger19.info(`Cache hit for key: ${cacheKey}`);
|
|
3302
|
+
return cachedData;
|
|
3303
|
+
}
|
|
3304
|
+
try {
|
|
3305
|
+
const pipeline = [
|
|
3306
|
+
{ $match: query },
|
|
3307
|
+
{
|
|
3308
|
+
$lookup: {
|
|
3309
|
+
from: "users",
|
|
3310
|
+
localField: "acceptedBy",
|
|
3311
|
+
foreignField: "_id",
|
|
3312
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
3313
|
+
as: "acceptedBy"
|
|
3314
|
+
}
|
|
3315
|
+
},
|
|
3316
|
+
{
|
|
3317
|
+
$unwind: {
|
|
3318
|
+
path: "$acceptedBy",
|
|
3319
|
+
preserveNullAndEmptyArrays: true
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
{
|
|
3323
|
+
$project: {
|
|
3324
|
+
name: 1,
|
|
3325
|
+
acceptedByName: "$acceptedBy.name",
|
|
3326
|
+
status: 1,
|
|
3327
|
+
createdAt: 1
|
|
3328
|
+
}
|
|
3329
|
+
},
|
|
3330
|
+
{ $sort: { _id: -1 } },
|
|
3331
|
+
{ $skip: page * limit },
|
|
3332
|
+
{ $limit: limit }
|
|
3333
|
+
];
|
|
3334
|
+
const items = await collection.aggregate(pipeline).toArray();
|
|
3335
|
+
const length = await collection.countDocuments(query);
|
|
3336
|
+
const data = paginate6(items, page, limit, length);
|
|
3337
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3338
|
+
logger19.info(`Cache set for key: ${cacheKey}`);
|
|
3339
|
+
}).catch((err) => {
|
|
3340
|
+
logger19.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3341
|
+
});
|
|
3342
|
+
return data;
|
|
3343
|
+
} catch (error) {
|
|
3344
|
+
throw error;
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
async function getAreaChecklistHistory({
|
|
3348
|
+
page = 1,
|
|
3349
|
+
limit = 10,
|
|
3350
|
+
search = "",
|
|
3351
|
+
site,
|
|
3352
|
+
type,
|
|
3353
|
+
parentChecklist,
|
|
3354
|
+
status,
|
|
3355
|
+
createdAt,
|
|
3356
|
+
user
|
|
3357
|
+
}) {
|
|
3358
|
+
page = page > 0 ? page - 1 : 0;
|
|
3359
|
+
const query = { type };
|
|
3360
|
+
const cacheOptions = {
|
|
3361
|
+
page,
|
|
3362
|
+
limit
|
|
3363
|
+
};
|
|
3364
|
+
try {
|
|
3365
|
+
query.site = new ObjectId12(site);
|
|
3366
|
+
cacheOptions.site = site.toString();
|
|
3367
|
+
} catch (error) {
|
|
3368
|
+
throw new BadRequestError21("Invalid site ID format.");
|
|
3369
|
+
}
|
|
3370
|
+
try {
|
|
3371
|
+
query.parentChecklist = new ObjectId12(parentChecklist);
|
|
3372
|
+
cacheOptions.parentChecklist = parentChecklist.toString();
|
|
3373
|
+
} catch (error) {
|
|
3374
|
+
throw new BadRequestError21("Invalid parent checklist ID format.");
|
|
3375
|
+
}
|
|
3376
|
+
if (search) {
|
|
3377
|
+
query.$text = { $search: search };
|
|
3378
|
+
cacheOptions.search = search;
|
|
3379
|
+
}
|
|
3380
|
+
if (createdAt) {
|
|
3381
|
+
query.createdAt = {
|
|
3382
|
+
$gte: /* @__PURE__ */ new Date(`${createdAt}T00:00:00Z`),
|
|
3383
|
+
$lte: /* @__PURE__ */ new Date(`${createdAt}T23:59:59Z`)
|
|
3384
|
+
};
|
|
3385
|
+
cacheOptions.createdAt = new Date(createdAt).toISOString().split("T")[0];
|
|
3386
|
+
}
|
|
3387
|
+
if (status) {
|
|
3388
|
+
query.status = status;
|
|
3389
|
+
cacheOptions.status = status;
|
|
3390
|
+
} else {
|
|
3391
|
+
query.status = { $in: ["In Progress", "Completed"] };
|
|
3392
|
+
}
|
|
3393
|
+
if (user) {
|
|
3394
|
+
try {
|
|
3395
|
+
query.acceptedBy = new ObjectId12(user);
|
|
3396
|
+
cacheOptions.user = user.toString();
|
|
3397
|
+
} catch (error) {
|
|
3398
|
+
throw new BadRequestError21("Invalid user ID format.");
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
const cacheKey = makeCacheKey6(namespace_collection, cacheOptions);
|
|
3402
|
+
const cachedData = await getCache(cacheKey);
|
|
3403
|
+
if (cachedData) {
|
|
3404
|
+
logger19.info(`Cache hit for key: ${cacheKey}`);
|
|
3405
|
+
return cachedData;
|
|
3406
|
+
}
|
|
3407
|
+
try {
|
|
3408
|
+
const pipeline = [
|
|
3409
|
+
{ $match: query },
|
|
3410
|
+
{
|
|
3411
|
+
$lookup: {
|
|
3412
|
+
from: "users",
|
|
3413
|
+
localField: "acceptedBy",
|
|
3414
|
+
foreignField: "_id",
|
|
3415
|
+
pipeline: [
|
|
3416
|
+
...search ? [{ $match: { name: { $regex: search, $options: "i" } } }] : [],
|
|
3417
|
+
{ $project: { name: 1 } }
|
|
3418
|
+
],
|
|
3419
|
+
as: "acceptedBy"
|
|
3420
|
+
}
|
|
3421
|
+
},
|
|
3422
|
+
{
|
|
3423
|
+
$unwind: {
|
|
3424
|
+
path: "$acceptedBy",
|
|
3425
|
+
preserveNullAndEmptyArrays: true
|
|
3426
|
+
}
|
|
3427
|
+
},
|
|
3428
|
+
{
|
|
3429
|
+
$project: {
|
|
3430
|
+
name: 1,
|
|
3431
|
+
createdAt: 1,
|
|
3432
|
+
acceptedByName: "$acceptedBy.name",
|
|
3433
|
+
status: 1,
|
|
3434
|
+
startedAt: 1,
|
|
3435
|
+
endedAt: 1
|
|
3436
|
+
}
|
|
3437
|
+
},
|
|
3438
|
+
{ $sort: { status: 1 } },
|
|
3439
|
+
{ $skip: page * limit },
|
|
3440
|
+
{ $limit: limit }
|
|
3441
|
+
];
|
|
3442
|
+
const items = await collection.aggregate(pipeline).toArray();
|
|
3443
|
+
const length = await collection.countDocuments(query);
|
|
3444
|
+
const data = paginate6(items, page, limit, length);
|
|
3445
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3446
|
+
logger19.info(`Cache set for key: ${cacheKey}`);
|
|
3447
|
+
}).catch((err) => {
|
|
3448
|
+
logger19.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3449
|
+
});
|
|
3450
|
+
return data;
|
|
3451
|
+
} catch (error) {
|
|
3452
|
+
throw error;
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
async function getAreaChecklistHistoryDetails(_id) {
|
|
3456
|
+
try {
|
|
3457
|
+
_id = new ObjectId12(_id);
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
throw new BadRequestError21("Invalid area checklist ID format.");
|
|
3460
|
+
}
|
|
3461
|
+
const cacheKey = makeCacheKey6(namespace_collection, {
|
|
3462
|
+
_id: _id.toString()
|
|
3463
|
+
});
|
|
3464
|
+
const cachedData = await getCache(cacheKey);
|
|
3465
|
+
if (cachedData) {
|
|
3466
|
+
logger19.info(`Cache hit for key: ${cacheKey}`);
|
|
3467
|
+
return cachedData;
|
|
3468
|
+
}
|
|
3469
|
+
try {
|
|
3470
|
+
const areaPipeline = [
|
|
3471
|
+
{ $match: { _id } },
|
|
3472
|
+
{
|
|
3473
|
+
$lookup: {
|
|
3474
|
+
from: "users",
|
|
3475
|
+
localField: "createdBy",
|
|
3476
|
+
foreignField: "_id",
|
|
3477
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
3478
|
+
as: "createdBy"
|
|
3479
|
+
}
|
|
3480
|
+
},
|
|
3481
|
+
{
|
|
3482
|
+
$unwind: {
|
|
3483
|
+
path: "$createdBy",
|
|
3484
|
+
preserveNullAndEmptyArrays: true
|
|
3485
|
+
}
|
|
3486
|
+
},
|
|
3487
|
+
{
|
|
3488
|
+
$lookup: {
|
|
3489
|
+
from: "users",
|
|
3490
|
+
localField: "acceptedBy",
|
|
3491
|
+
foreignField: "_id",
|
|
3492
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
3493
|
+
as: "acceptedBy"
|
|
3494
|
+
}
|
|
3495
|
+
},
|
|
3496
|
+
{
|
|
3497
|
+
$unwind: {
|
|
3498
|
+
path: "$acceptedBy",
|
|
3499
|
+
preserveNullAndEmptyArrays: true
|
|
3500
|
+
}
|
|
3501
|
+
},
|
|
3502
|
+
{
|
|
3503
|
+
$project: {
|
|
3504
|
+
name: 1,
|
|
3505
|
+
createdAt: 1,
|
|
3506
|
+
createdByName: "$createdBy.name",
|
|
3507
|
+
startedAt: 1,
|
|
3508
|
+
endedAt: 1,
|
|
3509
|
+
status: 1,
|
|
3510
|
+
signature: 1,
|
|
3511
|
+
acceptedByName: "$acceptedBy.name"
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
];
|
|
3515
|
+
const unitPipeline = [
|
|
3516
|
+
{ $match: { areaChecklist: _id } },
|
|
3517
|
+
{
|
|
3518
|
+
$project: {
|
|
3519
|
+
name: 1,
|
|
3520
|
+
remarks: "$metadata.remarks",
|
|
3521
|
+
attachments: "$metadata.attachments",
|
|
3522
|
+
approve: { $ifNull: ["$approve", null] },
|
|
3523
|
+
reject: { $ifNull: ["$reject", null] }
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
];
|
|
3527
|
+
const [area, units] = await Promise.all([
|
|
3528
|
+
collection.aggregate(areaPipeline).toArray(),
|
|
3529
|
+
unitChecklistCollection.aggregate(unitPipeline).toArray()
|
|
3530
|
+
]);
|
|
3531
|
+
if (!area.length) {
|
|
3532
|
+
throw new BadRequestError21("Area checklist not found.");
|
|
3533
|
+
}
|
|
3534
|
+
const items = {
|
|
3535
|
+
area: area[0],
|
|
3536
|
+
units
|
|
3537
|
+
};
|
|
3538
|
+
setCache(cacheKey, items, 15 * 60).then(() => {
|
|
3539
|
+
logger19.info(`Cache set for key: ${cacheKey}`);
|
|
3540
|
+
}).catch((err) => {
|
|
3541
|
+
logger19.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3542
|
+
});
|
|
3543
|
+
return items;
|
|
3544
|
+
} catch (error) {
|
|
3545
|
+
throw error;
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
async function acceptAreaChecklist(_id, acceptedBy) {
|
|
3549
|
+
try {
|
|
3550
|
+
_id = new ObjectId12(_id);
|
|
3551
|
+
} catch (error) {
|
|
3552
|
+
throw new BadRequestError21("Invalid area ID format.");
|
|
3553
|
+
}
|
|
3554
|
+
try {
|
|
3555
|
+
acceptedBy = new ObjectId12(acceptedBy);
|
|
3556
|
+
} catch (error) {
|
|
3557
|
+
throw new BadRequestError21("Invalid acceptedBy ID format.");
|
|
3558
|
+
}
|
|
3559
|
+
try {
|
|
3560
|
+
const now = /* @__PURE__ */ new Date();
|
|
3561
|
+
const updateValue = {
|
|
3562
|
+
acceptedBy,
|
|
3563
|
+
acceptedAt: now,
|
|
3564
|
+
startedAt: now,
|
|
3565
|
+
updatedAt: now
|
|
3566
|
+
};
|
|
3567
|
+
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
3568
|
+
if (res.modifiedCount === 0) {
|
|
3569
|
+
throw new InternalServerError7("Unable to update cleaning area.");
|
|
3570
|
+
}
|
|
3571
|
+
delNamespace().then(() => {
|
|
3572
|
+
logger19.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3573
|
+
}).catch((err) => {
|
|
3574
|
+
logger19.error(
|
|
3575
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3576
|
+
err
|
|
3577
|
+
);
|
|
3578
|
+
});
|
|
3579
|
+
return res.modifiedCount;
|
|
3580
|
+
} catch (error) {
|
|
3581
|
+
throw error;
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
async function attachImageAreaChecklist(_id, attachments, session) {
|
|
3585
|
+
try {
|
|
3586
|
+
_id = new ObjectId12(_id);
|
|
3587
|
+
} catch (error) {
|
|
3588
|
+
throw new BadRequestError21("Invalid area checklist ID format.");
|
|
3589
|
+
}
|
|
3590
|
+
try {
|
|
3591
|
+
const now = /* @__PURE__ */ new Date();
|
|
3592
|
+
const updateValue = {
|
|
3593
|
+
metadata: { attachments },
|
|
3594
|
+
updatedAt: now
|
|
3595
|
+
};
|
|
3596
|
+
const res = await collection.updateOne(
|
|
3597
|
+
{ _id },
|
|
3598
|
+
{ $set: updateValue },
|
|
3599
|
+
{ session }
|
|
3600
|
+
);
|
|
3601
|
+
if (res.modifiedCount === 0) {
|
|
3602
|
+
throw new InternalServerError7(
|
|
3603
|
+
"Unable to update cleaning area checklist."
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
delNamespace().then(() => {
|
|
3607
|
+
logger19.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3608
|
+
}).catch((err) => {
|
|
3609
|
+
logger19.error(
|
|
3610
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3611
|
+
err
|
|
3612
|
+
);
|
|
3613
|
+
});
|
|
3614
|
+
delUnitNamespace().then(() => {
|
|
3615
|
+
logger19.info(
|
|
3616
|
+
`Cache cleared for namespace: ${unit_checklist_collection}`
|
|
3617
|
+
);
|
|
3618
|
+
}).catch((err) => {
|
|
3619
|
+
logger19.error(
|
|
3620
|
+
`Failed to clear cache for namespace: ${unit_checklist_collection}`,
|
|
3621
|
+
err
|
|
3622
|
+
);
|
|
3623
|
+
});
|
|
3624
|
+
return res.modifiedCount;
|
|
3625
|
+
} catch (error) {
|
|
3626
|
+
throw error;
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
async function submitAreaChecklist(_id, signature) {
|
|
3630
|
+
try {
|
|
3631
|
+
_id = new ObjectId12(_id);
|
|
3632
|
+
} catch (error) {
|
|
3633
|
+
throw new BadRequestError21("Invalid area ID format.");
|
|
3634
|
+
}
|
|
3635
|
+
try {
|
|
3636
|
+
const now = /* @__PURE__ */ new Date();
|
|
3637
|
+
const updateValue = {
|
|
3638
|
+
signature,
|
|
3639
|
+
status: "In Progress",
|
|
3640
|
+
endedAt: now,
|
|
3641
|
+
updatedAt: now
|
|
3642
|
+
};
|
|
3643
|
+
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
3644
|
+
if (res.modifiedCount === 0) {
|
|
3645
|
+
throw new InternalServerError7("Unable to update cleaning area.");
|
|
3646
|
+
}
|
|
3647
|
+
delNamespace().then(() => {
|
|
3648
|
+
logger19.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3649
|
+
}).catch((err) => {
|
|
3650
|
+
logger19.error(
|
|
3651
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3652
|
+
err
|
|
3653
|
+
);
|
|
3654
|
+
});
|
|
3655
|
+
return res.modifiedCount;
|
|
3656
|
+
} catch (error) {
|
|
3657
|
+
throw error;
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
async function completeAreaChecklist(_id, signature) {
|
|
3661
|
+
try {
|
|
3662
|
+
_id = new ObjectId12(_id);
|
|
3663
|
+
} catch (error) {
|
|
3664
|
+
throw new BadRequestError21("Invalid area ID format.");
|
|
3665
|
+
}
|
|
3666
|
+
try {
|
|
3667
|
+
const now = /* @__PURE__ */ new Date();
|
|
3668
|
+
const updateValue = {
|
|
3669
|
+
signature,
|
|
3670
|
+
status: "Completed",
|
|
3671
|
+
endedAt: now,
|
|
3672
|
+
updatedAt: now
|
|
3673
|
+
};
|
|
3674
|
+
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
3675
|
+
if (res.modifiedCount === 0) {
|
|
3676
|
+
throw new InternalServerError7("Unable to update cleaning area.");
|
|
3677
|
+
}
|
|
3678
|
+
delNamespace().then(() => {
|
|
3679
|
+
logger19.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3680
|
+
}).catch((err) => {
|
|
3681
|
+
logger19.error(
|
|
3682
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3683
|
+
err
|
|
3684
|
+
);
|
|
3685
|
+
});
|
|
3686
|
+
return res.modifiedCount;
|
|
3687
|
+
} catch (error) {
|
|
3688
|
+
throw error;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
return {
|
|
3692
|
+
createIndex,
|
|
3693
|
+
createTextIndex,
|
|
3694
|
+
createAreaChecklist,
|
|
3695
|
+
getAllAreaChecklist,
|
|
3696
|
+
getAreaChecklistHistory,
|
|
3697
|
+
getAreaChecklistHistoryDetails,
|
|
3698
|
+
acceptAreaChecklist,
|
|
3699
|
+
attachImageAreaChecklist,
|
|
3700
|
+
submitAreaChecklist,
|
|
3701
|
+
completeAreaChecklist
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
// src/controllers/hygiene-area-checklist.controller.ts
|
|
3706
|
+
import { BadRequestError as BadRequestError22, logger as logger21 } from "@iservice365/node-server-utils";
|
|
3707
|
+
import Joi12 from "joi";
|
|
3708
|
+
|
|
3709
|
+
// src/services/hygiene-area-checklist.service.ts
|
|
3710
|
+
import { logger as logger20 } from "@iservice365/node-server-utils";
|
|
3711
|
+
function useAreaChecklistService() {
|
|
3712
|
+
const { createAreaChecklist: _createAreaChecklist } = useAreaChecklistRepo();
|
|
3713
|
+
const { getAreas } = useAreaRepository();
|
|
3714
|
+
const { getToiletLocations } = useToiletLocationRepository();
|
|
3715
|
+
async function createAreaChecklist(value) {
|
|
3716
|
+
try {
|
|
3717
|
+
const results = [];
|
|
3718
|
+
if (value.type === "cleaner") {
|
|
3719
|
+
const cleanerAreasResult = await getAreas({
|
|
3720
|
+
site: value.site
|
|
3721
|
+
});
|
|
3722
|
+
const cleanerAreas = cleanerAreasResult.items || [];
|
|
3723
|
+
if (cleanerAreas.length === 0) {
|
|
3724
|
+
logger20.warn(`No cleaner areas found for site: ${value.site}`);
|
|
3725
|
+
return results;
|
|
3726
|
+
}
|
|
3727
|
+
for (const area of cleanerAreas) {
|
|
3728
|
+
const checklistData = {
|
|
3729
|
+
type: "cleaner",
|
|
3730
|
+
site: value.site,
|
|
3731
|
+
parentChecklist: value.parentChecklist,
|
|
3732
|
+
area: area._id.toString(),
|
|
3733
|
+
name: area.name,
|
|
3734
|
+
createdBy: value.createdBy
|
|
3735
|
+
};
|
|
3736
|
+
const insertedId = await _createAreaChecklist(checklistData);
|
|
3737
|
+
results.push(insertedId);
|
|
3738
|
+
}
|
|
3739
|
+
logger20.info(
|
|
3740
|
+
`Created ${results.length} cleaner area checklists for site: ${value.site}`
|
|
3741
|
+
);
|
|
3742
|
+
}
|
|
3743
|
+
if (value.type === "toilet") {
|
|
3744
|
+
const toiletAreasResult = await getToiletLocations({
|
|
3745
|
+
site: value.site
|
|
3746
|
+
});
|
|
3747
|
+
const toiletAreas = toiletAreasResult.items || [];
|
|
3748
|
+
if (toiletAreas.length === 0) {
|
|
3749
|
+
logger20.warn(`No toilet locations found for site: ${value.site}`);
|
|
3750
|
+
return results;
|
|
3751
|
+
}
|
|
3752
|
+
for (const toiletLocation of toiletAreas) {
|
|
3753
|
+
const checklistData = {
|
|
3754
|
+
type: "toilet",
|
|
3755
|
+
site: value.site,
|
|
3756
|
+
parentChecklist: value.parentChecklist,
|
|
3757
|
+
area: toiletLocation._id,
|
|
3758
|
+
name: toiletLocation.name,
|
|
3759
|
+
createdBy: value.createdBy
|
|
3760
|
+
};
|
|
3761
|
+
const insertedId = await _createAreaChecklist(checklistData);
|
|
3762
|
+
results.push(insertedId);
|
|
3763
|
+
}
|
|
3764
|
+
logger20.info(
|
|
3765
|
+
`Created ${results.length} toilet area checklists for site: ${value.site}`
|
|
3766
|
+
);
|
|
3767
|
+
}
|
|
3768
|
+
return results;
|
|
3769
|
+
} catch (error) {
|
|
3770
|
+
logger20.error(`Error generating area checklists:`, error);
|
|
3771
|
+
throw error;
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
return { createAreaChecklist };
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
// src/controllers/hygiene-area-checklist.controller.ts
|
|
3778
|
+
function useAreaChecklistController() {
|
|
3779
|
+
const {
|
|
3780
|
+
getAllAreaChecklist: _getAllAreaChecklist,
|
|
3781
|
+
getAreaChecklistHistory: _getAreaChecklistHistory,
|
|
3782
|
+
getAreaChecklistHistoryDetails: _getAreaChecklistHistoryDetails,
|
|
3783
|
+
acceptAreaChecklist: _acceptAreaChecklist,
|
|
3784
|
+
attachImageAreaChecklist: _attachImageAreaChecklist,
|
|
3785
|
+
submitAreaChecklist: _submitAreaChecklist,
|
|
3786
|
+
completeAreaChecklist: _completeAreaChecklist
|
|
3787
|
+
} = useAreaChecklistRepo();
|
|
3788
|
+
const { createAreaChecklist: _createAreaChecklist } = useAreaChecklistService();
|
|
3789
|
+
async function createAreaChecklist(req, res, next) {
|
|
3790
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3791
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3792
|
+
{}
|
|
3793
|
+
) : {};
|
|
3794
|
+
const createdBy = cookies["user"] || "";
|
|
3795
|
+
const payload = { ...req.body, ...req.params, createdBy };
|
|
3796
|
+
const validation = Joi12.object({
|
|
3797
|
+
type: Joi12.string().required().valid(...allowedTypes),
|
|
3798
|
+
site: Joi12.string().hex().required(),
|
|
3799
|
+
parentChecklist: Joi12.string().hex().required(),
|
|
3800
|
+
createdBy: Joi12.string().hex().optional().allow("", null)
|
|
3801
|
+
});
|
|
3802
|
+
const { error } = validation.validate(payload);
|
|
3803
|
+
if (error) {
|
|
3804
|
+
logger21.log({ level: "error", message: error.message });
|
|
3805
|
+
next(new BadRequestError22(error.message));
|
|
3806
|
+
return;
|
|
3807
|
+
}
|
|
3808
|
+
try {
|
|
3809
|
+
await _createAreaChecklist(payload);
|
|
3810
|
+
res.status(201).json({ message: "Area checklists generated successfully." });
|
|
3811
|
+
return;
|
|
3812
|
+
} catch (error2) {
|
|
3813
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3814
|
+
next(error2);
|
|
3815
|
+
return;
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
async function getAllAreaChecklist(req, res, next) {
|
|
3819
|
+
const query = { ...req.query, ...req.params };
|
|
3820
|
+
const validation = Joi12.object({
|
|
3821
|
+
page: Joi12.number().min(1).optional().allow("", null),
|
|
3822
|
+
limit: Joi12.number().min(1).optional().allow("", null),
|
|
3823
|
+
search: Joi12.string().optional().allow("", null),
|
|
3824
|
+
site: Joi12.string().hex().required(),
|
|
3825
|
+
type: Joi12.string().valid(...allowedTypes).required(),
|
|
3826
|
+
parentChecklist: Joi12.string().hex().required()
|
|
3827
|
+
});
|
|
3828
|
+
const { error } = validation.validate(query);
|
|
3829
|
+
if (error) {
|
|
3830
|
+
logger21.log({ level: "error", message: error.message });
|
|
3831
|
+
next(new BadRequestError22(error.message));
|
|
3832
|
+
return;
|
|
3833
|
+
}
|
|
3834
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
3835
|
+
const limit = parseInt(req.query.limit) ?? 20;
|
|
3836
|
+
const search = req.query.search ?? "";
|
|
3837
|
+
const site = req.params.site ?? "";
|
|
3838
|
+
const type = req.params.type ?? "";
|
|
3839
|
+
const parentChecklist = req.params.parentChecklist ?? "";
|
|
3840
|
+
try {
|
|
3841
|
+
const data = await _getAllAreaChecklist({
|
|
3842
|
+
page,
|
|
3843
|
+
limit,
|
|
3844
|
+
search,
|
|
3845
|
+
site,
|
|
3846
|
+
type,
|
|
3847
|
+
parentChecklist
|
|
3848
|
+
});
|
|
3849
|
+
res.json(data);
|
|
3850
|
+
return;
|
|
3851
|
+
} catch (error2) {
|
|
3852
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3853
|
+
next(error2);
|
|
3854
|
+
return;
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
async function getAreaChecklistHistory(req, res, next) {
|
|
3858
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3859
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3860
|
+
{}
|
|
3861
|
+
) : {};
|
|
3862
|
+
const user = cookies["user"] || "";
|
|
3863
|
+
const query = { ...req.query, ...req.params, user };
|
|
3864
|
+
const validation = Joi12.object({
|
|
3865
|
+
page: Joi12.number().min(1).optional().allow("", null),
|
|
3866
|
+
limit: Joi12.number().min(1).optional().allow("", null),
|
|
3867
|
+
search: Joi12.string().optional().allow("", null),
|
|
3868
|
+
site: Joi12.string().hex().required(),
|
|
3869
|
+
type: Joi12.string().valid(...allowedTypes).required(),
|
|
3870
|
+
parentChecklist: Joi12.string().hex().required(),
|
|
3871
|
+
status: Joi12.string().allow("", null, ...allowedStatus),
|
|
3872
|
+
createdAt: Joi12.alternatives().try(Joi12.date(), Joi12.string()).optional().allow("", null),
|
|
3873
|
+
user: Joi12.string().hex().optional().allow("", null)
|
|
3874
|
+
});
|
|
3875
|
+
const { error } = validation.validate(query);
|
|
3876
|
+
if (error) {
|
|
3877
|
+
logger21.log({ level: "error", message: error.message });
|
|
3878
|
+
next(new BadRequestError22(error.message));
|
|
3879
|
+
return;
|
|
3880
|
+
}
|
|
3881
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
3882
|
+
const limit = parseInt(req.query.limit) ?? 20;
|
|
3883
|
+
const search = req.query.search ?? "";
|
|
3884
|
+
const site = req.params.site ?? "";
|
|
3885
|
+
const type = req.params.type ?? "";
|
|
3886
|
+
const parentChecklist = req.params.parentChecklist ?? "";
|
|
3887
|
+
const status = req.query.status ?? "";
|
|
3888
|
+
const createdAt = req.query.createdAt ?? "";
|
|
3889
|
+
try {
|
|
3890
|
+
const data = await _getAreaChecklistHistory({
|
|
3891
|
+
page,
|
|
3892
|
+
limit,
|
|
3893
|
+
search,
|
|
3894
|
+
site,
|
|
3895
|
+
type,
|
|
3896
|
+
parentChecklist,
|
|
3897
|
+
status,
|
|
3898
|
+
createdAt,
|
|
3899
|
+
user
|
|
3900
|
+
});
|
|
3901
|
+
res.json(data);
|
|
3902
|
+
return;
|
|
3903
|
+
} catch (error2) {
|
|
3904
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3905
|
+
next(error2);
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
async function getAreaChecklistHistoryDetails(req, res, next) {
|
|
3910
|
+
const validation = Joi12.string().hex().required();
|
|
3911
|
+
const _id = req.params.id;
|
|
3912
|
+
const { error } = validation.validate(_id);
|
|
3913
|
+
if (error) {
|
|
3914
|
+
logger21.log({ level: "error", message: error.message });
|
|
3915
|
+
next(new BadRequestError22(error.message));
|
|
3916
|
+
return;
|
|
3917
|
+
}
|
|
3918
|
+
try {
|
|
3919
|
+
const data = await _getAreaChecklistHistoryDetails(_id);
|
|
3920
|
+
res.json(data);
|
|
3921
|
+
return;
|
|
3922
|
+
} catch (error2) {
|
|
3923
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3924
|
+
next(error2);
|
|
3925
|
+
return;
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
async function acceptAreaChecklist(req, res, next) {
|
|
3929
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
3930
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
3931
|
+
{}
|
|
3932
|
+
) : {};
|
|
3933
|
+
const acceptedBy = cookies["user"] || "";
|
|
3934
|
+
const payload = { id: req.params.id, acceptedBy };
|
|
3935
|
+
const validation = Joi12.object({
|
|
3936
|
+
id: Joi12.string().hex().required(),
|
|
3937
|
+
acceptedBy: Joi12.string().hex().required()
|
|
3938
|
+
});
|
|
3939
|
+
const { error } = validation.validate(payload);
|
|
3940
|
+
if (error) {
|
|
3941
|
+
logger21.log({ level: "error", message: error.message });
|
|
3942
|
+
next(new BadRequestError22(error.message));
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
try {
|
|
3946
|
+
await _acceptAreaChecklist(payload.id, payload.acceptedBy);
|
|
3947
|
+
res.json({ message: "Area checklist updated successfully." });
|
|
3948
|
+
return;
|
|
3949
|
+
} catch (error2) {
|
|
3950
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3951
|
+
next(error2);
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
async function attachImageAreaChecklist(req, res, next) {
|
|
3956
|
+
const payload = { id: req.params.id, attachments: req.body.attachments };
|
|
3957
|
+
const validation = Joi12.object({
|
|
3958
|
+
id: Joi12.string().hex().required(),
|
|
3959
|
+
attachments: Joi12.array().items(Joi12.string()).optional()
|
|
3960
|
+
});
|
|
3961
|
+
const { error } = validation.validate(payload);
|
|
3962
|
+
if (error) {
|
|
3963
|
+
logger21.log({ level: "error", message: error.message });
|
|
3964
|
+
next(new BadRequestError22(error.message));
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3967
|
+
try {
|
|
3968
|
+
await _attachImageAreaChecklist(payload.id, payload.attachments);
|
|
3969
|
+
res.json({ message: "Area checklist attachments updated successfully." });
|
|
3970
|
+
return;
|
|
3971
|
+
} catch (error2) {
|
|
3972
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3973
|
+
next(error2);
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
async function submitAreaChecklist(req, res, next) {
|
|
3978
|
+
const payload = { id: req.params.id, signature: req.body.signature };
|
|
3979
|
+
const validation = Joi12.object({
|
|
3980
|
+
id: Joi12.string().hex().required(),
|
|
3981
|
+
signature: Joi12.string().required()
|
|
3982
|
+
});
|
|
3983
|
+
const { error } = validation.validate(payload);
|
|
3984
|
+
if (error) {
|
|
3985
|
+
logger21.log({ level: "error", message: error.message });
|
|
3986
|
+
next(new BadRequestError22(error.message));
|
|
3987
|
+
return;
|
|
3988
|
+
}
|
|
3989
|
+
try {
|
|
3990
|
+
await _submitAreaChecklist(payload.id, payload.signature);
|
|
3991
|
+
res.json({ message: "Area checklist submitted successfully." });
|
|
3992
|
+
return;
|
|
3993
|
+
} catch (error2) {
|
|
3994
|
+
logger21.log({ level: "error", message: error2.message });
|
|
3995
|
+
next(error2);
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
async function completeAreaChecklist(req, res, next) {
|
|
4000
|
+
const payload = { id: req.params.id, signature: req.body.signature };
|
|
4001
|
+
const validation = Joi12.object({
|
|
4002
|
+
id: Joi12.string().hex().required(),
|
|
4003
|
+
signature: Joi12.string().required()
|
|
4004
|
+
});
|
|
4005
|
+
const { error } = validation.validate(payload);
|
|
4006
|
+
if (error) {
|
|
4007
|
+
logger21.log({ level: "error", message: error.message });
|
|
4008
|
+
next(new BadRequestError22(error.message));
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
try {
|
|
4012
|
+
await _completeAreaChecklist(payload.id, payload.signature);
|
|
4013
|
+
res.json({ message: "Area checklist completed successfully." });
|
|
4014
|
+
return;
|
|
4015
|
+
} catch (error2) {
|
|
4016
|
+
logger21.log({ level: "error", message: error2.message });
|
|
4017
|
+
next(error2);
|
|
4018
|
+
return;
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
return {
|
|
4022
|
+
createAreaChecklist,
|
|
4023
|
+
getAllAreaChecklist,
|
|
4024
|
+
getAreaChecklistHistory,
|
|
4025
|
+
getAreaChecklistHistoryDetails,
|
|
4026
|
+
acceptAreaChecklist,
|
|
4027
|
+
attachImageAreaChecklist,
|
|
4028
|
+
submitAreaChecklist,
|
|
4029
|
+
completeAreaChecklist
|
|
4030
|
+
};
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
// src/models/hygiene-unit-checklist.model.ts
|
|
4034
|
+
import { BadRequestError as BadRequestError23, logger as logger22 } from "@iservice365/node-server-utils";
|
|
4035
|
+
import Joi13 from "joi";
|
|
4036
|
+
import { ObjectId as ObjectId13 } from "mongodb";
|
|
4037
|
+
var unitChecklistSchema = Joi13.object({
|
|
4038
|
+
site: Joi13.string().hex().required(),
|
|
4039
|
+
type: Joi13.string().valid(...allowedTypes).required(),
|
|
4040
|
+
parentChecklist: Joi13.string().hex().required(),
|
|
4041
|
+
areaChecklist: Joi13.string().hex().required(),
|
|
4042
|
+
unit: Joi13.string().hex().required(),
|
|
4043
|
+
name: Joi13.string().optional().allow("", null),
|
|
4044
|
+
createdBy: Joi13.string().hex().optional().allow("", null)
|
|
4045
|
+
});
|
|
4046
|
+
function MUnitChecklist(value) {
|
|
4047
|
+
const { error } = unitChecklistSchema.validate(value);
|
|
4048
|
+
if (error) {
|
|
4049
|
+
logger22.info(`Hygiene Checklist Unit Model: ${error.message}`);
|
|
4050
|
+
throw new BadRequestError23(error.message);
|
|
4051
|
+
}
|
|
4052
|
+
if (value.site) {
|
|
4053
|
+
try {
|
|
4054
|
+
value.site = new ObjectId13(value.site);
|
|
4055
|
+
} catch (error2) {
|
|
4056
|
+
throw new BadRequestError23("Invalid site ID format.");
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
if (value.parentChecklist) {
|
|
4060
|
+
try {
|
|
4061
|
+
value.parentChecklist = new ObjectId13(value.parentChecklist);
|
|
4062
|
+
} catch (error2) {
|
|
4063
|
+
throw new BadRequestError23("Invalid parent checklist ID format.");
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
if (value.areaChecklist) {
|
|
4067
|
+
try {
|
|
4068
|
+
value.areaChecklist = new ObjectId13(value.areaChecklist);
|
|
4069
|
+
} catch (error2) {
|
|
4070
|
+
throw new BadRequestError23("Invalid area checklist ID format.");
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
if (value.unit) {
|
|
4074
|
+
try {
|
|
4075
|
+
value.unit = new ObjectId13(value.unit);
|
|
4076
|
+
} catch (error2) {
|
|
4077
|
+
throw new BadRequestError23("Invalid unit ID format.");
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
if (value.createdBy) {
|
|
4081
|
+
try {
|
|
4082
|
+
value.createdBy = new ObjectId13(value.createdBy);
|
|
4083
|
+
} catch (error2) {
|
|
4084
|
+
throw new BadRequestError23("Invalid createdBy ID format.");
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
return {
|
|
4088
|
+
site: value.site,
|
|
4089
|
+
type: value.type,
|
|
4090
|
+
parentChecklist: value.parentChecklist,
|
|
4091
|
+
areaChecklist: value.areaChecklist,
|
|
4092
|
+
unit: value.unit,
|
|
4093
|
+
name: value.name ?? "",
|
|
4094
|
+
approve: false,
|
|
4095
|
+
reject: false,
|
|
4096
|
+
createdBy: value.createdBy ?? "",
|
|
4097
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4098
|
+
updatedAt: value.updatedAt ?? ""
|
|
4099
|
+
};
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// src/repositories/hygiene-unit-checklist.repository.ts
|
|
4103
|
+
import {
|
|
4104
|
+
BadRequestError as BadRequestError24,
|
|
4105
|
+
InternalServerError as InternalServerError8,
|
|
4106
|
+
logger as logger23,
|
|
4107
|
+
makeCacheKey as makeCacheKey7,
|
|
4108
|
+
paginate as paginate7,
|
|
4109
|
+
useAtlas as useAtlas11,
|
|
4110
|
+
useCache as useCache7
|
|
4111
|
+
} from "@iservice365/node-server-utils";
|
|
4112
|
+
import { ObjectId as ObjectId14 } from "mongodb";
|
|
4113
|
+
function useUnitChecklistRepo() {
|
|
4114
|
+
const db = useAtlas11.getDb();
|
|
4115
|
+
if (!db) {
|
|
4116
|
+
throw new InternalServerError8("Unable to connect to server.");
|
|
4117
|
+
}
|
|
4118
|
+
const namespace_collection = "hygiene-checklist.units";
|
|
4119
|
+
const collection = db.collection(namespace_collection);
|
|
4120
|
+
const { delNamespace, setCache, getCache } = useCache7(namespace_collection);
|
|
4121
|
+
async function createIndex() {
|
|
4122
|
+
try {
|
|
4123
|
+
await collection.createIndexes([
|
|
4124
|
+
{ key: { site: 1 } },
|
|
4125
|
+
{ key: { type: 1 } },
|
|
4126
|
+
{ key: { parentChecklist: 1 } },
|
|
4127
|
+
{ key: { areaChecklist: 1 } },
|
|
4128
|
+
{ key: { "metadata.workOrder.category": 1 } },
|
|
4129
|
+
{ key: { "metadata.workOrder.createdBy": 1 } },
|
|
4130
|
+
{ key: { "metadata.workOrder.serviceProvider": 1 } },
|
|
4131
|
+
{ key: { "metadata.workOrder.organization": 1 } },
|
|
4132
|
+
{ key: { "metadata.workOrder.site": 1 } }
|
|
4133
|
+
]);
|
|
4134
|
+
} catch (error) {
|
|
4135
|
+
throw new InternalServerError8(
|
|
4136
|
+
"Failed to create index on hygiene unit checklist."
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
async function createTextIndex() {
|
|
4141
|
+
try {
|
|
4142
|
+
await collection.createIndex({ name: "text" });
|
|
4143
|
+
} catch (error) {
|
|
4144
|
+
throw new InternalServerError8(
|
|
4145
|
+
"Failed to create text index on hygiene unit checklist."
|
|
4146
|
+
);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
async function createUnitChecklist(value, session) {
|
|
4150
|
+
try {
|
|
4151
|
+
value = MUnitChecklist(value);
|
|
4152
|
+
const res = await collection.insertOne(value, { session });
|
|
4153
|
+
delNamespace().then(() => {
|
|
4154
|
+
logger23.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4155
|
+
}).catch((err) => {
|
|
4156
|
+
logger23.error(
|
|
4157
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4158
|
+
err
|
|
4159
|
+
);
|
|
4160
|
+
});
|
|
4161
|
+
return res.insertedId;
|
|
4162
|
+
} catch (error) {
|
|
4163
|
+
throw error;
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
async function getAllUnitChecklist({
|
|
4167
|
+
page = 1,
|
|
4168
|
+
limit = 10,
|
|
4169
|
+
search = "",
|
|
4170
|
+
site,
|
|
4171
|
+
type,
|
|
4172
|
+
parentChecklist,
|
|
4173
|
+
areaChecklist
|
|
4174
|
+
}) {
|
|
4175
|
+
page = page > 0 ? page - 1 : 0;
|
|
4176
|
+
const query = { type };
|
|
4177
|
+
const cacheOptions = {
|
|
4178
|
+
page,
|
|
4179
|
+
limit
|
|
4180
|
+
};
|
|
4181
|
+
try {
|
|
4182
|
+
query.site = new ObjectId14(site);
|
|
4183
|
+
cacheOptions.site = site.toString();
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
throw new BadRequestError24("Invalid site ID format.");
|
|
4186
|
+
}
|
|
4187
|
+
try {
|
|
4188
|
+
query.parentChecklist = new ObjectId14(parentChecklist);
|
|
4189
|
+
cacheOptions.parentChecklist = parentChecklist.toString();
|
|
4190
|
+
} catch (error) {
|
|
4191
|
+
throw new BadRequestError24("Invalid parent checklist ID format.");
|
|
4192
|
+
}
|
|
4193
|
+
try {
|
|
4194
|
+
query.areaChecklist = new ObjectId14(areaChecklist);
|
|
4195
|
+
cacheOptions.areaChecklist = areaChecklist.toString();
|
|
4196
|
+
} catch (error) {
|
|
4197
|
+
throw new BadRequestError24("Invalid area checklist ID format.");
|
|
4198
|
+
}
|
|
4199
|
+
if (search) {
|
|
4200
|
+
query.$text = { $search: search };
|
|
4201
|
+
cacheOptions.search = search;
|
|
4202
|
+
}
|
|
4203
|
+
const cacheKey = makeCacheKey7(namespace_collection, cacheOptions);
|
|
4204
|
+
const cachedData = await getCache(cacheKey);
|
|
4205
|
+
if (cachedData) {
|
|
4206
|
+
logger23.info(`Cache hit for key: ${cacheKey}`);
|
|
4207
|
+
return cachedData;
|
|
4208
|
+
}
|
|
4209
|
+
try {
|
|
4210
|
+
const areaAttachmentsResult = await collection.aggregate([
|
|
4211
|
+
{ $match: query },
|
|
4212
|
+
{
|
|
4213
|
+
$lookup: {
|
|
4214
|
+
from: "hygiene-checklist.areas",
|
|
4215
|
+
localField: "areaChecklist",
|
|
4216
|
+
foreignField: "_id",
|
|
4217
|
+
pipeline: [
|
|
4218
|
+
{ $project: { attachments: "$metadata.attachments" } }
|
|
4219
|
+
],
|
|
4220
|
+
as: "areaChecklistData"
|
|
4221
|
+
}
|
|
4222
|
+
},
|
|
4223
|
+
{
|
|
4224
|
+
$unwind: {
|
|
4225
|
+
path: "$areaChecklistData",
|
|
4226
|
+
preserveNullAndEmptyArrays: true
|
|
4227
|
+
}
|
|
4228
|
+
},
|
|
4229
|
+
{
|
|
4230
|
+
$group: {
|
|
4231
|
+
_id: null,
|
|
4232
|
+
attachments: { $first: "$areaChecklistData.attachments" }
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
]).toArray();
|
|
4236
|
+
const areaAttachments = areaAttachmentsResult.length > 0 ? areaAttachmentsResult[0].attachments || [] : [];
|
|
4237
|
+
const pipeline = [
|
|
4238
|
+
{ $match: query },
|
|
4239
|
+
{
|
|
4240
|
+
$lookup: {
|
|
4241
|
+
from: "organizations",
|
|
4242
|
+
localField: "metadata.workOrder.category",
|
|
4243
|
+
foreignField: "_id",
|
|
4244
|
+
pipeline: [{ $project: { nature: 1 } }],
|
|
4245
|
+
as: "categoryData"
|
|
4246
|
+
}
|
|
4247
|
+
},
|
|
4248
|
+
{
|
|
4249
|
+
$lookup: {
|
|
4250
|
+
from: "users",
|
|
4251
|
+
localField: "metadata.workOrder.createdBy",
|
|
4252
|
+
foreignField: "_id",
|
|
4253
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
4254
|
+
as: "createdByData"
|
|
4255
|
+
}
|
|
4256
|
+
},
|
|
4257
|
+
{
|
|
4258
|
+
$lookup: {
|
|
4259
|
+
from: "service-providers",
|
|
4260
|
+
localField: "metadata.workOrder.serviceProvider",
|
|
4261
|
+
foreignField: "_id",
|
|
4262
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
4263
|
+
as: "serviceProviderData"
|
|
4264
|
+
}
|
|
4265
|
+
},
|
|
4266
|
+
{
|
|
4267
|
+
$lookup: {
|
|
4268
|
+
from: "organizations",
|
|
4269
|
+
localField: "metadata.workOrder.organization",
|
|
4270
|
+
foreignField: "_id",
|
|
4271
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
4272
|
+
as: "organizationData"
|
|
4273
|
+
}
|
|
4274
|
+
},
|
|
4275
|
+
{
|
|
4276
|
+
$lookup: {
|
|
4277
|
+
from: "sites",
|
|
4278
|
+
localField: "metadata.workOrder.site",
|
|
4279
|
+
foreignField: "_id",
|
|
4280
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
4281
|
+
as: "siteData"
|
|
4282
|
+
}
|
|
4283
|
+
},
|
|
4284
|
+
{
|
|
4285
|
+
$addFields: {
|
|
4286
|
+
"metadata.workOrder.categoryName": {
|
|
4287
|
+
$arrayElemAt: ["$categoryData.nature", 0]
|
|
4288
|
+
},
|
|
4289
|
+
"metadata.workOrder.createdByName": {
|
|
4290
|
+
$arrayElemAt: ["$createdByData.name", 0]
|
|
4291
|
+
},
|
|
4292
|
+
"metadata.workOrder.serviceProviderName": {
|
|
4293
|
+
$arrayElemAt: ["$serviceProviderData.name", 0]
|
|
4294
|
+
},
|
|
4295
|
+
"metadata.workOrder.organizationName": {
|
|
4296
|
+
$arrayElemAt: ["$organizationData.name", 0]
|
|
4297
|
+
},
|
|
4298
|
+
"metadata.workOrder.siteName": {
|
|
4299
|
+
$arrayElemAt: ["$siteData.name", 0]
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
},
|
|
4303
|
+
{
|
|
4304
|
+
$project: {
|
|
4305
|
+
name: 1,
|
|
4306
|
+
remarks: "$metadata.remarks",
|
|
4307
|
+
attachments: "$metadata.attachments",
|
|
4308
|
+
workOrder: "$metadata.workOrder",
|
|
4309
|
+
approve: { $ifNull: ["$approve", null] },
|
|
4310
|
+
reject: { $ifNull: ["$reject", null] }
|
|
4311
|
+
}
|
|
4312
|
+
},
|
|
4313
|
+
{ $sort: { _id: -1 } },
|
|
4314
|
+
{ $skip: page * limit },
|
|
4315
|
+
{ $limit: limit }
|
|
4316
|
+
];
|
|
4317
|
+
const items = await collection.aggregate(pipeline).toArray();
|
|
4318
|
+
const length = await collection.countDocuments(query);
|
|
4319
|
+
const paginatedData = paginate7(items, page, limit, length);
|
|
4320
|
+
const data = {
|
|
4321
|
+
attachments: areaAttachments,
|
|
4322
|
+
...paginatedData
|
|
4323
|
+
};
|
|
4324
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4325
|
+
logger23.info(`Cache set for key: ${cacheKey}`);
|
|
4326
|
+
}).catch((err) => {
|
|
4327
|
+
logger23.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4328
|
+
});
|
|
4329
|
+
return data;
|
|
4330
|
+
} catch (error) {
|
|
4331
|
+
throw error;
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
async function getUnitChecklistById(_id) {
|
|
4335
|
+
try {
|
|
4336
|
+
_id = new ObjectId14(_id);
|
|
4337
|
+
} catch (error) {
|
|
4338
|
+
throw new BadRequestError24("Invalid unit checklist ID format.");
|
|
4339
|
+
}
|
|
4340
|
+
try {
|
|
4341
|
+
return await collection.findOne({ _id });
|
|
4342
|
+
} catch (error) {
|
|
4343
|
+
throw error;
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
async function updateUnitChecklist(_id, value, session) {
|
|
4347
|
+
try {
|
|
4348
|
+
_id = new ObjectId14(_id);
|
|
4349
|
+
} catch (error) {
|
|
4350
|
+
throw new BadRequestError24("Invalid unit checklist ID format.");
|
|
4351
|
+
}
|
|
4352
|
+
if (value.checkedBy && typeof value.checkedBy === "string") {
|
|
4353
|
+
try {
|
|
4354
|
+
value.checkedBy = new ObjectId14(value.checkedBy);
|
|
4355
|
+
} catch (error) {
|
|
4356
|
+
throw new BadRequestError24("Invalid checkedBy ID format.");
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
if ("metadata" in value && value.metadata?.workOrder) {
|
|
4360
|
+
const workOrder = value.metadata.workOrder;
|
|
4361
|
+
if (workOrder.category && typeof workOrder.category === "string") {
|
|
4362
|
+
try {
|
|
4363
|
+
workOrder.category = new ObjectId14(workOrder.category);
|
|
4364
|
+
} catch (error) {
|
|
4365
|
+
throw new BadRequestError24("Invalid category ID format.");
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
if (workOrder.createdBy && typeof workOrder.createdBy === "string") {
|
|
4369
|
+
try {
|
|
4370
|
+
workOrder.createdBy = new ObjectId14(workOrder.createdBy);
|
|
4371
|
+
} catch (error) {
|
|
4372
|
+
throw new BadRequestError24("Invalid createdBy ID format.");
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
if (workOrder.serviceProvider && typeof workOrder.serviceProvider === "string") {
|
|
4376
|
+
try {
|
|
4377
|
+
workOrder.serviceProvider = new ObjectId14(workOrder.serviceProvider);
|
|
4378
|
+
} catch (error) {
|
|
4379
|
+
throw new BadRequestError24("Invalid serviceProvider ID format.");
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
if (workOrder.organization && typeof workOrder.organization === "string") {
|
|
4383
|
+
try {
|
|
4384
|
+
workOrder.organization = new ObjectId14(workOrder.organization);
|
|
4385
|
+
} catch (error) {
|
|
4386
|
+
throw new BadRequestError24("Invalid organization ID format.");
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
if (workOrder.site && typeof workOrder.site === "string") {
|
|
4390
|
+
try {
|
|
4391
|
+
workOrder.site = new ObjectId14(workOrder.site);
|
|
4392
|
+
} catch (error) {
|
|
4393
|
+
throw new BadRequestError24("Invalid site ID format.");
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
try {
|
|
4398
|
+
const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
|
|
4399
|
+
const res = await collection.updateOne(
|
|
4400
|
+
{ _id },
|
|
4401
|
+
{ $set: updateValue },
|
|
4402
|
+
{ session }
|
|
4403
|
+
);
|
|
4404
|
+
if (res.modifiedCount === 0) {
|
|
4405
|
+
throw new InternalServerError8("Unable to update unit checklist.");
|
|
4406
|
+
}
|
|
4407
|
+
delNamespace().then(() => {
|
|
4408
|
+
logger23.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4409
|
+
}).catch((err) => {
|
|
4410
|
+
logger23.error(
|
|
4411
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4412
|
+
err
|
|
4413
|
+
);
|
|
4414
|
+
});
|
|
4415
|
+
return res.modifiedCount;
|
|
4416
|
+
} catch (error) {
|
|
4417
|
+
throw error;
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
return {
|
|
4421
|
+
createIndex,
|
|
4422
|
+
createTextIndex,
|
|
4423
|
+
createUnitChecklist,
|
|
4424
|
+
getAllUnitChecklist,
|
|
4425
|
+
getUnitChecklistById,
|
|
4426
|
+
updateUnitChecklist
|
|
4427
|
+
};
|
|
4428
|
+
}
|
|
4429
|
+
|
|
4430
|
+
// src/controllers/hygiene-unit-checklist.controller.ts
|
|
4431
|
+
import { BadRequestError as BadRequestError25, logger as logger25 } from "@iservice365/node-server-utils";
|
|
4432
|
+
import Joi14 from "joi";
|
|
4433
|
+
|
|
4434
|
+
// src/services/hygiene-unit-checklist.service.ts
|
|
4435
|
+
import { logger as logger24, NotFoundError as NotFoundError8 } from "@iservice365/node-server-utils";
|
|
4436
|
+
import {
|
|
4437
|
+
useSiteRepo,
|
|
4438
|
+
useWorkOrderService
|
|
4439
|
+
} from "@iservice365/core";
|
|
4440
|
+
function useUnitChecklistService() {
|
|
4441
|
+
const {
|
|
4442
|
+
getUnitChecklistById: _getUnitChecklistById,
|
|
4443
|
+
updateUnitChecklist: _updateUnitChecklist
|
|
4444
|
+
} = useUnitChecklistRepo();
|
|
4445
|
+
const { getSiteById } = useSiteRepo();
|
|
4446
|
+
const { createWorkOrder } = useWorkOrderService();
|
|
4447
|
+
async function approveUnitChecklist(id, value) {
|
|
4448
|
+
try {
|
|
4449
|
+
value.approve = true;
|
|
4450
|
+
value.reject = false;
|
|
4451
|
+
const result = await _updateUnitChecklist(id, value);
|
|
4452
|
+
return result;
|
|
4453
|
+
} catch (error) {
|
|
4454
|
+
logger24.error(`Error updating unit checklist with id ${id}:`, error);
|
|
4455
|
+
throw error;
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
async function rejectUnitChecklist(id, value, fullHost) {
|
|
4459
|
+
try {
|
|
4460
|
+
value.reject = true;
|
|
4461
|
+
value.approve = false;
|
|
4462
|
+
if (value.metadata?.workOrder) {
|
|
4463
|
+
const existingChecklist = await _getUnitChecklistById(id);
|
|
4464
|
+
if (!existingChecklist)
|
|
4465
|
+
throw new NotFoundError8("Unit checklist not found.");
|
|
4466
|
+
const site = await getSiteById(
|
|
4467
|
+
existingChecklist.site
|
|
4468
|
+
);
|
|
4469
|
+
if (!site)
|
|
4470
|
+
throw new NotFoundError8("Site not found.");
|
|
4471
|
+
const workOrderData = {
|
|
4472
|
+
...value.metadata.workOrder,
|
|
4473
|
+
attachments: value.metadata.attachments,
|
|
4474
|
+
createdBy: value.checkedBy,
|
|
4475
|
+
organization: site.orgId,
|
|
4476
|
+
site: site._id
|
|
4477
|
+
};
|
|
4478
|
+
const workOrder = await createWorkOrder(
|
|
4479
|
+
workOrderData,
|
|
4480
|
+
fullHost
|
|
4481
|
+
);
|
|
4482
|
+
if (!workOrder)
|
|
4483
|
+
throw new NotFoundError8("Failed to create work order.");
|
|
4484
|
+
}
|
|
4485
|
+
const result = await _updateUnitChecklist(id, value);
|
|
4486
|
+
return result;
|
|
4487
|
+
} catch (error) {
|
|
4488
|
+
logger24.error(`Error updating unit checklist with id ${id}:`, error);
|
|
4489
|
+
throw error;
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
return {
|
|
4493
|
+
approveUnitChecklist,
|
|
4494
|
+
rejectUnitChecklist
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
|
|
4498
|
+
// src/controllers/hygiene-unit-checklist.controller.ts
|
|
4499
|
+
import { workOrderSchema } from "@iservice365/core";
|
|
4500
|
+
function useUnitChecklistController() {
|
|
4501
|
+
const {
|
|
4502
|
+
createUnitChecklist: _createUnitChecklist,
|
|
4503
|
+
getAllUnitChecklist: _getAllUnitChecklist
|
|
4504
|
+
} = useUnitChecklistRepo();
|
|
4505
|
+
const {
|
|
4506
|
+
approveUnitChecklist: _approveUnitChecklist,
|
|
4507
|
+
rejectUnitChecklist: _rejectUnitChecklist
|
|
4508
|
+
} = useUnitChecklistService();
|
|
4509
|
+
async function createUnitChecklist(req, res, next) {
|
|
4510
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
4511
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
4512
|
+
{}
|
|
4513
|
+
) : {};
|
|
4514
|
+
const createdBy = cookies["user"] || "";
|
|
4515
|
+
const payload = { ...req.body, ...req.params, createdBy };
|
|
4516
|
+
const { error } = unitChecklistSchema.validate(payload);
|
|
4517
|
+
if (error) {
|
|
4518
|
+
logger25.log({ level: "error", message: error.message });
|
|
4519
|
+
next(new BadRequestError25(error.message));
|
|
4520
|
+
return;
|
|
4521
|
+
}
|
|
4522
|
+
try {
|
|
4523
|
+
const id = await _createUnitChecklist(payload);
|
|
4524
|
+
res.status(201).json({ message: "Unit checklist created successfully.", id });
|
|
4525
|
+
return;
|
|
4526
|
+
} catch (error2) {
|
|
4527
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4528
|
+
next(error2);
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
async function getAllUnitChecklist(req, res, next) {
|
|
4533
|
+
const query = { ...req.query, ...req.params };
|
|
4534
|
+
const validation = Joi14.object({
|
|
4535
|
+
page: Joi14.number().min(1).optional().allow("", null),
|
|
4536
|
+
limit: Joi14.number().min(1).optional().allow("", null),
|
|
4537
|
+
search: Joi14.string().optional().allow("", null),
|
|
4538
|
+
site: Joi14.string().hex().required(),
|
|
4539
|
+
type: Joi14.string().valid(...allowedTypes).required(),
|
|
4540
|
+
parentChecklist: Joi14.string().hex().required(),
|
|
4541
|
+
areaChecklist: Joi14.string().hex().required()
|
|
4542
|
+
});
|
|
4543
|
+
const { error } = validation.validate(query);
|
|
4544
|
+
if (error) {
|
|
4545
|
+
logger25.log({ level: "error", message: error.message });
|
|
4546
|
+
next(new BadRequestError25(error.message));
|
|
4547
|
+
return;
|
|
4548
|
+
}
|
|
4549
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
4550
|
+
const limit = parseInt(req.query.limit) ?? 20;
|
|
4551
|
+
const search = req.query.search ?? "";
|
|
4552
|
+
const site = req.params.site ?? "";
|
|
4553
|
+
const type = req.params.type ?? "";
|
|
4554
|
+
const parentChecklist = req.params.parentChecklist ?? "";
|
|
4555
|
+
const areaChecklist = req.params.areaChecklist ?? "";
|
|
4556
|
+
try {
|
|
4557
|
+
const data = await _getAllUnitChecklist({
|
|
4558
|
+
page,
|
|
4559
|
+
limit,
|
|
4560
|
+
search,
|
|
4561
|
+
site,
|
|
4562
|
+
type,
|
|
4563
|
+
parentChecklist,
|
|
4564
|
+
areaChecklist
|
|
4565
|
+
});
|
|
4566
|
+
res.json(data);
|
|
4567
|
+
return;
|
|
4568
|
+
} catch (error2) {
|
|
4569
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4570
|
+
next(error2);
|
|
4571
|
+
return;
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
async function approveUnitChecklist(req, res, next) {
|
|
4575
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
4576
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
4577
|
+
{}
|
|
4578
|
+
) : {};
|
|
4579
|
+
const checkedBy = cookies["user"] || "";
|
|
4580
|
+
const payload = { id: req.params.id, checkedBy };
|
|
4581
|
+
const validation = Joi14.object({
|
|
4582
|
+
id: Joi14.string().hex().required(),
|
|
4583
|
+
checkedBy: Joi14.string().hex().required()
|
|
4584
|
+
});
|
|
4585
|
+
const { error } = validation.validate(payload);
|
|
4586
|
+
if (error) {
|
|
4587
|
+
logger25.log({ level: "error", message: error.message });
|
|
4588
|
+
next(new BadRequestError25(error.message));
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
try {
|
|
4592
|
+
const { id, ...value } = payload;
|
|
4593
|
+
await _approveUnitChecklist(id, value);
|
|
4594
|
+
res.json({ message: "Unit checklist approved successfully." });
|
|
4595
|
+
return;
|
|
4596
|
+
} catch (error2) {
|
|
4597
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4598
|
+
next(error2);
|
|
4599
|
+
return;
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
async function rejectUnitChecklist(req, res, next) {
|
|
4603
|
+
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
4604
|
+
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
4605
|
+
{}
|
|
4606
|
+
) : {};
|
|
4607
|
+
const checkedBy = cookies["user"] || "";
|
|
4608
|
+
if (req.body.workOrder) {
|
|
4609
|
+
req.body.workOrder.createdBy = checkedBy;
|
|
4610
|
+
}
|
|
4611
|
+
const payload = { id: req.params.id, checkedBy, ...req.body };
|
|
4612
|
+
const validation = Joi14.object({
|
|
4613
|
+
id: Joi14.string().hex().required(),
|
|
4614
|
+
attachments: Joi14.array().items(Joi14.string()).optional(),
|
|
4615
|
+
remarks: Joi14.string().required(),
|
|
4616
|
+
workOrder: workOrderSchema.optional(),
|
|
4617
|
+
checkedBy: Joi14.string().hex().required()
|
|
4618
|
+
});
|
|
4619
|
+
const { error } = validation.validate(payload);
|
|
4620
|
+
if (error) {
|
|
4621
|
+
logger25.log({ level: "error", message: error.message });
|
|
4622
|
+
next(new BadRequestError25(error.message));
|
|
4623
|
+
return;
|
|
4624
|
+
}
|
|
4625
|
+
try {
|
|
4626
|
+
const { id, attachments, remarks, workOrder, ...value } = payload;
|
|
4627
|
+
value.metadata = {
|
|
4628
|
+
attachments: attachments || [],
|
|
4629
|
+
remarks: remarks || "",
|
|
4630
|
+
workOrder
|
|
4631
|
+
};
|
|
4632
|
+
if (value.metadata.workOrder) {
|
|
4633
|
+
if (value.metadata.workOrder.category) {
|
|
4634
|
+
value.metadata.workOrder.category = value.metadata.workOrder.category;
|
|
4635
|
+
}
|
|
4636
|
+
if (value.metadata.workOrder.serviceProvider) {
|
|
4637
|
+
value.metadata.workOrder.serviceProvider = value.metadata.workOrder.serviceProvider;
|
|
4638
|
+
}
|
|
4639
|
+
value.metadata.workOrder.createdBy = checkedBy;
|
|
4640
|
+
}
|
|
4641
|
+
const fullHost = req.headers.origin;
|
|
4642
|
+
await _rejectUnitChecklist(id, value, fullHost);
|
|
4643
|
+
res.json({ message: "Unit checklist rejected successfully." });
|
|
4644
|
+
return;
|
|
4645
|
+
} catch (error2) {
|
|
4646
|
+
logger25.log({ level: "error", message: error2.message });
|
|
4647
|
+
next(error2);
|
|
4648
|
+
return;
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
return {
|
|
4652
|
+
createUnitChecklist,
|
|
4653
|
+
getAllUnitChecklist,
|
|
4654
|
+
approveUnitChecklist,
|
|
4655
|
+
rejectUnitChecklist
|
|
2753
4656
|
};
|
|
2754
4657
|
}
|
|
2755
4658
|
export {
|
|
2756
4659
|
MArea,
|
|
4660
|
+
MAreaChecklist,
|
|
2757
4661
|
MParentChecklist,
|
|
2758
4662
|
MScheduleTaskArea,
|
|
2759
4663
|
MToiletLocation,
|
|
2760
4664
|
MUnit,
|
|
4665
|
+
MUnitChecklist,
|
|
4666
|
+
allowedStatus,
|
|
4667
|
+
allowedTypes,
|
|
4668
|
+
areaChecklistSchema,
|
|
2761
4669
|
areaSchema,
|
|
2762
4670
|
parentChecklistSchema,
|
|
2763
4671
|
scheduleTaskAreaSchema,
|
|
2764
4672
|
toiletLocationSchema,
|
|
4673
|
+
unitChecklistSchema,
|
|
2765
4674
|
unitSchema,
|
|
4675
|
+
useAreaChecklistController,
|
|
4676
|
+
useAreaChecklistRepo,
|
|
2766
4677
|
useAreaController,
|
|
2767
4678
|
useAreaRepository,
|
|
2768
4679
|
useAreaService,
|
|
2769
|
-
|
|
4680
|
+
useParentChecklistController,
|
|
2770
4681
|
useParentChecklistRepo,
|
|
2771
4682
|
useScheduleTaskAreaController,
|
|
2772
4683
|
useScheduleTaskAreaRepository,
|
|
@@ -2774,6 +4685,8 @@ export {
|
|
|
2774
4685
|
useToiletLocationController,
|
|
2775
4686
|
useToiletLocationRepository,
|
|
2776
4687
|
useToiletLocationService,
|
|
4688
|
+
useUnitChecklistController,
|
|
4689
|
+
useUnitChecklistRepo,
|
|
2777
4690
|
useUnitController,
|
|
2778
4691
|
useUnitRepository,
|
|
2779
4692
|
useUnitService
|