@7365admin1/module-hygiene 4.8.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +10 -10
- package/dist/index.js +238 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +238 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -84,7 +84,12 @@ module.exports = __toCommonJS(src_exports);
|
|
|
84
84
|
|
|
85
85
|
// src/models/hygiene-base.model.ts
|
|
86
86
|
var allowedTypes = ["common", "toilet"];
|
|
87
|
-
var allowedStatus = [
|
|
87
|
+
var allowedStatus = [
|
|
88
|
+
"open",
|
|
89
|
+
"ongoing",
|
|
90
|
+
"completed",
|
|
91
|
+
"closed"
|
|
92
|
+
];
|
|
88
93
|
var allowedPeriods = ["today", "thisWeek", "thisMonth"];
|
|
89
94
|
|
|
90
95
|
// src/repositories/hygiene-dashboard.repository.ts
|
|
@@ -1366,10 +1371,14 @@ function useAreaService() {
|
|
|
1366
1371
|
try {
|
|
1367
1372
|
dataArray = JSON.parse(dataJson);
|
|
1368
1373
|
} catch (error) {
|
|
1369
|
-
throw new import_node_server_utils8.BadRequestError(
|
|
1374
|
+
throw new import_node_server_utils8.BadRequestError(
|
|
1375
|
+
"We couldn't read the uploaded file. Please make sure you're uploading the correct file and try again."
|
|
1376
|
+
);
|
|
1370
1377
|
}
|
|
1371
1378
|
if (!dataArray || dataArray.length === 0) {
|
|
1372
|
-
throw new import_node_server_utils8.NotFoundError(
|
|
1379
|
+
throw new import_node_server_utils8.NotFoundError(
|
|
1380
|
+
"The uploaded file is empty. Please make sure your file contains data and try again."
|
|
1381
|
+
);
|
|
1373
1382
|
}
|
|
1374
1383
|
let availableUnits = [];
|
|
1375
1384
|
try {
|
|
@@ -1470,33 +1479,35 @@ function useAreaService() {
|
|
|
1470
1479
|
}
|
|
1471
1480
|
}
|
|
1472
1481
|
}
|
|
1473
|
-
let message = `Upload
|
|
1482
|
+
let message = `Upload complete! ${insertedAreaIds.length} ${insertedAreaIds.length === 1 ? "area was" : "areas were"} successfully added`;
|
|
1474
1483
|
if (duplicateAreas.length > 0) {
|
|
1475
|
-
message += `, ${duplicateAreas.length} areas skipped
|
|
1484
|
+
message += `, ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area was" : "areas were"} skipped because they already exist`;
|
|
1476
1485
|
}
|
|
1477
1486
|
if (failedAreas.length > 0) {
|
|
1478
|
-
message += `, ${failedAreas.length} areas
|
|
1487
|
+
message += `, ${failedAreas.length} ${failedAreas.length === 1 ? "area" : "areas"} could not be saved`;
|
|
1479
1488
|
}
|
|
1480
1489
|
if (skippedRows.length > 0) {
|
|
1481
|
-
message += `, ${skippedRows.length} rows skipped
|
|
1490
|
+
message += `, ${skippedRows.length} ${skippedRows.length === 1 ? "row was" : "rows were"} skipped due to missing information`;
|
|
1482
1491
|
}
|
|
1483
1492
|
import_node_server_utils8.logger.info(message);
|
|
1484
1493
|
if (insertedAreaIds.length === 0) {
|
|
1485
1494
|
if (duplicateAreas.length > 0 && failedAreas.length === 0 && skippedRows.length === 0) {
|
|
1486
1495
|
return {
|
|
1487
|
-
message: `No new areas were
|
|
1496
|
+
message: `No new areas were added. All ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area" : "areas"} in your file already exist in the system: ${duplicateAreas.join(
|
|
1497
|
+
", "
|
|
1498
|
+
)}.`
|
|
1488
1499
|
};
|
|
1489
1500
|
} else if (failedAreas.length > 0) {
|
|
1490
1501
|
throw new import_node_server_utils8.BadRequestError(
|
|
1491
|
-
`No areas were
|
|
1502
|
+
`No areas were added. ${failedAreas.length} ${failedAreas.length === 1 ? "area" : "areas"} could not be saved. Please review the area names and information in your file, then try again.`
|
|
1492
1503
|
);
|
|
1493
1504
|
} else if (skippedRows.length > 0 && duplicateAreas.length === 0) {
|
|
1494
1505
|
throw new import_node_server_utils8.BadRequestError(
|
|
1495
|
-
`No areas were
|
|
1506
|
+
`No areas were added. ${skippedRows.length} ${skippedRows.length === 1 ? "row" : "rows"} in your file had missing or incomplete information. Please fill in all required fields and try again.`
|
|
1496
1507
|
);
|
|
1497
1508
|
} else {
|
|
1498
1509
|
return {
|
|
1499
|
-
message: `No new areas were
|
|
1510
|
+
message: `No new areas were added. ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area" : "areas"} already exist${skippedRows.length > 0 ? `, and ${skippedRows.length} ${skippedRows.length === 1 ? "row" : "rows"} had missing or incomplete information` : ""}.`
|
|
1500
1511
|
};
|
|
1501
1512
|
}
|
|
1502
1513
|
}
|
|
@@ -1507,11 +1518,11 @@ function useAreaService() {
|
|
|
1507
1518
|
throw error;
|
|
1508
1519
|
} else if (error.message.includes("validation")) {
|
|
1509
1520
|
throw new import_node_server_utils8.BadRequestError(
|
|
1510
|
-
"Upload failed
|
|
1521
|
+
"Upload failed because some required information is missing or incorrect. Please review your file and make sure all fields are filled in properly."
|
|
1511
1522
|
);
|
|
1512
1523
|
} else {
|
|
1513
1524
|
throw new import_node_server_utils8.BadRequestError(
|
|
1514
|
-
|
|
1525
|
+
"Something went wrong while uploading. Please check your file and try again."
|
|
1515
1526
|
);
|
|
1516
1527
|
}
|
|
1517
1528
|
}
|
|
@@ -1520,11 +1531,15 @@ function useAreaService() {
|
|
|
1520
1531
|
try {
|
|
1521
1532
|
const areas = await getAreasForChecklist(site);
|
|
1522
1533
|
if (!areas || !Array.isArray(areas) || areas.length === 0) {
|
|
1523
|
-
throw new import_node_server_utils8.BadRequestError(
|
|
1534
|
+
throw new import_node_server_utils8.BadRequestError(
|
|
1535
|
+
"There are no areas to export yet. Please add some areas first, then try again."
|
|
1536
|
+
);
|
|
1524
1537
|
}
|
|
1525
1538
|
const excelBuffer = await generateAreaExcel(areas);
|
|
1526
1539
|
if (!excelBuffer || excelBuffer.length === 0) {
|
|
1527
|
-
throw new Error(
|
|
1540
|
+
throw new Error(
|
|
1541
|
+
"Something went wrong while preparing your export file. Please try again."
|
|
1542
|
+
);
|
|
1528
1543
|
}
|
|
1529
1544
|
return excelBuffer;
|
|
1530
1545
|
} catch (error) {
|
|
@@ -1824,10 +1839,14 @@ function useUnitService() {
|
|
|
1824
1839
|
try {
|
|
1825
1840
|
dataArray = JSON.parse(dataJson);
|
|
1826
1841
|
} catch (error) {
|
|
1827
|
-
throw new import_node_server_utils11.BadRequestError(
|
|
1842
|
+
throw new import_node_server_utils11.BadRequestError(
|
|
1843
|
+
"We couldn't read the uploaded file. Please make sure you're uploading the correct file and try again."
|
|
1844
|
+
);
|
|
1828
1845
|
}
|
|
1829
1846
|
if (!dataArray || dataArray.length === 0) {
|
|
1830
|
-
throw new import_node_server_utils11.NotFoundError(
|
|
1847
|
+
throw new import_node_server_utils11.NotFoundError(
|
|
1848
|
+
"The uploaded file is empty. Please make sure your file contains data and try again."
|
|
1849
|
+
);
|
|
1831
1850
|
}
|
|
1832
1851
|
const insertedUnitIds = [];
|
|
1833
1852
|
const duplicateUnits = [];
|
|
@@ -1868,29 +1887,31 @@ function useUnitService() {
|
|
|
1868
1887
|
}
|
|
1869
1888
|
}
|
|
1870
1889
|
}
|
|
1871
|
-
let message = `Upload
|
|
1890
|
+
let message = `Upload complete! ${insertedUnitIds.length} ${insertedUnitIds.length === 1 ? "unit was" : "units were"} successfully added`;
|
|
1872
1891
|
if (duplicateUnits.length > 0) {
|
|
1873
|
-
message += `, ${duplicateUnits.length} units skipped
|
|
1892
|
+
message += `, ${duplicateUnits.length} ${duplicateUnits.length === 1 ? "unit was" : "units were"} skipped because they already exist`;
|
|
1874
1893
|
}
|
|
1875
1894
|
if (failedUnits.length > 0) {
|
|
1876
|
-
message += `, ${failedUnits.length} units
|
|
1895
|
+
message += `, ${failedUnits.length} ${failedUnits.length === 1 ? "unit" : "units"} could not be saved`;
|
|
1877
1896
|
}
|
|
1878
1897
|
if (skippedRows.length > 0) {
|
|
1879
|
-
message += `, ${skippedRows.length} rows skipped
|
|
1898
|
+
message += `, ${skippedRows.length} ${skippedRows.length === 1 ? "row was" : "rows were"} skipped due to missing information`;
|
|
1880
1899
|
}
|
|
1881
1900
|
import_node_server_utils11.logger.info(message);
|
|
1882
1901
|
if (insertedUnitIds.length === 0) {
|
|
1883
1902
|
if (duplicateUnits.length > 0 && failedUnits.length === 0) {
|
|
1884
1903
|
throw new import_node_server_utils11.BadRequestError(
|
|
1885
|
-
`No new units were
|
|
1904
|
+
`No new units were added. All ${duplicateUnits.length} ${duplicateUnits.length === 1 ? "unit" : "units"} in your file already exist in the system: ${duplicateUnits.join(
|
|
1905
|
+
", "
|
|
1906
|
+
)}.`
|
|
1886
1907
|
);
|
|
1887
1908
|
} else if (failedUnits.length > 0) {
|
|
1888
1909
|
throw new import_node_server_utils11.BadRequestError(
|
|
1889
|
-
`No units were
|
|
1910
|
+
`No units were added. ${failedUnits.length} ${failedUnits.length === 1 ? "unit" : "units"} could not be saved. Please review the unit names in your file and try again.`
|
|
1890
1911
|
);
|
|
1891
1912
|
} else if (skippedRows.length > 0) {
|
|
1892
1913
|
throw new import_node_server_utils11.BadRequestError(
|
|
1893
|
-
`No units were
|
|
1914
|
+
`No units were added. ${skippedRows.length} ${skippedRows.length === 1 ? "row" : "rows"} in your file had missing or incomplete information. Please fill in all required fields and try again.`
|
|
1894
1915
|
);
|
|
1895
1916
|
}
|
|
1896
1917
|
}
|
|
@@ -1901,15 +1922,15 @@ function useUnitService() {
|
|
|
1901
1922
|
throw error;
|
|
1902
1923
|
} else if (error.message.includes("duplicate")) {
|
|
1903
1924
|
throw new import_node_server_utils11.BadRequestError(
|
|
1904
|
-
"Upload failed
|
|
1925
|
+
"Upload failed because some unit names are already taken. Please make sure each unit has a unique name and try again."
|
|
1905
1926
|
);
|
|
1906
1927
|
} else if (error.message.includes("validation")) {
|
|
1907
1928
|
throw new import_node_server_utils11.BadRequestError(
|
|
1908
|
-
"Upload failed
|
|
1929
|
+
"Upload failed because some required information is missing or incorrect. Please review your file and make sure all fields are filled in properly."
|
|
1909
1930
|
);
|
|
1910
1931
|
} else {
|
|
1911
1932
|
throw new import_node_server_utils11.BadRequestError(
|
|
1912
|
-
|
|
1933
|
+
"Something went wrong while uploading. Please check your file and try again."
|
|
1913
1934
|
);
|
|
1914
1935
|
}
|
|
1915
1936
|
}
|
|
@@ -1941,7 +1962,9 @@ function useUnitService() {
|
|
|
1941
1962
|
session?.startTransaction();
|
|
1942
1963
|
const isExistingArea = await verifyAreaByUnitId(_id);
|
|
1943
1964
|
if (isExistingArea) {
|
|
1944
|
-
throw new import_node_server_utils11.BadRequestError(
|
|
1965
|
+
throw new import_node_server_utils11.BadRequestError(
|
|
1966
|
+
"This unit can't be deleted because it's currently being used in one or more areas. Please remove it from those areas first, then try again."
|
|
1967
|
+
);
|
|
1945
1968
|
}
|
|
1946
1969
|
const result = await _deleteUnit(_id, session);
|
|
1947
1970
|
await session?.commitTransaction();
|
|
@@ -1967,11 +1990,15 @@ function useUnitService() {
|
|
|
1967
1990
|
site
|
|
1968
1991
|
});
|
|
1969
1992
|
if (!data || !data.items || data.items.length === 0) {
|
|
1970
|
-
throw new import_node_server_utils11.BadRequestError(
|
|
1993
|
+
throw new import_node_server_utils11.BadRequestError(
|
|
1994
|
+
"There are no units to export yet. Please add some units first, then try again."
|
|
1995
|
+
);
|
|
1971
1996
|
}
|
|
1972
1997
|
const excelBuffer = await _generateUnitExcel(data.items);
|
|
1973
1998
|
if (!excelBuffer || excelBuffer.length === 0) {
|
|
1974
|
-
throw new Error(
|
|
1999
|
+
throw new Error(
|
|
2000
|
+
"Something went wrong while preparing your export file. Please try again."
|
|
2001
|
+
);
|
|
1975
2002
|
}
|
|
1976
2003
|
return excelBuffer;
|
|
1977
2004
|
} catch (error) {
|
|
@@ -2178,7 +2205,7 @@ function MParentChecklist(value) {
|
|
|
2178
2205
|
}
|
|
2179
2206
|
return {
|
|
2180
2207
|
site: value.site,
|
|
2181
|
-
status: "
|
|
2208
|
+
status: "open",
|
|
2182
2209
|
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
2183
2210
|
updatedAt: value.updatedAt ?? ""
|
|
2184
2211
|
};
|
|
@@ -2368,18 +2395,23 @@ function useParentChecklistRepo() {
|
|
|
2368
2395
|
status: {
|
|
2369
2396
|
$switch: {
|
|
2370
2397
|
branches: [
|
|
2371
|
-
{ case: { $eq: ["$status", "
|
|
2398
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2372
2399
|
{
|
|
2373
2400
|
case: { $eq: ["$status", "ongoing"] },
|
|
2374
2401
|
then: "Ongoing"
|
|
2375
2402
|
},
|
|
2376
|
-
{ case: { $eq: ["$status", "completed"] }, then: "Completed" }
|
|
2403
|
+
{ case: { $eq: ["$status", "completed"] }, then: "Completed" },
|
|
2404
|
+
{
|
|
2405
|
+
case: { $eq: ["$status", "closed"] },
|
|
2406
|
+
then: "Closed"
|
|
2407
|
+
}
|
|
2377
2408
|
],
|
|
2378
2409
|
default: "$status"
|
|
2379
2410
|
}
|
|
2380
2411
|
},
|
|
2381
2412
|
completedAt: 1,
|
|
2382
|
-
createdAt: 1
|
|
2413
|
+
createdAt: 1,
|
|
2414
|
+
closeIn: { $add: ["$createdAt", 24 * 60 * 60 * 1e3] }
|
|
2383
2415
|
}
|
|
2384
2416
|
});
|
|
2385
2417
|
pipeline.push(
|
|
@@ -2467,11 +2499,42 @@ function useParentChecklistRepo() {
|
|
|
2467
2499
|
throw error;
|
|
2468
2500
|
}
|
|
2469
2501
|
}
|
|
2502
|
+
async function closeExpiredParentChecklists() {
|
|
2503
|
+
try {
|
|
2504
|
+
const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
2505
|
+
const result = await collection.updateMany(
|
|
2506
|
+
{
|
|
2507
|
+
status: { $in: ["open", "ongoing", "completed"] },
|
|
2508
|
+
createdAt: { $lte: expiryDate }
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
$set: {
|
|
2512
|
+
status: "closed",
|
|
2513
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
);
|
|
2517
|
+
if (result.modifiedCount > 0) {
|
|
2518
|
+
delNamespace().catch((err) => {
|
|
2519
|
+
import_node_server_utils14.logger.error(
|
|
2520
|
+
"Failed to invalidate cache after closing expired parent checklists",
|
|
2521
|
+
err
|
|
2522
|
+
);
|
|
2523
|
+
});
|
|
2524
|
+
import_node_server_utils14.logger.info(`Closed ${result.modifiedCount} expired parent checklists`);
|
|
2525
|
+
}
|
|
2526
|
+
return result.modifiedCount;
|
|
2527
|
+
} catch (error) {
|
|
2528
|
+
import_node_server_utils14.logger.error("Failed to close expired parent checklists", error);
|
|
2529
|
+
throw error;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2470
2532
|
return {
|
|
2471
2533
|
createIndex,
|
|
2472
2534
|
createParentChecklist,
|
|
2473
2535
|
getAllParentChecklist,
|
|
2474
|
-
updateParentChecklistStatuses
|
|
2536
|
+
updateParentChecklistStatuses,
|
|
2537
|
+
closeExpiredParentChecklists
|
|
2475
2538
|
};
|
|
2476
2539
|
}
|
|
2477
2540
|
|
|
@@ -2553,7 +2616,7 @@ function useParentChecklistController() {
|
|
|
2553
2616
|
var import_joi8 = __toESM(require("joi"));
|
|
2554
2617
|
var import_mongodb8 = require("mongodb");
|
|
2555
2618
|
var import_node_server_utils16 = require("@7365admin1/node-server-utils");
|
|
2556
|
-
var allowedChecklistStatus = ["
|
|
2619
|
+
var allowedChecklistStatus = ["open", "completed", "closed"];
|
|
2557
2620
|
var areaChecklistSchema = import_joi8.default.object({
|
|
2558
2621
|
schedule: import_joi8.default.string().hex().required(),
|
|
2559
2622
|
area: import_joi8.default.string().hex().required(),
|
|
@@ -2601,7 +2664,7 @@ function MAreaChecklist(value) {
|
|
|
2601
2664
|
name: unit.name,
|
|
2602
2665
|
approve: false,
|
|
2603
2666
|
reject: false,
|
|
2604
|
-
status: "
|
|
2667
|
+
status: "open",
|
|
2605
2668
|
remarks: "",
|
|
2606
2669
|
completedBy: "",
|
|
2607
2670
|
timestamp: ""
|
|
@@ -2622,7 +2685,7 @@ function MAreaChecklist(value) {
|
|
|
2622
2685
|
name: value.name,
|
|
2623
2686
|
type: value.type,
|
|
2624
2687
|
checklist: value.checklist || [],
|
|
2625
|
-
status: "
|
|
2688
|
+
status: "open",
|
|
2626
2689
|
createdBy: value.createdBy,
|
|
2627
2690
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2628
2691
|
completedAt: "",
|
|
@@ -2780,17 +2843,37 @@ function useAreaChecklistRepo() {
|
|
|
2780
2843
|
preserveNullAndEmptyArrays: true
|
|
2781
2844
|
}
|
|
2782
2845
|
},
|
|
2846
|
+
{
|
|
2847
|
+
$addFields: {
|
|
2848
|
+
statusOrder: {
|
|
2849
|
+
$switch: {
|
|
2850
|
+
branches: [
|
|
2851
|
+
{ case: { $eq: ["$status", "open"] }, then: 0 },
|
|
2852
|
+
{ case: { $eq: ["$status", "ongoing"] }, then: 1 },
|
|
2853
|
+
{ case: { $eq: ["$status", "completed"] }, then: 3 },
|
|
2854
|
+
{ case: { $eq: ["$status", "closed"] }, then: 4 }
|
|
2855
|
+
],
|
|
2856
|
+
default: 2
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
},
|
|
2861
|
+
{ $sort: { statusOrder: 1, _id: -1 } },
|
|
2783
2862
|
{
|
|
2784
2863
|
$project: {
|
|
2785
2864
|
name: 1,
|
|
2786
2865
|
status: {
|
|
2787
2866
|
$switch: {
|
|
2788
2867
|
branches: [
|
|
2789
|
-
{ case: { $eq: ["$status", "
|
|
2868
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2790
2869
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2791
2870
|
{
|
|
2792
2871
|
case: { $eq: ["$status", "completed"] },
|
|
2793
2872
|
then: "Completed"
|
|
2873
|
+
},
|
|
2874
|
+
{
|
|
2875
|
+
case: { $eq: ["$status", "closed"] },
|
|
2876
|
+
then: "Closed"
|
|
2794
2877
|
}
|
|
2795
2878
|
],
|
|
2796
2879
|
default: "$status"
|
|
@@ -2815,7 +2898,6 @@ function useAreaChecklistRepo() {
|
|
|
2815
2898
|
createdByName: "$createdByDoc.name"
|
|
2816
2899
|
}
|
|
2817
2900
|
},
|
|
2818
|
-
{ $sort: { _id: -1 } },
|
|
2819
2901
|
{ $skip: page * limit },
|
|
2820
2902
|
{ $limit: limit }
|
|
2821
2903
|
];
|
|
@@ -2892,11 +2974,15 @@ function useAreaChecklistRepo() {
|
|
|
2892
2974
|
status: {
|
|
2893
2975
|
$switch: {
|
|
2894
2976
|
branches: [
|
|
2895
|
-
{ case: { $eq: ["$status", "
|
|
2977
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2896
2978
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2897
2979
|
{
|
|
2898
2980
|
case: { $eq: ["$status", "completed"] },
|
|
2899
2981
|
then: "Completed"
|
|
2982
|
+
},
|
|
2983
|
+
{
|
|
2984
|
+
case: { $eq: ["$status", "closed"] },
|
|
2985
|
+
then: "Closed"
|
|
2900
2986
|
}
|
|
2901
2987
|
],
|
|
2902
2988
|
default: "$status"
|
|
@@ -2961,11 +3047,15 @@ function useAreaChecklistRepo() {
|
|
|
2961
3047
|
status: {
|
|
2962
3048
|
$switch: {
|
|
2963
3049
|
branches: [
|
|
2964
|
-
{ case: { $eq: ["$status", "
|
|
3050
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2965
3051
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2966
3052
|
{
|
|
2967
3053
|
case: { $eq: ["$status", "completed"] },
|
|
2968
3054
|
then: "Completed"
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
case: { $eq: ["$status", "closed"] },
|
|
3058
|
+
then: "Closed"
|
|
2969
3059
|
}
|
|
2970
3060
|
],
|
|
2971
3061
|
default: "$status"
|
|
@@ -3025,12 +3115,16 @@ function useAreaChecklistRepo() {
|
|
|
3025
3115
|
$switch: {
|
|
3026
3116
|
branches: [
|
|
3027
3117
|
{
|
|
3028
|
-
case: { $eq: ["$checklist.units.status", "
|
|
3029
|
-
then: "
|
|
3118
|
+
case: { $eq: ["$checklist.units.status", "open"] },
|
|
3119
|
+
then: "Open"
|
|
3030
3120
|
},
|
|
3031
3121
|
{
|
|
3032
3122
|
case: { $eq: ["$checklist.units.status", "completed"] },
|
|
3033
3123
|
then: "Completed"
|
|
3124
|
+
},
|
|
3125
|
+
{
|
|
3126
|
+
case: { $eq: ["$checklist.units.status", "closed"] },
|
|
3127
|
+
then: "Closed"
|
|
3034
3128
|
}
|
|
3035
3129
|
],
|
|
3036
3130
|
default: "$checklist.units.status"
|
|
@@ -3173,16 +3267,21 @@ function useAreaChecklistRepo() {
|
|
|
3173
3267
|
name: "$checklist.units.name",
|
|
3174
3268
|
approve: "$checklist.units.approve",
|
|
3175
3269
|
reject: "$checklist.units.reject",
|
|
3270
|
+
timestamp: "$checklist.units.timestamp",
|
|
3176
3271
|
status: {
|
|
3177
3272
|
$switch: {
|
|
3178
3273
|
branches: [
|
|
3179
3274
|
{
|
|
3180
|
-
case: { $eq: ["$checklist.units.status", "
|
|
3181
|
-
then: "
|
|
3275
|
+
case: { $eq: ["$checklist.units.status", "open"] },
|
|
3276
|
+
then: "Open"
|
|
3182
3277
|
},
|
|
3183
3278
|
{
|
|
3184
3279
|
case: { $eq: ["$checklist.units.status", "completed"] },
|
|
3185
3280
|
then: "Completed"
|
|
3281
|
+
},
|
|
3282
|
+
{
|
|
3283
|
+
case: { $eq: ["$checklist.units.status", "closed"] },
|
|
3284
|
+
then: "Closed"
|
|
3186
3285
|
}
|
|
3187
3286
|
],
|
|
3188
3287
|
default: "$checklist.units.status"
|
|
@@ -3203,6 +3302,7 @@ function useAreaChecklistRepo() {
|
|
|
3203
3302
|
name: "$name",
|
|
3204
3303
|
approve: "$approve",
|
|
3205
3304
|
reject: "$reject",
|
|
3305
|
+
timestamp: "$timestamp",
|
|
3206
3306
|
status: "$status",
|
|
3207
3307
|
remarks: "$remarks",
|
|
3208
3308
|
completedByName: "$completedByName"
|
|
@@ -3315,7 +3415,7 @@ function useAreaChecklistRepo() {
|
|
|
3315
3415
|
} else if (value.reject === true) {
|
|
3316
3416
|
updateValue["checklist.$[checklist].units.$[unit].approve"] = false;
|
|
3317
3417
|
updateValue["checklist.$[checklist].units.$[unit].reject"] = true;
|
|
3318
|
-
updateValue["checklist.$[checklist].units.$[unit].status"] = "
|
|
3418
|
+
updateValue["checklist.$[checklist].units.$[unit].status"] = "open";
|
|
3319
3419
|
}
|
|
3320
3420
|
if (value.remarks) {
|
|
3321
3421
|
updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
|
|
@@ -3441,6 +3541,42 @@ function useAreaChecklistRepo() {
|
|
|
3441
3541
|
return 0;
|
|
3442
3542
|
}
|
|
3443
3543
|
}
|
|
3544
|
+
async function closeExpiredAreaChecklists() {
|
|
3545
|
+
try {
|
|
3546
|
+
const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
3547
|
+
const result = await collection.updateMany(
|
|
3548
|
+
{
|
|
3549
|
+
status: { $in: ["open", "ongoing", "completed"] },
|
|
3550
|
+
createdAt: { $lte: expiryDate }
|
|
3551
|
+
},
|
|
3552
|
+
{
|
|
3553
|
+
$set: {
|
|
3554
|
+
status: "closed",
|
|
3555
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
3556
|
+
"checklist.$[].units.$[unit].status": "closed"
|
|
3557
|
+
}
|
|
3558
|
+
},
|
|
3559
|
+
{
|
|
3560
|
+
arrayFilters: [{ "unit.status": { $in: ["open", "completed"] } }]
|
|
3561
|
+
}
|
|
3562
|
+
);
|
|
3563
|
+
if (result.modifiedCount > 0) {
|
|
3564
|
+
delNamespace().catch((err) => {
|
|
3565
|
+
import_node_server_utils17.logger.error(
|
|
3566
|
+
"Failed to invalidate cache after closing expired area checklists",
|
|
3567
|
+
err
|
|
3568
|
+
);
|
|
3569
|
+
});
|
|
3570
|
+
import_node_server_utils17.logger.info(
|
|
3571
|
+
`Closed ${result.modifiedCount} expired area checklists and their units`
|
|
3572
|
+
);
|
|
3573
|
+
}
|
|
3574
|
+
return result.modifiedCount;
|
|
3575
|
+
} catch (error) {
|
|
3576
|
+
import_node_server_utils17.logger.error("Failed to close expired area checklists", error);
|
|
3577
|
+
throw error;
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3444
3580
|
return {
|
|
3445
3581
|
createIndex,
|
|
3446
3582
|
createTextIndex,
|
|
@@ -3454,7 +3590,8 @@ function useAreaChecklistRepo() {
|
|
|
3454
3590
|
updateAreaChecklist,
|
|
3455
3591
|
updateAreaChecklistStatus,
|
|
3456
3592
|
updateAreaChecklistUnits,
|
|
3457
|
-
getMaxSetNumberForArea
|
|
3593
|
+
getMaxSetNumberForArea,
|
|
3594
|
+
closeExpiredAreaChecklists
|
|
3458
3595
|
};
|
|
3459
3596
|
}
|
|
3460
3597
|
|
|
@@ -3550,12 +3687,12 @@ function useAreaChecklistService() {
|
|
|
3550
3687
|
},
|
|
3551
3688
|
session
|
|
3552
3689
|
);
|
|
3553
|
-
let areaStatus = "
|
|
3690
|
+
let areaStatus = "open";
|
|
3554
3691
|
if (allUnitsResult && allUnitsResult.items && allUnitsResult.items.length > 0) {
|
|
3555
3692
|
const sets = allUnitsResult.items;
|
|
3556
3693
|
const allUnits = sets.flatMap((set2) => set2.units || []);
|
|
3557
|
-
const
|
|
3558
|
-
(unit) => unit.status === "
|
|
3694
|
+
const openCount = allUnits.filter(
|
|
3695
|
+
(unit) => unit.status === "Open"
|
|
3559
3696
|
).length;
|
|
3560
3697
|
const completedCount = allUnits.filter(
|
|
3561
3698
|
(unit) => unit.status === "Completed"
|
|
@@ -3563,8 +3700,8 @@ function useAreaChecklistService() {
|
|
|
3563
3700
|
const totalCount = allUnits.length;
|
|
3564
3701
|
if (completedCount === totalCount) {
|
|
3565
3702
|
areaStatus = "completed";
|
|
3566
|
-
} else if (
|
|
3567
|
-
areaStatus = "
|
|
3703
|
+
} else if (openCount === totalCount) {
|
|
3704
|
+
areaStatus = "open";
|
|
3568
3705
|
} else {
|
|
3569
3706
|
areaStatus = "ongoing";
|
|
3570
3707
|
}
|
|
@@ -3583,18 +3720,18 @@ function useAreaChecklistService() {
|
|
|
3583
3720
|
);
|
|
3584
3721
|
if (allAreasResult && allAreasResult.items && allAreasResult.items.length > 0) {
|
|
3585
3722
|
const areas = allAreasResult.items;
|
|
3586
|
-
const
|
|
3587
|
-
(area) => area.status === "
|
|
3723
|
+
const openAreasCount = areas.filter(
|
|
3724
|
+
(area) => area.status === "Open"
|
|
3588
3725
|
).length;
|
|
3589
3726
|
const completedAreasCount = areas.filter(
|
|
3590
3727
|
(area) => area.status === "Completed"
|
|
3591
3728
|
).length;
|
|
3592
3729
|
const totalAreasCount = areas.length;
|
|
3593
|
-
let parentStatus = "
|
|
3730
|
+
let parentStatus = "open";
|
|
3594
3731
|
if (completedAreasCount === totalAreasCount) {
|
|
3595
3732
|
parentStatus = "completed";
|
|
3596
|
-
} else if (
|
|
3597
|
-
parentStatus = "
|
|
3733
|
+
} else if (openAreasCount === totalAreasCount) {
|
|
3734
|
+
parentStatus = "open";
|
|
3598
3735
|
} else {
|
|
3599
3736
|
parentStatus = "ongoing";
|
|
3600
3737
|
}
|
|
@@ -3605,7 +3742,7 @@ function useAreaChecklistService() {
|
|
|
3605
3742
|
);
|
|
3606
3743
|
} else {
|
|
3607
3744
|
import_node_server_utils18.logger.info(
|
|
3608
|
-
"No area checklists found, keeping parent status as
|
|
3745
|
+
"No area checklists found, keeping parent status as open"
|
|
3609
3746
|
);
|
|
3610
3747
|
}
|
|
3611
3748
|
}
|
|
@@ -5163,7 +5300,7 @@ function useCheckOutItemService() {
|
|
|
5163
5300
|
const session = import_node_server_utils30.useAtlas.getClient()?.startSession();
|
|
5164
5301
|
try {
|
|
5165
5302
|
session?.startTransaction();
|
|
5166
|
-
const { site,
|
|
5303
|
+
const { site, createdBy, items } = value;
|
|
5167
5304
|
const createdByData = await getUserById(createdBy);
|
|
5168
5305
|
const createdCheckOutItemIds = [];
|
|
5169
5306
|
for (const item of items) {
|
|
@@ -5174,7 +5311,7 @@ function useCheckOutItemService() {
|
|
|
5174
5311
|
supply: item.supply,
|
|
5175
5312
|
supplyName: supplyData?.name || "",
|
|
5176
5313
|
qty: item.qty,
|
|
5177
|
-
attachment,
|
|
5314
|
+
attachment: item.attachment,
|
|
5178
5315
|
createdBy,
|
|
5179
5316
|
createdByName: createdByData?.name || ""
|
|
5180
5317
|
},
|
|
@@ -5265,12 +5402,12 @@ function useCheckOutItemController() {
|
|
|
5265
5402
|
};
|
|
5266
5403
|
const validation = import_joi15.default.object({
|
|
5267
5404
|
site: import_joi15.default.string().hex().required(),
|
|
5268
|
-
attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null),
|
|
5269
5405
|
createdBy: import_joi15.default.string().hex().required(),
|
|
5270
5406
|
items: import_joi15.default.array().items(
|
|
5271
5407
|
import_joi15.default.object({
|
|
5272
5408
|
supply: import_joi15.default.string().hex().required(),
|
|
5273
|
-
qty: import_joi15.default.number().min(0).required()
|
|
5409
|
+
qty: import_joi15.default.number().min(0).required(),
|
|
5410
|
+
attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null)
|
|
5274
5411
|
})
|
|
5275
5412
|
).min(1).required()
|
|
5276
5413
|
});
|
|
@@ -5358,8 +5495,9 @@ var scheduleTaskSchema = import_joi16.default.object({
|
|
|
5358
5495
|
site: import_joi16.default.string().hex().required(),
|
|
5359
5496
|
title: import_joi16.default.string().required(),
|
|
5360
5497
|
time: import_joi16.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
|
|
5361
|
-
|
|
5362
|
-
|
|
5498
|
+
dates: import_joi16.default.array().min(1).items(
|
|
5499
|
+
import_joi16.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
|
|
5500
|
+
).required(),
|
|
5363
5501
|
description: import_joi16.default.string().optional().allow("", null),
|
|
5364
5502
|
areas: import_joi16.default.array().min(1).items(
|
|
5365
5503
|
import_joi16.default.object({
|
|
@@ -5405,8 +5543,7 @@ function MScheduleTask(value) {
|
|
|
5405
5543
|
site: value.site,
|
|
5406
5544
|
title: value.title,
|
|
5407
5545
|
time: value.time,
|
|
5408
|
-
|
|
5409
|
-
endDate: value.endDate,
|
|
5546
|
+
dates: value.dates,
|
|
5410
5547
|
description: value.description,
|
|
5411
5548
|
areas: value.areas,
|
|
5412
5549
|
status: "active",
|
|
@@ -5619,8 +5756,7 @@ function useScheduleTaskRepository() {
|
|
|
5619
5756
|
$project: {
|
|
5620
5757
|
title: 1,
|
|
5621
5758
|
time: 1,
|
|
5622
|
-
|
|
5623
|
-
endDate: 1,
|
|
5759
|
+
dates: 1,
|
|
5624
5760
|
description: 1,
|
|
5625
5761
|
areas: 1,
|
|
5626
5762
|
status: 1,
|
|
@@ -5717,29 +5853,24 @@ function useScheduleTaskService() {
|
|
|
5717
5853
|
timeZone: "Asia/Singapore"
|
|
5718
5854
|
});
|
|
5719
5855
|
const [currentHour, currentMinute] = timeString.split(":").map(Number);
|
|
5720
|
-
const currentDateString = now.toLocaleDateString("en-US", {
|
|
5721
|
-
timeZone: "Asia/Singapore"
|
|
5722
|
-
});
|
|
5723
5856
|
import_node_server_utils34.logger.info(
|
|
5724
|
-
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute},
|
|
5857
|
+
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
|
|
5725
5858
|
);
|
|
5726
|
-
const
|
|
5727
|
-
|
|
5728
|
-
|
|
5859
|
+
const currentDateFormatted = now.toLocaleDateString("en-CA", {
|
|
5860
|
+
timeZone: "Asia/Singapore"
|
|
5861
|
+
});
|
|
5862
|
+
if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
|
|
5863
|
+
import_node_server_utils34.logger.info(`Schedule ${schedule._id}: No dates configured, skipping`);
|
|
5864
|
+
return false;
|
|
5865
|
+
}
|
|
5866
|
+
if (!schedule.dates.includes(currentDateFormatted)) {
|
|
5729
5867
|
import_node_server_utils34.logger.info(
|
|
5730
|
-
`Schedule ${schedule._id}: Current date ${
|
|
5868
|
+
`Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
|
|
5869
|
+
", "
|
|
5870
|
+
)}]`
|
|
5731
5871
|
);
|
|
5732
5872
|
return false;
|
|
5733
5873
|
}
|
|
5734
|
-
if (schedule.endDate) {
|
|
5735
|
-
const endDate = /* @__PURE__ */ new Date(schedule.endDate + "T00:00:00");
|
|
5736
|
-
if (currentDateOnly > endDate) {
|
|
5737
|
-
import_node_server_utils34.logger.info(
|
|
5738
|
-
`Schedule ${schedule._id}: Current date ${currentDateString} is after end date ${schedule.endDate}`
|
|
5739
|
-
);
|
|
5740
|
-
return false;
|
|
5741
|
-
}
|
|
5742
|
-
}
|
|
5743
5874
|
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
5744
5875
|
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
5745
5876
|
if (!timeMatches) {
|
|
@@ -5775,7 +5906,9 @@ function useScheduleTaskService() {
|
|
|
5775
5906
|
for (const scheduleTask of scheduleTasks) {
|
|
5776
5907
|
try {
|
|
5777
5908
|
import_node_server_utils34.logger.info(
|
|
5778
|
-
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time},
|
|
5909
|
+
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, dates=${JSON.stringify(
|
|
5910
|
+
scheduleTask.dates
|
|
5911
|
+
)}`
|
|
5779
5912
|
);
|
|
5780
5913
|
const shouldRun = checkScheduleConditions(scheduleTask, currentDate);
|
|
5781
5914
|
if (!shouldRun) {
|
|
@@ -6085,8 +6218,9 @@ function useScheduleTaskController() {
|
|
|
6085
6218
|
id: import_joi17.default.string().hex().required(),
|
|
6086
6219
|
title: import_joi17.default.string().optional().allow("", null),
|
|
6087
6220
|
time: import_joi17.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).optional().allow("", null),
|
|
6088
|
-
|
|
6089
|
-
|
|
6221
|
+
dates: import_joi17.default.array().min(1).items(
|
|
6222
|
+
import_joi17.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
|
|
6223
|
+
).optional(),
|
|
6090
6224
|
description: import_joi17.default.string().optional().allow("", null),
|
|
6091
6225
|
areas: import_joi17.default.array().min(1).items(
|
|
6092
6226
|
import_joi17.default.object({
|
|
@@ -6200,7 +6334,7 @@ function useQRService() {
|
|
|
6200
6334
|
throw error;
|
|
6201
6335
|
}
|
|
6202
6336
|
}
|
|
6203
|
-
async function generateQRPDF(qrUrl, title) {
|
|
6337
|
+
async function generateQRPDF(qrUrl, title, subtitle) {
|
|
6204
6338
|
try {
|
|
6205
6339
|
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
6206
6340
|
const browser = await (0, import_puppeteer2.launch)({
|
|
@@ -6214,6 +6348,7 @@ function useQRService() {
|
|
|
6214
6348
|
height: 1100
|
|
6215
6349
|
});
|
|
6216
6350
|
const escapedTitle = (title || "Cleaning Schedule QR Code").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6351
|
+
const escapedSubtitle = subtitle ? subtitle.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """) : "";
|
|
6217
6352
|
const html = `
|
|
6218
6353
|
<!DOCTYPE html>
|
|
6219
6354
|
<html>
|
|
@@ -6241,10 +6376,16 @@ function useQRService() {
|
|
|
6241
6376
|
padding: 0;
|
|
6242
6377
|
}
|
|
6243
6378
|
h1 {
|
|
6244
|
-
font-size:
|
|
6379
|
+
font-size: 38px;
|
|
6245
6380
|
color: #333;
|
|
6246
|
-
margin-bottom:
|
|
6247
|
-
font-weight:
|
|
6381
|
+
margin-bottom: 8px;
|
|
6382
|
+
font-weight: 700;
|
|
6383
|
+
}
|
|
6384
|
+
.subtitle {
|
|
6385
|
+
font-size: 26px;
|
|
6386
|
+
color: #555;
|
|
6387
|
+
margin-bottom: 12px;
|
|
6388
|
+
font-weight: 500;
|
|
6248
6389
|
}
|
|
6249
6390
|
.qr-wrapper {
|
|
6250
6391
|
display: inline-block;
|
|
@@ -6279,6 +6420,7 @@ function useQRService() {
|
|
|
6279
6420
|
<body>
|
|
6280
6421
|
<div class="qr-container">
|
|
6281
6422
|
<h1>${escapedTitle}</h1>
|
|
6423
|
+
${escapedSubtitle ? `<p class="subtitle">${escapedSubtitle}</p>` : ""}
|
|
6282
6424
|
<div class="qr-wrapper">
|
|
6283
6425
|
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
6284
6426
|
</div>
|
|
@@ -6339,6 +6481,7 @@ function useQRController() {
|
|
|
6339
6481
|
url: import_joi18.default.string().uri().required(),
|
|
6340
6482
|
filename: import_joi18.default.string().optional().allow("", null),
|
|
6341
6483
|
title: import_joi18.default.string().optional().allow("", null),
|
|
6484
|
+
subtitle: import_joi18.default.string().optional().allow("", null),
|
|
6342
6485
|
download: import_joi18.default.boolean().optional().default(false)
|
|
6343
6486
|
});
|
|
6344
6487
|
const query = { ...req.query };
|
|
@@ -6349,9 +6492,9 @@ function useQRController() {
|
|
|
6349
6492
|
return;
|
|
6350
6493
|
}
|
|
6351
6494
|
try {
|
|
6352
|
-
const { url, filename, title, download } = value;
|
|
6495
|
+
const { url, filename, title, subtitle, download } = value;
|
|
6353
6496
|
if (download) {
|
|
6354
|
-
const pdfBuffer = await _generateQRPDF(url, title);
|
|
6497
|
+
const pdfBuffer = await _generateQRPDF(url, title, subtitle);
|
|
6355
6498
|
if (!pdfBuffer || pdfBuffer.length === 0) {
|
|
6356
6499
|
throw new Error("Generated QR PDF is empty or invalid.");
|
|
6357
6500
|
}
|