@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.mjs
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// src/models/hygiene-base.model.ts
|
|
2
2
|
var allowedTypes = ["common", "toilet"];
|
|
3
|
-
var allowedStatus = [
|
|
3
|
+
var allowedStatus = [
|
|
4
|
+
"open",
|
|
5
|
+
"ongoing",
|
|
6
|
+
"completed",
|
|
7
|
+
"closed"
|
|
8
|
+
];
|
|
4
9
|
var allowedPeriods = ["today", "thisWeek", "thisMonth"];
|
|
5
10
|
|
|
6
11
|
// src/repositories/hygiene-dashboard.repository.ts
|
|
@@ -1310,10 +1315,14 @@ function useAreaService() {
|
|
|
1310
1315
|
try {
|
|
1311
1316
|
dataArray = JSON.parse(dataJson);
|
|
1312
1317
|
} catch (error) {
|
|
1313
|
-
throw new BadRequestError7(
|
|
1318
|
+
throw new BadRequestError7(
|
|
1319
|
+
"We couldn't read the uploaded file. Please make sure you're uploading the correct file and try again."
|
|
1320
|
+
);
|
|
1314
1321
|
}
|
|
1315
1322
|
if (!dataArray || dataArray.length === 0) {
|
|
1316
|
-
throw new NotFoundError2(
|
|
1323
|
+
throw new NotFoundError2(
|
|
1324
|
+
"The uploaded file is empty. Please make sure your file contains data and try again."
|
|
1325
|
+
);
|
|
1317
1326
|
}
|
|
1318
1327
|
let availableUnits = [];
|
|
1319
1328
|
try {
|
|
@@ -1414,33 +1423,35 @@ function useAreaService() {
|
|
|
1414
1423
|
}
|
|
1415
1424
|
}
|
|
1416
1425
|
}
|
|
1417
|
-
let message = `Upload
|
|
1426
|
+
let message = `Upload complete! ${insertedAreaIds.length} ${insertedAreaIds.length === 1 ? "area was" : "areas were"} successfully added`;
|
|
1418
1427
|
if (duplicateAreas.length > 0) {
|
|
1419
|
-
message += `, ${duplicateAreas.length} areas skipped
|
|
1428
|
+
message += `, ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area was" : "areas were"} skipped because they already exist`;
|
|
1420
1429
|
}
|
|
1421
1430
|
if (failedAreas.length > 0) {
|
|
1422
|
-
message += `, ${failedAreas.length} areas
|
|
1431
|
+
message += `, ${failedAreas.length} ${failedAreas.length === 1 ? "area" : "areas"} could not be saved`;
|
|
1423
1432
|
}
|
|
1424
1433
|
if (skippedRows.length > 0) {
|
|
1425
|
-
message += `, ${skippedRows.length} rows skipped
|
|
1434
|
+
message += `, ${skippedRows.length} ${skippedRows.length === 1 ? "row was" : "rows were"} skipped due to missing information`;
|
|
1426
1435
|
}
|
|
1427
1436
|
logger8.info(message);
|
|
1428
1437
|
if (insertedAreaIds.length === 0) {
|
|
1429
1438
|
if (duplicateAreas.length > 0 && failedAreas.length === 0 && skippedRows.length === 0) {
|
|
1430
1439
|
return {
|
|
1431
|
-
message: `No new areas were
|
|
1440
|
+
message: `No new areas were added. All ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area" : "areas"} in your file already exist in the system: ${duplicateAreas.join(
|
|
1441
|
+
", "
|
|
1442
|
+
)}.`
|
|
1432
1443
|
};
|
|
1433
1444
|
} else if (failedAreas.length > 0) {
|
|
1434
1445
|
throw new BadRequestError7(
|
|
1435
|
-
`No areas were
|
|
1446
|
+
`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.`
|
|
1436
1447
|
);
|
|
1437
1448
|
} else if (skippedRows.length > 0 && duplicateAreas.length === 0) {
|
|
1438
1449
|
throw new BadRequestError7(
|
|
1439
|
-
`No areas were
|
|
1450
|
+
`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.`
|
|
1440
1451
|
);
|
|
1441
1452
|
} else {
|
|
1442
1453
|
return {
|
|
1443
|
-
message: `No new areas were
|
|
1454
|
+
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` : ""}.`
|
|
1444
1455
|
};
|
|
1445
1456
|
}
|
|
1446
1457
|
}
|
|
@@ -1451,11 +1462,11 @@ function useAreaService() {
|
|
|
1451
1462
|
throw error;
|
|
1452
1463
|
} else if (error.message.includes("validation")) {
|
|
1453
1464
|
throw new BadRequestError7(
|
|
1454
|
-
"Upload failed
|
|
1465
|
+
"Upload failed because some required information is missing or incorrect. Please review your file and make sure all fields are filled in properly."
|
|
1455
1466
|
);
|
|
1456
1467
|
} else {
|
|
1457
1468
|
throw new BadRequestError7(
|
|
1458
|
-
|
|
1469
|
+
"Something went wrong while uploading. Please check your file and try again."
|
|
1459
1470
|
);
|
|
1460
1471
|
}
|
|
1461
1472
|
}
|
|
@@ -1464,11 +1475,15 @@ function useAreaService() {
|
|
|
1464
1475
|
try {
|
|
1465
1476
|
const areas = await getAreasForChecklist(site);
|
|
1466
1477
|
if (!areas || !Array.isArray(areas) || areas.length === 0) {
|
|
1467
|
-
throw new BadRequestError7(
|
|
1478
|
+
throw new BadRequestError7(
|
|
1479
|
+
"There are no areas to export yet. Please add some areas first, then try again."
|
|
1480
|
+
);
|
|
1468
1481
|
}
|
|
1469
1482
|
const excelBuffer = await generateAreaExcel(areas);
|
|
1470
1483
|
if (!excelBuffer || excelBuffer.length === 0) {
|
|
1471
|
-
throw new Error(
|
|
1484
|
+
throw new Error(
|
|
1485
|
+
"Something went wrong while preparing your export file. Please try again."
|
|
1486
|
+
);
|
|
1472
1487
|
}
|
|
1473
1488
|
return excelBuffer;
|
|
1474
1489
|
} catch (error) {
|
|
@@ -1773,10 +1788,14 @@ function useUnitService() {
|
|
|
1773
1788
|
try {
|
|
1774
1789
|
dataArray = JSON.parse(dataJson);
|
|
1775
1790
|
} catch (error) {
|
|
1776
|
-
throw new BadRequestError9(
|
|
1791
|
+
throw new BadRequestError9(
|
|
1792
|
+
"We couldn't read the uploaded file. Please make sure you're uploading the correct file and try again."
|
|
1793
|
+
);
|
|
1777
1794
|
}
|
|
1778
1795
|
if (!dataArray || dataArray.length === 0) {
|
|
1779
|
-
throw new NotFoundError3(
|
|
1796
|
+
throw new NotFoundError3(
|
|
1797
|
+
"The uploaded file is empty. Please make sure your file contains data and try again."
|
|
1798
|
+
);
|
|
1780
1799
|
}
|
|
1781
1800
|
const insertedUnitIds = [];
|
|
1782
1801
|
const duplicateUnits = [];
|
|
@@ -1817,29 +1836,31 @@ function useUnitService() {
|
|
|
1817
1836
|
}
|
|
1818
1837
|
}
|
|
1819
1838
|
}
|
|
1820
|
-
let message = `Upload
|
|
1839
|
+
let message = `Upload complete! ${insertedUnitIds.length} ${insertedUnitIds.length === 1 ? "unit was" : "units were"} successfully added`;
|
|
1821
1840
|
if (duplicateUnits.length > 0) {
|
|
1822
|
-
message += `, ${duplicateUnits.length} units skipped
|
|
1841
|
+
message += `, ${duplicateUnits.length} ${duplicateUnits.length === 1 ? "unit was" : "units were"} skipped because they already exist`;
|
|
1823
1842
|
}
|
|
1824
1843
|
if (failedUnits.length > 0) {
|
|
1825
|
-
message += `, ${failedUnits.length} units
|
|
1844
|
+
message += `, ${failedUnits.length} ${failedUnits.length === 1 ? "unit" : "units"} could not be saved`;
|
|
1826
1845
|
}
|
|
1827
1846
|
if (skippedRows.length > 0) {
|
|
1828
|
-
message += `, ${skippedRows.length} rows skipped
|
|
1847
|
+
message += `, ${skippedRows.length} ${skippedRows.length === 1 ? "row was" : "rows were"} skipped due to missing information`;
|
|
1829
1848
|
}
|
|
1830
1849
|
logger11.info(message);
|
|
1831
1850
|
if (insertedUnitIds.length === 0) {
|
|
1832
1851
|
if (duplicateUnits.length > 0 && failedUnits.length === 0) {
|
|
1833
1852
|
throw new BadRequestError9(
|
|
1834
|
-
`No new units were
|
|
1853
|
+
`No new units were added. All ${duplicateUnits.length} ${duplicateUnits.length === 1 ? "unit" : "units"} in your file already exist in the system: ${duplicateUnits.join(
|
|
1854
|
+
", "
|
|
1855
|
+
)}.`
|
|
1835
1856
|
);
|
|
1836
1857
|
} else if (failedUnits.length > 0) {
|
|
1837
1858
|
throw new BadRequestError9(
|
|
1838
|
-
`No units were
|
|
1859
|
+
`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.`
|
|
1839
1860
|
);
|
|
1840
1861
|
} else if (skippedRows.length > 0) {
|
|
1841
1862
|
throw new BadRequestError9(
|
|
1842
|
-
`No units were
|
|
1863
|
+
`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.`
|
|
1843
1864
|
);
|
|
1844
1865
|
}
|
|
1845
1866
|
}
|
|
@@ -1850,15 +1871,15 @@ function useUnitService() {
|
|
|
1850
1871
|
throw error;
|
|
1851
1872
|
} else if (error.message.includes("duplicate")) {
|
|
1852
1873
|
throw new BadRequestError9(
|
|
1853
|
-
"Upload failed
|
|
1874
|
+
"Upload failed because some unit names are already taken. Please make sure each unit has a unique name and try again."
|
|
1854
1875
|
);
|
|
1855
1876
|
} else if (error.message.includes("validation")) {
|
|
1856
1877
|
throw new BadRequestError9(
|
|
1857
|
-
"Upload failed
|
|
1878
|
+
"Upload failed because some required information is missing or incorrect. Please review your file and make sure all fields are filled in properly."
|
|
1858
1879
|
);
|
|
1859
1880
|
} else {
|
|
1860
1881
|
throw new BadRequestError9(
|
|
1861
|
-
|
|
1882
|
+
"Something went wrong while uploading. Please check your file and try again."
|
|
1862
1883
|
);
|
|
1863
1884
|
}
|
|
1864
1885
|
}
|
|
@@ -1890,7 +1911,9 @@ function useUnitService() {
|
|
|
1890
1911
|
session?.startTransaction();
|
|
1891
1912
|
const isExistingArea = await verifyAreaByUnitId(_id);
|
|
1892
1913
|
if (isExistingArea) {
|
|
1893
|
-
throw new BadRequestError9(
|
|
1914
|
+
throw new BadRequestError9(
|
|
1915
|
+
"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."
|
|
1916
|
+
);
|
|
1894
1917
|
}
|
|
1895
1918
|
const result = await _deleteUnit(_id, session);
|
|
1896
1919
|
await session?.commitTransaction();
|
|
@@ -1916,11 +1939,15 @@ function useUnitService() {
|
|
|
1916
1939
|
site
|
|
1917
1940
|
});
|
|
1918
1941
|
if (!data || !data.items || data.items.length === 0) {
|
|
1919
|
-
throw new BadRequestError9(
|
|
1942
|
+
throw new BadRequestError9(
|
|
1943
|
+
"There are no units to export yet. Please add some units first, then try again."
|
|
1944
|
+
);
|
|
1920
1945
|
}
|
|
1921
1946
|
const excelBuffer = await _generateUnitExcel(data.items);
|
|
1922
1947
|
if (!excelBuffer || excelBuffer.length === 0) {
|
|
1923
|
-
throw new Error(
|
|
1948
|
+
throw new Error(
|
|
1949
|
+
"Something went wrong while preparing your export file. Please try again."
|
|
1950
|
+
);
|
|
1924
1951
|
}
|
|
1925
1952
|
return excelBuffer;
|
|
1926
1953
|
} catch (error) {
|
|
@@ -2127,7 +2154,7 @@ function MParentChecklist(value) {
|
|
|
2127
2154
|
}
|
|
2128
2155
|
return {
|
|
2129
2156
|
site: value.site,
|
|
2130
|
-
status: "
|
|
2157
|
+
status: "open",
|
|
2131
2158
|
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
2132
2159
|
updatedAt: value.updatedAt ?? ""
|
|
2133
2160
|
};
|
|
@@ -2325,18 +2352,23 @@ function useParentChecklistRepo() {
|
|
|
2325
2352
|
status: {
|
|
2326
2353
|
$switch: {
|
|
2327
2354
|
branches: [
|
|
2328
|
-
{ case: { $eq: ["$status", "
|
|
2355
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2329
2356
|
{
|
|
2330
2357
|
case: { $eq: ["$status", "ongoing"] },
|
|
2331
2358
|
then: "Ongoing"
|
|
2332
2359
|
},
|
|
2333
|
-
{ case: { $eq: ["$status", "completed"] }, then: "Completed" }
|
|
2360
|
+
{ case: { $eq: ["$status", "completed"] }, then: "Completed" },
|
|
2361
|
+
{
|
|
2362
|
+
case: { $eq: ["$status", "closed"] },
|
|
2363
|
+
then: "Closed"
|
|
2364
|
+
}
|
|
2334
2365
|
],
|
|
2335
2366
|
default: "$status"
|
|
2336
2367
|
}
|
|
2337
2368
|
},
|
|
2338
2369
|
completedAt: 1,
|
|
2339
|
-
createdAt: 1
|
|
2370
|
+
createdAt: 1,
|
|
2371
|
+
closeIn: { $add: ["$createdAt", 24 * 60 * 60 * 1e3] }
|
|
2340
2372
|
}
|
|
2341
2373
|
});
|
|
2342
2374
|
pipeline.push(
|
|
@@ -2424,11 +2456,42 @@ function useParentChecklistRepo() {
|
|
|
2424
2456
|
throw error;
|
|
2425
2457
|
}
|
|
2426
2458
|
}
|
|
2459
|
+
async function closeExpiredParentChecklists() {
|
|
2460
|
+
try {
|
|
2461
|
+
const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
2462
|
+
const result = await collection.updateMany(
|
|
2463
|
+
{
|
|
2464
|
+
status: { $in: ["open", "ongoing", "completed"] },
|
|
2465
|
+
createdAt: { $lte: expiryDate }
|
|
2466
|
+
},
|
|
2467
|
+
{
|
|
2468
|
+
$set: {
|
|
2469
|
+
status: "closed",
|
|
2470
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
);
|
|
2474
|
+
if (result.modifiedCount > 0) {
|
|
2475
|
+
delNamespace().catch((err) => {
|
|
2476
|
+
logger14.error(
|
|
2477
|
+
"Failed to invalidate cache after closing expired parent checklists",
|
|
2478
|
+
err
|
|
2479
|
+
);
|
|
2480
|
+
});
|
|
2481
|
+
logger14.info(`Closed ${result.modifiedCount} expired parent checklists`);
|
|
2482
|
+
}
|
|
2483
|
+
return result.modifiedCount;
|
|
2484
|
+
} catch (error) {
|
|
2485
|
+
logger14.error("Failed to close expired parent checklists", error);
|
|
2486
|
+
throw error;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2427
2489
|
return {
|
|
2428
2490
|
createIndex,
|
|
2429
2491
|
createParentChecklist,
|
|
2430
2492
|
getAllParentChecklist,
|
|
2431
|
-
updateParentChecklistStatuses
|
|
2493
|
+
updateParentChecklistStatuses,
|
|
2494
|
+
closeExpiredParentChecklists
|
|
2432
2495
|
};
|
|
2433
2496
|
}
|
|
2434
2497
|
|
|
@@ -2510,7 +2573,7 @@ function useParentChecklistController() {
|
|
|
2510
2573
|
import Joi8 from "joi";
|
|
2511
2574
|
import { ObjectId as ObjectId8 } from "mongodb";
|
|
2512
2575
|
import { BadRequestError as BadRequestError14, logger as logger16 } from "@7365admin1/node-server-utils";
|
|
2513
|
-
var allowedChecklistStatus = ["
|
|
2576
|
+
var allowedChecklistStatus = ["open", "completed", "closed"];
|
|
2514
2577
|
var areaChecklistSchema = Joi8.object({
|
|
2515
2578
|
schedule: Joi8.string().hex().required(),
|
|
2516
2579
|
area: Joi8.string().hex().required(),
|
|
@@ -2558,7 +2621,7 @@ function MAreaChecklist(value) {
|
|
|
2558
2621
|
name: unit.name,
|
|
2559
2622
|
approve: false,
|
|
2560
2623
|
reject: false,
|
|
2561
|
-
status: "
|
|
2624
|
+
status: "open",
|
|
2562
2625
|
remarks: "",
|
|
2563
2626
|
completedBy: "",
|
|
2564
2627
|
timestamp: ""
|
|
@@ -2579,7 +2642,7 @@ function MAreaChecklist(value) {
|
|
|
2579
2642
|
name: value.name,
|
|
2580
2643
|
type: value.type,
|
|
2581
2644
|
checklist: value.checklist || [],
|
|
2582
|
-
status: "
|
|
2645
|
+
status: "open",
|
|
2583
2646
|
createdBy: value.createdBy,
|
|
2584
2647
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2585
2648
|
completedAt: "",
|
|
@@ -2751,11 +2814,15 @@ function useAreaChecklistRepo() {
|
|
|
2751
2814
|
status: {
|
|
2752
2815
|
$switch: {
|
|
2753
2816
|
branches: [
|
|
2754
|
-
{ case: { $eq: ["$status", "
|
|
2817
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2755
2818
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2756
2819
|
{
|
|
2757
2820
|
case: { $eq: ["$status", "completed"] },
|
|
2758
2821
|
then: "Completed"
|
|
2822
|
+
},
|
|
2823
|
+
{
|
|
2824
|
+
case: { $eq: ["$status", "closed"] },
|
|
2825
|
+
then: "Closed"
|
|
2759
2826
|
}
|
|
2760
2827
|
],
|
|
2761
2828
|
default: "$status"
|
|
@@ -2857,11 +2924,15 @@ function useAreaChecklistRepo() {
|
|
|
2857
2924
|
status: {
|
|
2858
2925
|
$switch: {
|
|
2859
2926
|
branches: [
|
|
2860
|
-
{ case: { $eq: ["$status", "
|
|
2927
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2861
2928
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2862
2929
|
{
|
|
2863
2930
|
case: { $eq: ["$status", "completed"] },
|
|
2864
2931
|
then: "Completed"
|
|
2932
|
+
},
|
|
2933
|
+
{
|
|
2934
|
+
case: { $eq: ["$status", "closed"] },
|
|
2935
|
+
then: "Closed"
|
|
2865
2936
|
}
|
|
2866
2937
|
],
|
|
2867
2938
|
default: "$status"
|
|
@@ -2926,11 +2997,15 @@ function useAreaChecklistRepo() {
|
|
|
2926
2997
|
status: {
|
|
2927
2998
|
$switch: {
|
|
2928
2999
|
branches: [
|
|
2929
|
-
{ case: { $eq: ["$status", "
|
|
3000
|
+
{ case: { $eq: ["$status", "open"] }, then: "Open" },
|
|
2930
3001
|
{ case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
|
|
2931
3002
|
{
|
|
2932
3003
|
case: { $eq: ["$status", "completed"] },
|
|
2933
3004
|
then: "Completed"
|
|
3005
|
+
},
|
|
3006
|
+
{
|
|
3007
|
+
case: { $eq: ["$status", "closed"] },
|
|
3008
|
+
then: "Closed"
|
|
2934
3009
|
}
|
|
2935
3010
|
],
|
|
2936
3011
|
default: "$status"
|
|
@@ -2990,12 +3065,16 @@ function useAreaChecklistRepo() {
|
|
|
2990
3065
|
$switch: {
|
|
2991
3066
|
branches: [
|
|
2992
3067
|
{
|
|
2993
|
-
case: { $eq: ["$checklist.units.status", "
|
|
2994
|
-
then: "
|
|
3068
|
+
case: { $eq: ["$checklist.units.status", "open"] },
|
|
3069
|
+
then: "Open"
|
|
2995
3070
|
},
|
|
2996
3071
|
{
|
|
2997
3072
|
case: { $eq: ["$checklist.units.status", "completed"] },
|
|
2998
3073
|
then: "Completed"
|
|
3074
|
+
},
|
|
3075
|
+
{
|
|
3076
|
+
case: { $eq: ["$checklist.units.status", "closed"] },
|
|
3077
|
+
then: "Closed"
|
|
2999
3078
|
}
|
|
3000
3079
|
],
|
|
3001
3080
|
default: "$checklist.units.status"
|
|
@@ -3142,12 +3221,16 @@ function useAreaChecklistRepo() {
|
|
|
3142
3221
|
$switch: {
|
|
3143
3222
|
branches: [
|
|
3144
3223
|
{
|
|
3145
|
-
case: { $eq: ["$checklist.units.status", "
|
|
3146
|
-
then: "
|
|
3224
|
+
case: { $eq: ["$checklist.units.status", "open"] },
|
|
3225
|
+
then: "Open"
|
|
3147
3226
|
},
|
|
3148
3227
|
{
|
|
3149
3228
|
case: { $eq: ["$checklist.units.status", "completed"] },
|
|
3150
3229
|
then: "Completed"
|
|
3230
|
+
},
|
|
3231
|
+
{
|
|
3232
|
+
case: { $eq: ["$checklist.units.status", "closed"] },
|
|
3233
|
+
then: "Closed"
|
|
3151
3234
|
}
|
|
3152
3235
|
],
|
|
3153
3236
|
default: "$checklist.units.status"
|
|
@@ -3280,7 +3363,7 @@ function useAreaChecklistRepo() {
|
|
|
3280
3363
|
} else if (value.reject === true) {
|
|
3281
3364
|
updateValue["checklist.$[checklist].units.$[unit].approve"] = false;
|
|
3282
3365
|
updateValue["checklist.$[checklist].units.$[unit].reject"] = true;
|
|
3283
|
-
updateValue["checklist.$[checklist].units.$[unit].status"] = "
|
|
3366
|
+
updateValue["checklist.$[checklist].units.$[unit].status"] = "open";
|
|
3284
3367
|
}
|
|
3285
3368
|
if (value.remarks) {
|
|
3286
3369
|
updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
|
|
@@ -3406,6 +3489,42 @@ function useAreaChecklistRepo() {
|
|
|
3406
3489
|
return 0;
|
|
3407
3490
|
}
|
|
3408
3491
|
}
|
|
3492
|
+
async function closeExpiredAreaChecklists() {
|
|
3493
|
+
try {
|
|
3494
|
+
const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
3495
|
+
const result = await collection.updateMany(
|
|
3496
|
+
{
|
|
3497
|
+
status: { $in: ["open", "ongoing", "completed"] },
|
|
3498
|
+
createdAt: { $lte: expiryDate }
|
|
3499
|
+
},
|
|
3500
|
+
{
|
|
3501
|
+
$set: {
|
|
3502
|
+
status: "closed",
|
|
3503
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
3504
|
+
"checklist.$[].units.$[unit].status": "closed"
|
|
3505
|
+
}
|
|
3506
|
+
},
|
|
3507
|
+
{
|
|
3508
|
+
arrayFilters: [{ "unit.status": { $in: ["open", "completed"] } }]
|
|
3509
|
+
}
|
|
3510
|
+
);
|
|
3511
|
+
if (result.modifiedCount > 0) {
|
|
3512
|
+
delNamespace().catch((err) => {
|
|
3513
|
+
logger17.error(
|
|
3514
|
+
"Failed to invalidate cache after closing expired area checklists",
|
|
3515
|
+
err
|
|
3516
|
+
);
|
|
3517
|
+
});
|
|
3518
|
+
logger17.info(
|
|
3519
|
+
`Closed ${result.modifiedCount} expired area checklists and their units`
|
|
3520
|
+
);
|
|
3521
|
+
}
|
|
3522
|
+
return result.modifiedCount;
|
|
3523
|
+
} catch (error) {
|
|
3524
|
+
logger17.error("Failed to close expired area checklists", error);
|
|
3525
|
+
throw error;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3409
3528
|
return {
|
|
3410
3529
|
createIndex,
|
|
3411
3530
|
createTextIndex,
|
|
@@ -3419,7 +3538,8 @@ function useAreaChecklistRepo() {
|
|
|
3419
3538
|
updateAreaChecklist,
|
|
3420
3539
|
updateAreaChecklistStatus,
|
|
3421
3540
|
updateAreaChecklistUnits,
|
|
3422
|
-
getMaxSetNumberForArea
|
|
3541
|
+
getMaxSetNumberForArea,
|
|
3542
|
+
closeExpiredAreaChecklists
|
|
3423
3543
|
};
|
|
3424
3544
|
}
|
|
3425
3545
|
|
|
@@ -3515,12 +3635,12 @@ function useAreaChecklistService() {
|
|
|
3515
3635
|
},
|
|
3516
3636
|
session
|
|
3517
3637
|
);
|
|
3518
|
-
let areaStatus = "
|
|
3638
|
+
let areaStatus = "open";
|
|
3519
3639
|
if (allUnitsResult && allUnitsResult.items && allUnitsResult.items.length > 0) {
|
|
3520
3640
|
const sets = allUnitsResult.items;
|
|
3521
3641
|
const allUnits = sets.flatMap((set2) => set2.units || []);
|
|
3522
|
-
const
|
|
3523
|
-
(unit) => unit.status === "
|
|
3642
|
+
const openCount = allUnits.filter(
|
|
3643
|
+
(unit) => unit.status === "Open"
|
|
3524
3644
|
).length;
|
|
3525
3645
|
const completedCount = allUnits.filter(
|
|
3526
3646
|
(unit) => unit.status === "Completed"
|
|
@@ -3528,8 +3648,8 @@ function useAreaChecklistService() {
|
|
|
3528
3648
|
const totalCount = allUnits.length;
|
|
3529
3649
|
if (completedCount === totalCount) {
|
|
3530
3650
|
areaStatus = "completed";
|
|
3531
|
-
} else if (
|
|
3532
|
-
areaStatus = "
|
|
3651
|
+
} else if (openCount === totalCount) {
|
|
3652
|
+
areaStatus = "open";
|
|
3533
3653
|
} else {
|
|
3534
3654
|
areaStatus = "ongoing";
|
|
3535
3655
|
}
|
|
@@ -3548,18 +3668,18 @@ function useAreaChecklistService() {
|
|
|
3548
3668
|
);
|
|
3549
3669
|
if (allAreasResult && allAreasResult.items && allAreasResult.items.length > 0) {
|
|
3550
3670
|
const areas = allAreasResult.items;
|
|
3551
|
-
const
|
|
3552
|
-
(area) => area.status === "
|
|
3671
|
+
const openAreasCount = areas.filter(
|
|
3672
|
+
(area) => area.status === "Open"
|
|
3553
3673
|
).length;
|
|
3554
3674
|
const completedAreasCount = areas.filter(
|
|
3555
3675
|
(area) => area.status === "Completed"
|
|
3556
3676
|
).length;
|
|
3557
3677
|
const totalAreasCount = areas.length;
|
|
3558
|
-
let parentStatus = "
|
|
3678
|
+
let parentStatus = "open";
|
|
3559
3679
|
if (completedAreasCount === totalAreasCount) {
|
|
3560
3680
|
parentStatus = "completed";
|
|
3561
|
-
} else if (
|
|
3562
|
-
parentStatus = "
|
|
3681
|
+
} else if (openAreasCount === totalAreasCount) {
|
|
3682
|
+
parentStatus = "open";
|
|
3563
3683
|
} else {
|
|
3564
3684
|
parentStatus = "ongoing";
|
|
3565
3685
|
}
|
|
@@ -3570,7 +3690,7 @@ function useAreaChecklistService() {
|
|
|
3570
3690
|
);
|
|
3571
3691
|
} else {
|
|
3572
3692
|
logger18.info(
|
|
3573
|
-
"No area checklists found, keeping parent status as
|
|
3693
|
+
"No area checklists found, keeping parent status as open"
|
|
3574
3694
|
);
|
|
3575
3695
|
}
|
|
3576
3696
|
}
|
|
@@ -5158,7 +5278,7 @@ function useCheckOutItemService() {
|
|
|
5158
5278
|
const session = useAtlas13.getClient()?.startSession();
|
|
5159
5279
|
try {
|
|
5160
5280
|
session?.startTransaction();
|
|
5161
|
-
const { site,
|
|
5281
|
+
const { site, createdBy, items } = value;
|
|
5162
5282
|
const createdByData = await getUserById(createdBy);
|
|
5163
5283
|
const createdCheckOutItemIds = [];
|
|
5164
5284
|
for (const item of items) {
|
|
@@ -5169,7 +5289,7 @@ function useCheckOutItemService() {
|
|
|
5169
5289
|
supply: item.supply,
|
|
5170
5290
|
supplyName: supplyData?.name || "",
|
|
5171
5291
|
qty: item.qty,
|
|
5172
|
-
attachment,
|
|
5292
|
+
attachment: item.attachment,
|
|
5173
5293
|
createdBy,
|
|
5174
5294
|
createdByName: createdByData?.name || ""
|
|
5175
5295
|
},
|
|
@@ -5260,12 +5380,12 @@ function useCheckOutItemController() {
|
|
|
5260
5380
|
};
|
|
5261
5381
|
const validation = Joi15.object({
|
|
5262
5382
|
site: Joi15.string().hex().required(),
|
|
5263
|
-
attachment: Joi15.array().items(Joi15.string()).optional().allow(null),
|
|
5264
5383
|
createdBy: Joi15.string().hex().required(),
|
|
5265
5384
|
items: Joi15.array().items(
|
|
5266
5385
|
Joi15.object({
|
|
5267
5386
|
supply: Joi15.string().hex().required(),
|
|
5268
|
-
qty: Joi15.number().min(0).required()
|
|
5387
|
+
qty: Joi15.number().min(0).required(),
|
|
5388
|
+
attachment: Joi15.array().items(Joi15.string()).optional().allow(null)
|
|
5269
5389
|
})
|
|
5270
5390
|
).min(1).required()
|
|
5271
5391
|
});
|
|
@@ -5353,8 +5473,9 @@ var scheduleTaskSchema = Joi16.object({
|
|
|
5353
5473
|
site: Joi16.string().hex().required(),
|
|
5354
5474
|
title: Joi16.string().required(),
|
|
5355
5475
|
time: Joi16.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
|
|
5356
|
-
|
|
5357
|
-
|
|
5476
|
+
dates: Joi16.array().min(1).items(
|
|
5477
|
+
Joi16.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
|
|
5478
|
+
).required(),
|
|
5358
5479
|
description: Joi16.string().optional().allow("", null),
|
|
5359
5480
|
areas: Joi16.array().min(1).items(
|
|
5360
5481
|
Joi16.object({
|
|
@@ -5400,8 +5521,7 @@ function MScheduleTask(value) {
|
|
|
5400
5521
|
site: value.site,
|
|
5401
5522
|
title: value.title,
|
|
5402
5523
|
time: value.time,
|
|
5403
|
-
|
|
5404
|
-
endDate: value.endDate,
|
|
5524
|
+
dates: value.dates,
|
|
5405
5525
|
description: value.description,
|
|
5406
5526
|
areas: value.areas,
|
|
5407
5527
|
status: "active",
|
|
@@ -5623,8 +5743,7 @@ function useScheduleTaskRepository() {
|
|
|
5623
5743
|
$project: {
|
|
5624
5744
|
title: 1,
|
|
5625
5745
|
time: 1,
|
|
5626
|
-
|
|
5627
|
-
endDate: 1,
|
|
5746
|
+
dates: 1,
|
|
5628
5747
|
description: 1,
|
|
5629
5748
|
areas: 1,
|
|
5630
5749
|
status: 1,
|
|
@@ -5721,29 +5840,24 @@ function useScheduleTaskService() {
|
|
|
5721
5840
|
timeZone: "Asia/Singapore"
|
|
5722
5841
|
});
|
|
5723
5842
|
const [currentHour, currentMinute] = timeString.split(":").map(Number);
|
|
5724
|
-
const currentDateString = now.toLocaleDateString("en-US", {
|
|
5725
|
-
timeZone: "Asia/Singapore"
|
|
5726
|
-
});
|
|
5727
5843
|
logger32.info(
|
|
5728
|
-
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute},
|
|
5844
|
+
`Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
|
|
5729
5845
|
);
|
|
5730
|
-
const
|
|
5731
|
-
|
|
5732
|
-
|
|
5846
|
+
const currentDateFormatted = now.toLocaleDateString("en-CA", {
|
|
5847
|
+
timeZone: "Asia/Singapore"
|
|
5848
|
+
});
|
|
5849
|
+
if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
|
|
5850
|
+
logger32.info(`Schedule ${schedule._id}: No dates configured, skipping`);
|
|
5851
|
+
return false;
|
|
5852
|
+
}
|
|
5853
|
+
if (!schedule.dates.includes(currentDateFormatted)) {
|
|
5733
5854
|
logger32.info(
|
|
5734
|
-
`Schedule ${schedule._id}: Current date ${
|
|
5855
|
+
`Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
|
|
5856
|
+
", "
|
|
5857
|
+
)}]`
|
|
5735
5858
|
);
|
|
5736
5859
|
return false;
|
|
5737
5860
|
}
|
|
5738
|
-
if (schedule.endDate) {
|
|
5739
|
-
const endDate = /* @__PURE__ */ new Date(schedule.endDate + "T00:00:00");
|
|
5740
|
-
if (currentDateOnly > endDate) {
|
|
5741
|
-
logger32.info(
|
|
5742
|
-
`Schedule ${schedule._id}: Current date ${currentDateString} is after end date ${schedule.endDate}`
|
|
5743
|
-
);
|
|
5744
|
-
return false;
|
|
5745
|
-
}
|
|
5746
|
-
}
|
|
5747
5861
|
const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
|
|
5748
5862
|
const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
|
|
5749
5863
|
if (!timeMatches) {
|
|
@@ -5779,7 +5893,9 @@ function useScheduleTaskService() {
|
|
|
5779
5893
|
for (const scheduleTask of scheduleTasks) {
|
|
5780
5894
|
try {
|
|
5781
5895
|
logger32.info(
|
|
5782
|
-
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time},
|
|
5896
|
+
`Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, dates=${JSON.stringify(
|
|
5897
|
+
scheduleTask.dates
|
|
5898
|
+
)}`
|
|
5783
5899
|
);
|
|
5784
5900
|
const shouldRun = checkScheduleConditions(scheduleTask, currentDate);
|
|
5785
5901
|
if (!shouldRun) {
|
|
@@ -6089,8 +6205,9 @@ function useScheduleTaskController() {
|
|
|
6089
6205
|
id: Joi17.string().hex().required(),
|
|
6090
6206
|
title: Joi17.string().optional().allow("", null),
|
|
6091
6207
|
time: Joi17.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).optional().allow("", null),
|
|
6092
|
-
|
|
6093
|
-
|
|
6208
|
+
dates: Joi17.array().min(1).items(
|
|
6209
|
+
Joi17.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
|
|
6210
|
+
).optional(),
|
|
6094
6211
|
description: Joi17.string().optional().allow("", null),
|
|
6095
6212
|
areas: Joi17.array().min(1).items(
|
|
6096
6213
|
Joi17.object({
|
|
@@ -6204,7 +6321,7 @@ function useQRService() {
|
|
|
6204
6321
|
throw error;
|
|
6205
6322
|
}
|
|
6206
6323
|
}
|
|
6207
|
-
async function generateQRPDF(qrUrl, title) {
|
|
6324
|
+
async function generateQRPDF(qrUrl, title, subtitle) {
|
|
6208
6325
|
try {
|
|
6209
6326
|
const qrDataUrl = await generateQRDataUrl(qrUrl);
|
|
6210
6327
|
const browser = await launch2({
|
|
@@ -6218,6 +6335,7 @@ function useQRService() {
|
|
|
6218
6335
|
height: 1100
|
|
6219
6336
|
});
|
|
6220
6337
|
const escapedTitle = (title || "Cleaning Schedule QR Code").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6338
|
+
const escapedSubtitle = subtitle ? subtitle.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """) : "";
|
|
6221
6339
|
const html = `
|
|
6222
6340
|
<!DOCTYPE html>
|
|
6223
6341
|
<html>
|
|
@@ -6245,10 +6363,16 @@ function useQRService() {
|
|
|
6245
6363
|
padding: 0;
|
|
6246
6364
|
}
|
|
6247
6365
|
h1 {
|
|
6248
|
-
font-size:
|
|
6366
|
+
font-size: 38px;
|
|
6249
6367
|
color: #333;
|
|
6250
|
-
margin-bottom:
|
|
6251
|
-
font-weight:
|
|
6368
|
+
margin-bottom: 8px;
|
|
6369
|
+
font-weight: 700;
|
|
6370
|
+
}
|
|
6371
|
+
.subtitle {
|
|
6372
|
+
font-size: 26px;
|
|
6373
|
+
color: #555;
|
|
6374
|
+
margin-bottom: 12px;
|
|
6375
|
+
font-weight: 500;
|
|
6252
6376
|
}
|
|
6253
6377
|
.qr-wrapper {
|
|
6254
6378
|
display: inline-block;
|
|
@@ -6283,6 +6407,7 @@ function useQRService() {
|
|
|
6283
6407
|
<body>
|
|
6284
6408
|
<div class="qr-container">
|
|
6285
6409
|
<h1>${escapedTitle}</h1>
|
|
6410
|
+
${escapedSubtitle ? `<p class="subtitle">${escapedSubtitle}</p>` : ""}
|
|
6286
6411
|
<div class="qr-wrapper">
|
|
6287
6412
|
<img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
|
|
6288
6413
|
</div>
|
|
@@ -6343,6 +6468,7 @@ function useQRController() {
|
|
|
6343
6468
|
url: Joi18.string().uri().required(),
|
|
6344
6469
|
filename: Joi18.string().optional().allow("", null),
|
|
6345
6470
|
title: Joi18.string().optional().allow("", null),
|
|
6471
|
+
subtitle: Joi18.string().optional().allow("", null),
|
|
6346
6472
|
download: Joi18.boolean().optional().default(false)
|
|
6347
6473
|
});
|
|
6348
6474
|
const query = { ...req.query };
|
|
@@ -6353,9 +6479,9 @@ function useQRController() {
|
|
|
6353
6479
|
return;
|
|
6354
6480
|
}
|
|
6355
6481
|
try {
|
|
6356
|
-
const { url, filename, title, download } = value;
|
|
6482
|
+
const { url, filename, title, subtitle, download } = value;
|
|
6357
6483
|
if (download) {
|
|
6358
|
-
const pdfBuffer = await _generateQRPDF(url, title);
|
|
6484
|
+
const pdfBuffer = await _generateQRPDF(url, title, subtitle);
|
|
6359
6485
|
if (!pdfBuffer || pdfBuffer.length === 0) {
|
|
6360
6486
|
throw new Error("Generated QR PDF is empty or invalid.");
|
|
6361
6487
|
}
|