@7365admin1/module-hygiene 4.8.0 → 4.9.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 +6 -0
- package/dist/index.d.ts +10 -10
- package/dist/index.js +220 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +220 -94
- 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: "",
|
|
@@ -2786,11 +2849,15 @@ function useAreaChecklistRepo() {
|
|
|
2786
2849
|
status: {
|
|
2787
2850
|
$switch: {
|
|
2788
2851
|
branches: [
|
|
2789
|
-
{ case: { $eq: ["$status", "
|
|
2852
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2790
2853
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2791
2854
|
{
|
|
2792
2855
|
case: { $eq: ["$status", "completed"] },
|
|
2793
2856
|
then: "Completed"
|
|
2857
|
+
},
|
|
2858
|
+
{
|
|
2859
|
+
case: { $eq: ["$status", "closed"] },
|
|
2860
|
+
then: "Closed"
|
|
2794
2861
|
}
|
|
2795
2862
|
],
|
|
2796
2863
|
default: "$status"
|
|
@@ -2892,11 +2959,15 @@ function useAreaChecklistRepo() {
|
|
|
2892
2959
|
status: {
|
|
2893
2960
|
$switch: {
|
|
2894
2961
|
branches: [
|
|
2895
|
-
{ case: { $eq: ["$status", "
|
|
2962
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2896
2963
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2897
2964
|
{
|
|
2898
2965
|
case: { $eq: ["$status", "completed"] },
|
|
2899
2966
|
then: "Completed"
|
|
2967
|
+
},
|
|
2968
|
+
{
|
|
2969
|
+
case: { $eq: ["$status", "closed"] },
|
|
2970
|
+
then: "Closed"
|
|
2900
2971
|
}
|
|
2901
2972
|
],
|
|
2902
2973
|
default: "$status"
|
|
@@ -2961,11 +3032,15 @@ function useAreaChecklistRepo() {
|
|
|
2961
3032
|
status: {
|
|
2962
3033
|
$switch: {
|
|
2963
3034
|
branches: [
|
|
2964
|
-
{ case: { $eq: ["$status", "
|
|
3035
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2965
3036
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2966
3037
|
{
|
|
2967
3038
|
case: { $eq: ["$status", "completed"] },
|
|
2968
3039
|
then: "Completed"
|
|
3040
|
+
},
|
|
3041
|
+
{
|
|
3042
|
+
case: { $eq: ["$status", "closed"] },
|
|
3043
|
+
then: "Closed"
|
|
2969
3044
|
}
|
|
2970
3045
|
],
|
|
2971
3046
|
default: "$status"
|
|
@@ -3025,12 +3100,16 @@ function useAreaChecklistRepo() {
|
|
|
3025
3100
|
$switch: {
|
|
3026
3101
|
branches: [
|
|
3027
3102
|
{
|
|
3028
|
-
case: { $eq: ["$checklist.units.status", "
|
|
3029
|
-
then: "
|
|
3103
|
+
case: { $eq: ["$checklist.units.status", "open"] },
|
|
3104
|
+
then: "Open"
|
|
3030
3105
|
},
|
|
3031
3106
|
{
|
|
3032
3107
|
case: { $eq: ["$checklist.units.status", "completed"] },
|
|
3033
3108
|
then: "Completed"
|
|
3109
|
+
},
|
|
3110
|
+
{
|
|
3111
|
+
case: { $eq: ["$checklist.units.status", "closed"] },
|
|
3112
|
+
then: "Closed"
|
|
3034
3113
|
}
|
|
3035
3114
|
],
|
|
3036
3115
|
default: "$checklist.units.status"
|
|
@@ -3177,12 +3256,16 @@ function useAreaChecklistRepo() {
|
|
|
3177
3256
|
$switch: {
|
|
3178
3257
|
branches: [
|
|
3179
3258
|
{
|
|
3180
|
-
case: { $eq: ["$checklist.units.status", "
|
|
3181
|
-
then: "
|
|
3259
|
+
case: { $eq: ["$checklist.units.status", "open"] },
|
|
3260
|
+
then: "Open"
|
|
3182
3261
|
},
|
|
3183
3262
|
{
|
|
3184
3263
|
case: { $eq: ["$checklist.units.status", "completed"] },
|
|
3185
3264
|
then: "Completed"
|
|
3265
|
+
},
|
|
3266
|
+
{
|
|
3267
|
+
case: { $eq: ["$checklist.units.status", "closed"] },
|
|
3268
|
+
then: "Closed"
|
|
3186
3269
|
}
|
|
3187
3270
|
],
|
|
3188
3271
|
default: "$checklist.units.status"
|
|
@@ -3315,7 +3398,7 @@ function useAreaChecklistRepo() {
|
|
|
3315
3398
|
} else if (value.reject === true) {
|
|
3316
3399
|
updateValue["checklist.$[checklist].units.$[unit].approve"] = false;
|
|
3317
3400
|
updateValue["checklist.$[checklist].units.$[unit].reject"] = true;
|
|
3318
|
-
updateValue["checklist.$[checklist].units.$[unit].status"] = "
|
|
3401
|
+
updateValue["checklist.$[checklist].units.$[unit].status"] = "open";
|
|
3319
3402
|
}
|
|
3320
3403
|
if (value.remarks) {
|
|
3321
3404
|
updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
|
|
@@ -3441,6 +3524,42 @@ function useAreaChecklistRepo() {
|
|
|
3441
3524
|
return 0;
|
|
3442
3525
|
}
|
|
3443
3526
|
}
|
|
3527
|
+
async function closeExpiredAreaChecklists() {
|
|
3528
|
+
try {
|
|
3529
|
+
const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
3530
|
+
const result = await collection.updateMany(
|
|
3531
|
+
{
|
|
3532
|
+
status: { $in: ["open", "ongoing", "completed"] },
|
|
3533
|
+
createdAt: { $lte: expiryDate }
|
|
3534
|
+
},
|
|
3535
|
+
{
|
|
3536
|
+
$set: {
|
|
3537
|
+
status: "closed",
|
|
3538
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
3539
|
+
"checklist.$[].units.$[unit].status": "closed"
|
|
3540
|
+
}
|
|
3541
|
+
},
|
|
3542
|
+
{
|
|
3543
|
+
arrayFilters: [{ "unit.status": { $in: ["open", "completed"] } }]
|
|
3544
|
+
}
|
|
3545
|
+
);
|
|
3546
|
+
if (result.modifiedCount > 0) {
|
|
3547
|
+
delNamespace().catch((err) => {
|
|
3548
|
+
import_node_server_utils17.logger.error(
|
|
3549
|
+
"Failed to invalidate cache after closing expired area checklists",
|
|
3550
|
+
err
|
|
3551
|
+
);
|
|
3552
|
+
});
|
|
3553
|
+
import_node_server_utils17.logger.info(
|
|
3554
|
+
`Closed ${result.modifiedCount} expired area checklists and their units`
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3557
|
+
return result.modifiedCount;
|
|
3558
|
+
} catch (error) {
|
|
3559
|
+
import_node_server_utils17.logger.error("Failed to close expired area checklists", error);
|
|
3560
|
+
throw error;
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3444
3563
|
return {
|
|
3445
3564
|
createIndex,
|
|
3446
3565
|
createTextIndex,
|
|
@@ -3454,7 +3573,8 @@ function useAreaChecklistRepo() {
|
|
|
3454
3573
|
updateAreaChecklist,
|
|
3455
3574
|
updateAreaChecklistStatus,
|
|
3456
3575
|
updateAreaChecklistUnits,
|
|
3457
|
-
getMaxSetNumberForArea
|
|
3576
|
+
getMaxSetNumberForArea,
|
|
3577
|
+
closeExpiredAreaChecklists
|
|
3458
3578
|
};
|
|
3459
3579
|
}
|
|
3460
3580
|
|
|
@@ -3550,12 +3670,12 @@ function useAreaChecklistService() {
|
|
|
3550
3670
|
},
|
|
3551
3671
|
session
|
|
3552
3672
|
);
|
|
3553
|
-
let areaStatus = "
|
|
3673
|
+
let areaStatus = "open";
|
|
3554
3674
|
if (allUnitsResult && allUnitsResult.items && allUnitsResult.items.length > 0) {
|
|
3555
3675
|
const sets = allUnitsResult.items;
|
|
3556
3676
|
const allUnits = sets.flatMap((set2) => set2.units || []);
|
|
3557
|
-
const
|
|
3558
|
-
(unit) => unit.status === "
|
|
3677
|
+
const openCount = allUnits.filter(
|
|
3678
|
+
(unit) => unit.status === "Open"
|
|
3559
3679
|
).length;
|
|
3560
3680
|
const completedCount = allUnits.filter(
|
|
3561
3681
|
(unit) => unit.status === "Completed"
|
|
@@ -3563,8 +3683,8 @@ function useAreaChecklistService() {
|
|
|
3563
3683
|
const totalCount = allUnits.length;
|
|
3564
3684
|
if (completedCount === totalCount) {
|
|
3565
3685
|
areaStatus = "completed";
|
|
3566
|
-
} else if (
|
|
3567
|
-
areaStatus = "
|
|
3686
|
+
} else if (openCount === totalCount) {
|
|
3687
|
+
areaStatus = "open";
|
|
3568
3688
|
} else {
|
|
3569
3689
|
areaStatus = "ongoing";
|
|
3570
3690
|
}
|
|
@@ -3583,18 +3703,18 @@ function useAreaChecklistService() {
|
|
|
3583
3703
|
);
|
|
3584
3704
|
if (allAreasResult && allAreasResult.items && allAreasResult.items.length > 0) {
|
|
3585
3705
|
const areas = allAreasResult.items;
|
|
3586
|
-
const
|
|
3587
|
-
(area) => area.status === "
|
|
3706
|
+
const openAreasCount = areas.filter(
|
|
3707
|
+
(area) => area.status === "Open"
|
|
3588
3708
|
).length;
|
|
3589
3709
|
const completedAreasCount = areas.filter(
|
|
3590
3710
|
(area) => area.status === "Completed"
|
|
3591
3711
|
).length;
|
|
3592
3712
|
const totalAreasCount = areas.length;
|
|
3593
|
-
let parentStatus = "
|
|
3713
|
+
let parentStatus = "open";
|
|
3594
3714
|
if (completedAreasCount === totalAreasCount) {
|
|
3595
3715
|
parentStatus = "completed";
|
|
3596
|
-
} else if (
|
|
3597
|
-
parentStatus = "
|
|
3716
|
+
} else if (openAreasCount === totalAreasCount) {
|
|
3717
|
+
parentStatus = "open";
|
|
3598
3718
|
} else {
|
|
3599
3719
|
parentStatus = "ongoing";
|
|
3600
3720
|
}
|
|
@@ -3605,7 +3725,7 @@ function useAreaChecklistService() {
|
|
|
3605
3725
|
);
|
|
3606
3726
|
} else {
|
|
3607
3727
|
import_node_server_utils18.logger.info(
|
|
3608
|
-
"No area checklists found, keeping parent status as
|
|
3728
|
+
"No area checklists found, keeping parent status as open"
|
|
3609
3729
|
);
|
|
3610
3730
|
}
|
|
3611
3731
|
}
|
|
@@ -5163,7 +5283,7 @@ function useCheckOutItemService() {
|
|
|
5163
5283
|
const session = import_node_server_utils30.useAtlas.getClient()?.startSession();
|
|
5164
5284
|
try {
|
|
5165
5285
|
session?.startTransaction();
|
|
5166
|
-
const { site,
|
|
5286
|
+
const { site, createdBy, items } = value;
|
|
5167
5287
|
const createdByData = await getUserById(createdBy);
|
|
5168
5288
|
const createdCheckOutItemIds = [];
|
|
5169
5289
|
for (const item of items) {
|
|
@@ -5174,7 +5294,7 @@ function useCheckOutItemService() {
|
|
|
5174
5294
|
supply: item.supply,
|
|
5175
5295
|
supplyName: supplyData?.name || "",
|
|
5176
5296
|
qty: item.qty,
|
|
5177
|
-
attachment,
|
|
5297
|
+
attachment: item.attachment,
|
|
5178
5298
|
createdBy,
|
|
5179
5299
|
createdByName: createdByData?.name || ""
|
|
5180
5300
|
},
|
|
@@ -5265,12 +5385,12 @@ function useCheckOutItemController() {
|
|
|
5265
5385
|
};
|
|
5266
5386
|
const validation = import_joi15.default.object({
|
|
5267
5387
|
site: import_joi15.default.string().hex().required(),
|
|
5268
|
-
attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null),
|
|
5269
5388
|
createdBy: import_joi15.default.string().hex().required(),
|
|
5270
5389
|
items: import_joi15.default.array().items(
|
|
5271
5390
|
import_joi15.default.object({
|
|
5272
5391
|
supply: import_joi15.default.string().hex().required(),
|
|
5273
|
-
qty: import_joi15.default.number().min(0).required()
|
|
5392
|
+
qty: import_joi15.default.number().min(0).required(),
|
|
5393
|
+
attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null)
|
|
5274
5394
|
})
|
|
5275
5395
|
).min(1).required()
|
|
5276
5396
|
});
|
|
@@ -5358,8 +5478,9 @@ var scheduleTaskSchema = import_joi16.default.object({
|
|
|
5358
5478
|
site: import_joi16.default.string().hex().required(),
|
|
5359
5479
|
title: import_joi16.default.string().required(),
|
|
5360
5480
|
time: import_joi16.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
|
|
5361
|
-
|
|
5362
|
-
|
|
5481
|
+
dates: import_joi16.default.array().min(1).items(
|
|
5482
|
+
import_joi16.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
|
|
5483
|
+
).required(),
|
|
5363
5484
|
description: import_joi16.default.string().optional().allow("", null),
|
|
5364
5485
|
areas: import_joi16.default.array().min(1).items(
|
|
5365
5486
|
import_joi16.default.object({
|
|
@@ -5405,8 +5526,7 @@ function MScheduleTask(value) {
|
|
|
5405
5526
|
site: value.site,
|
|
5406
5527
|
title: value.title,
|
|
5407
5528
|
time: value.time,
|
|
5408
|
-
|
|
5409
|
-
endDate: value.endDate,
|
|
5529
|
+
dates: value.dates,
|
|
5410
5530
|
description: value.description,
|
|
5411
5531
|
areas: value.areas,
|
|
5412
5532
|
status: "active",
|
|
@@ -5619,8 +5739,7 @@ function useScheduleTaskRepository() {
|
|
|
5619
5739
|
$project: {
|
|
5620
5740
|
title: 1,
|
|
5621
5741
|
time: 1,
|
|
5622
|
-
|
|
5623
|
-
endDate: 1,
|
|
5742
|
+
dates: 1,
|
|
5624
5743
|
description: 1,
|
|
5625
5744
|
areas: 1,
|
|
5626
5745
|
status: 1,
|
|
@@ -5717,29 +5836,24 @@ function useScheduleTaskService() {
|
|
|
5717
5836
|
timeZone: "Asia/Singapore"
|
|
5718
5837
|
});
|
|
5719
5838
|
const [currentHour, currentMinute] = timeString.split(":").map(Number);
|
|
5720
|
-
const currentDateString = now.toLocaleDateString("en-US", {
|
|
5721
|
-
timeZone: "Asia/Singapore"
|
|
5722
|
-
});
|
|
5723
5839
|
import_node_server_utils34.logger.info(
|
|
5724
|
-
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute},
|
|
5840
|
+
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
|
|
5725
5841
|
);
|
|
5726
|
-
const
|
|
5727
|
-
|
|
5728
|
-
|
|
5842
|
+
const currentDateFormatted = now.toLocaleDateString("en-CA", {
|
|
5843
|
+
timeZone: "Asia/Singapore"
|
|
5844
|
+
});
|
|
5845
|
+
if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
|
|
5846
|
+
import_node_server_utils34.logger.info(`Schedule ${schedule._id}: No dates configured, skipping`);
|
|
5847
|
+
return false;
|
|
5848
|
+
}
|
|
5849
|
+
if (!schedule.dates.includes(currentDateFormatted)) {
|
|
5729
5850
|
import_node_server_utils34.logger.info(
|
|
5730
|
-
`Schedule ${schedule._id}: Current date ${
|
|
5851
|
+
`Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
|
|
5852
|
+
", "
|
|
5853
|
+
)}]`
|
|
5731
5854
|
);
|
|
5732
5855
|
return false;
|
|
5733
5856
|
}
|
|
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
5857
|
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
5744
5858
|
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
5745
5859
|
if (!timeMatches) {
|
|
@@ -5775,7 +5889,9 @@ function useScheduleTaskService() {
|
|
|
5775
5889
|
for (const scheduleTask of scheduleTasks) {
|
|
5776
5890
|
try {
|
|
5777
5891
|
import_node_server_utils34.logger.info(
|
|
5778
|
-
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time},
|
|
5892
|
+
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, dates=${JSON.stringify(
|
|
5893
|
+
scheduleTask.dates
|
|
5894
|
+
)}`
|
|
5779
5895
|
);
|
|
5780
5896
|
const shouldRun = checkScheduleConditions(scheduleTask, currentDate);
|
|
5781
5897
|
if (!shouldRun) {
|
|
@@ -6085,8 +6201,9 @@ function useScheduleTaskController() {
|
|
|
6085
6201
|
id: import_joi17.default.string().hex().required(),
|
|
6086
6202
|
title: import_joi17.default.string().optional().allow("", null),
|
|
6087
6203
|
time: import_joi17.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).optional().allow("", null),
|
|
6088
|
-
|
|
6089
|
-
|
|
6204
|
+
dates: import_joi17.default.array().min(1).items(
|
|
6205
|
+
import_joi17.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
|
|
6206
|
+
).optional(),
|
|
6090
6207
|
description: import_joi17.default.string().optional().allow("", null),
|
|
6091
6208
|
areas: import_joi17.default.array().min(1).items(
|
|
6092
6209
|
import_joi17.default.object({
|
|
@@ -6200,7 +6317,7 @@ function useQRService() {
|
|
|
6200
6317
|
throw error;
|
|
6201
6318
|
}
|
|
6202
6319
|
}
|
|
6203
|
-
async function generateQRPDF(qrUrl, title) {
|
|
6320
|
+
async function generateQRPDF(qrUrl, title, subtitle) {
|
|
6204
6321
|
try {
|
|
6205
6322
|
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
6206
6323
|
const browser = await (0, import_puppeteer2.launch)({
|
|
@@ -6214,6 +6331,7 @@ function useQRService() {
|
|
|
6214
6331
|
height: 1100
|
|
6215
6332
|
});
|
|
6216
6333
|
const escapedTitle = (title || "Cleaning Schedule QR Code").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6334
|
+
const escapedSubtitle = subtitle ? subtitle.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """) : "";
|
|
6217
6335
|
const html = `
|
|
6218
6336
|
<!DOCTYPE html>
|
|
6219
6337
|
<html>
|
|
@@ -6241,10 +6359,16 @@ function useQRService() {
|
|
|
6241
6359
|
padding: 0;
|
|
6242
6360
|
}
|
|
6243
6361
|
h1 {
|
|
6244
|
-
font-size:
|
|
6362
|
+
font-size: 38px;
|
|
6245
6363
|
color: #333;
|
|
6246
|
-
margin-bottom:
|
|
6247
|
-
font-weight:
|
|
6364
|
+
margin-bottom: 8px;
|
|
6365
|
+
font-weight: 700;
|
|
6366
|
+
}
|
|
6367
|
+
.subtitle {
|
|
6368
|
+
font-size: 26px;
|
|
6369
|
+
color: #555;
|
|
6370
|
+
margin-bottom: 12px;
|
|
6371
|
+
font-weight: 500;
|
|
6248
6372
|
}
|
|
6249
6373
|
.qr-wrapper {
|
|
6250
6374
|
display: inline-block;
|
|
@@ -6279,6 +6403,7 @@ function useQRService() {
|
|
|
6279
6403
|
<body>
|
|
6280
6404
|
<div class="qr-container">
|
|
6281
6405
|
<h1>${escapedTitle}</h1>
|
|
6406
|
+
${escapedSubtitle ? `<p class="subtitle">${escapedSubtitle}</p>` : ""}
|
|
6282
6407
|
<div class="qr-wrapper">
|
|
6283
6408
|
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
6284
6409
|
</div>
|
|
@@ -6339,6 +6464,7 @@ function useQRController() {
|
|
|
6339
6464
|
url: import_joi18.default.string().uri().required(),
|
|
6340
6465
|
filename: import_joi18.default.string().optional().allow("", null),
|
|
6341
6466
|
title: import_joi18.default.string().optional().allow("", null),
|
|
6467
|
+
subtitle: import_joi18.default.string().optional().allow("", null),
|
|
6342
6468
|
download: import_joi18.default.boolean().optional().default(false)
|
|
6343
6469
|
});
|
|
6344
6470
|
const query = { ...req.query };
|
|
@@ -6349,9 +6475,9 @@ function useQRController() {
|
|
|
6349
6475
|
return;
|
|
6350
6476
|
}
|
|
6351
6477
|
try {
|
|
6352
|
-
const { url, filename, title, download } = value;
|
|
6478
|
+
const { url, filename, title, subtitle, download } = value;
|
|
6353
6479
|
if (download) {
|
|
6354
|
-
const pdfBuffer = await _generateQRPDF(url, title);
|
|
6480
|
+
const pdfBuffer = await _generateQRPDF(url, title, subtitle);
|
|
6355
6481
|
if (!pdfBuffer || pdfBuffer.length === 0) {
|
|
6356
6482
|
throw new Error("Generated QR PDF is empty or invalid.");
|
|
6357
6483
|
}
|