@7365admin1/core 2.14.0 → 2.16.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 +95 -19
- package/dist/index.js +1355 -254
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1348 -252
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tsconfig.json +1 -3
package/dist/index.mjs
CHANGED
|
@@ -1388,7 +1388,9 @@ var schemaOccurrenceEntry = Joi5.object({
|
|
|
1388
1388
|
subject: Joi5.string().hex().optional().allow(null, ""),
|
|
1389
1389
|
occurrence: Joi5.string().optional().allow(null, ""),
|
|
1390
1390
|
signature: Joi5.string().hex().optional().allow(null, ""),
|
|
1391
|
-
incidentReportId: Joi5.string().hex().optional().allow(null, "")
|
|
1391
|
+
incidentReportId: Joi5.string().hex().optional().allow(null, ""),
|
|
1392
|
+
eSignature: Joi5.string().required(),
|
|
1393
|
+
createdByName: Joi5.string().optional().allow(null, "")
|
|
1392
1394
|
});
|
|
1393
1395
|
var schemaUpdateOccurrenceEntry = Joi5.object({
|
|
1394
1396
|
_id: Joi5.string().hex().required(),
|
|
@@ -1397,7 +1399,9 @@ var schemaUpdateOccurrenceEntry = Joi5.object({
|
|
|
1397
1399
|
subject: Joi5.string().hex().optional().allow(null, ""),
|
|
1398
1400
|
occurrence: Joi5.string().optional().allow(null, ""),
|
|
1399
1401
|
signature: Joi5.string().hex().optional().allow(null, ""),
|
|
1400
|
-
incidentReportId: Joi5.string().hex().optional().allow(null, "")
|
|
1402
|
+
incidentReportId: Joi5.string().hex().optional().allow(null, ""),
|
|
1403
|
+
eSignature: Joi5.string().required(),
|
|
1404
|
+
createdByName: Joi5.string().optional().allow(null, "")
|
|
1401
1405
|
});
|
|
1402
1406
|
function MOccurrenceEntry(value) {
|
|
1403
1407
|
if (value._id && typeof value._id === "string") {
|
|
@@ -1454,6 +1458,8 @@ function MOccurrenceEntry(value) {
|
|
|
1454
1458
|
incidentReportId: value.incidentReportId,
|
|
1455
1459
|
signature: value.signature,
|
|
1456
1460
|
userName: value.userName,
|
|
1461
|
+
eSignature: value.eSignature,
|
|
1462
|
+
createdByName: value.createdByName,
|
|
1457
1463
|
date: value.date,
|
|
1458
1464
|
createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1459
1465
|
updatedAt: value.updatedAt,
|
|
@@ -6424,12 +6430,12 @@ function useUserController() {
|
|
|
6424
6430
|
}
|
|
6425
6431
|
}
|
|
6426
6432
|
async function createUserByVerification(req, res, next) {
|
|
6427
|
-
const
|
|
6433
|
+
const allowedTypes = ["user-sign-up", "user-invite"];
|
|
6428
6434
|
const validation = Joi14.object({
|
|
6429
6435
|
id: Joi14.string().hex().required(),
|
|
6430
6436
|
name: Joi14.string().required(),
|
|
6431
6437
|
password: Joi14.string().required(),
|
|
6432
|
-
type: Joi14.string().required().valid(...
|
|
6438
|
+
type: Joi14.string().required().valid(...allowedTypes)
|
|
6433
6439
|
});
|
|
6434
6440
|
const id = req.params.id;
|
|
6435
6441
|
const payload = { ...req.body };
|
|
@@ -10900,7 +10906,7 @@ function useFeedbackController() {
|
|
|
10900
10906
|
}
|
|
10901
10907
|
async function deleteFeedback(req, res, next) {
|
|
10902
10908
|
const validation = Joi28.string().hex().required();
|
|
10903
|
-
const _id = req.
|
|
10909
|
+
const _id = req.params.id;
|
|
10904
10910
|
const { error } = validation.validate(_id);
|
|
10905
10911
|
if (error) {
|
|
10906
10912
|
logger39.log({ level: "error", message: error.message });
|
|
@@ -11264,8 +11270,7 @@ function useWorkOrderController() {
|
|
|
11264
11270
|
}
|
|
11265
11271
|
async function deleteWorkOrder(req, res, next) {
|
|
11266
11272
|
const validation = Joi29.string().hex().required();
|
|
11267
|
-
const _id = req.
|
|
11268
|
-
console.log(_id);
|
|
11273
|
+
const _id = req.params.id;
|
|
11269
11274
|
const { error } = validation.validate(_id);
|
|
11270
11275
|
if (error) {
|
|
11271
11276
|
logger40.log({ level: "error", message: error.message });
|
|
@@ -15065,11 +15070,14 @@ function usePersonRepo() {
|
|
|
15065
15070
|
const namespace_collection = "site.people";
|
|
15066
15071
|
const collection = db.collection(namespace_collection);
|
|
15067
15072
|
const { delNamespace, getCache, setCache } = useCache27(namespace_collection);
|
|
15068
|
-
async function
|
|
15073
|
+
async function createIndexes() {
|
|
15069
15074
|
try {
|
|
15070
|
-
await collection.
|
|
15075
|
+
await collection.createIndexes([
|
|
15076
|
+
{ key: { contact: 1 } },
|
|
15077
|
+
{ key: { nric: 1 } }
|
|
15078
|
+
]);
|
|
15071
15079
|
} catch (error) {
|
|
15072
|
-
throw new InternalServerError27("Failed to create index.");
|
|
15080
|
+
throw new InternalServerError27("Failed to create index on site people.");
|
|
15073
15081
|
}
|
|
15074
15082
|
}
|
|
15075
15083
|
async function createTextIndex() {
|
|
@@ -15459,6 +15467,47 @@ function usePersonRepo() {
|
|
|
15459
15467
|
throw new InternalServerError27("Failed to fetch people by plate number.");
|
|
15460
15468
|
}
|
|
15461
15469
|
}
|
|
15470
|
+
async function getPeopleByNRIC({
|
|
15471
|
+
page = 1,
|
|
15472
|
+
limit = 10,
|
|
15473
|
+
nric = "",
|
|
15474
|
+
sort = {},
|
|
15475
|
+
site = ""
|
|
15476
|
+
}) {
|
|
15477
|
+
page = page > 0 ? page - 1 : 0;
|
|
15478
|
+
const normalizedNric = nric.trim();
|
|
15479
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
15480
|
+
const query = {
|
|
15481
|
+
...nric && { nric: normalizedNric },
|
|
15482
|
+
site: typeof site === "string" ? new ObjectId44(site) : site
|
|
15483
|
+
};
|
|
15484
|
+
const cacheOptions = {
|
|
15485
|
+
site,
|
|
15486
|
+
...nric && { nric: normalizedNric },
|
|
15487
|
+
page: String(page),
|
|
15488
|
+
limit: String(limit),
|
|
15489
|
+
sort: JSON.stringify(sort)
|
|
15490
|
+
};
|
|
15491
|
+
try {
|
|
15492
|
+
const cacheKey = makeCacheKey25(namespace_collection, cacheOptions);
|
|
15493
|
+
const cachedData = await getCache(cacheKey);
|
|
15494
|
+
if (cachedData) {
|
|
15495
|
+
logger60.info(`Cache hit for key: ${cacheKey}`);
|
|
15496
|
+
return cachedData;
|
|
15497
|
+
}
|
|
15498
|
+
const items = await collection.find(query).sort(sort).skip(page * limit).limit(limit).toArray();
|
|
15499
|
+
const length = await collection.countDocuments(query);
|
|
15500
|
+
const data = paginate20(items, page, limit, length);
|
|
15501
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15502
|
+
logger60.info(`Cache set for key: ${cacheKey}`);
|
|
15503
|
+
}).catch((err) => {
|
|
15504
|
+
logger60.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15505
|
+
});
|
|
15506
|
+
return data;
|
|
15507
|
+
} catch (error) {
|
|
15508
|
+
throw new InternalServerError27("Failed to retrieve person by NRIC.");
|
|
15509
|
+
}
|
|
15510
|
+
}
|
|
15462
15511
|
return {
|
|
15463
15512
|
add,
|
|
15464
15513
|
getAll,
|
|
@@ -15468,11 +15517,12 @@ function usePersonRepo() {
|
|
|
15468
15517
|
createTextIndex,
|
|
15469
15518
|
getPersonByPlateNumber,
|
|
15470
15519
|
getByNRIC,
|
|
15471
|
-
|
|
15520
|
+
createIndexes,
|
|
15472
15521
|
getPersonByPhoneNumber,
|
|
15473
15522
|
getPeopleByUnit,
|
|
15474
15523
|
getCompany,
|
|
15475
|
-
getPeopleByPlateNumber
|
|
15524
|
+
getPeopleByPlateNumber,
|
|
15525
|
+
getPeopleByNRIC
|
|
15476
15526
|
};
|
|
15477
15527
|
}
|
|
15478
15528
|
|
|
@@ -15493,12 +15543,45 @@ import {
|
|
|
15493
15543
|
import { BadRequestError as BadRequestError77, logger as logger61 } from "@7365admin1/node-server-utils";
|
|
15494
15544
|
import Joi43 from "joi";
|
|
15495
15545
|
import { ObjectId as ObjectId45 } from "mongodb";
|
|
15496
|
-
var
|
|
15497
|
-
|
|
15546
|
+
var VehicleType = /* @__PURE__ */ ((VehicleType2) => {
|
|
15547
|
+
VehicleType2["WHITELIST"] = "whitelist";
|
|
15548
|
+
VehicleType2["BLOCKLIST"] = "blocklist";
|
|
15549
|
+
return VehicleType2;
|
|
15550
|
+
})(VehicleType || {});
|
|
15551
|
+
var VehicleCategory = /* @__PURE__ */ ((VehicleCategory2) => {
|
|
15552
|
+
VehicleCategory2["RESIDENT"] = "resident";
|
|
15553
|
+
VehicleCategory2["VISITOR"] = "visitor";
|
|
15554
|
+
return VehicleCategory2;
|
|
15555
|
+
})(VehicleCategory || {});
|
|
15556
|
+
var VehicleStatus = /* @__PURE__ */ ((VehicleStatus2) => {
|
|
15557
|
+
VehicleStatus2["PENDING"] = "pending";
|
|
15558
|
+
VehicleStatus2["ACTIVE"] = "active";
|
|
15559
|
+
VehicleStatus2["INACTIVE"] = "inactive";
|
|
15560
|
+
VehicleStatus2["DELETED"] = "deleted";
|
|
15561
|
+
return VehicleStatus2;
|
|
15562
|
+
})(VehicleStatus || {});
|
|
15563
|
+
var OrgNature = /* @__PURE__ */ ((OrgNature2) => {
|
|
15564
|
+
OrgNature2["PROPERTY_MANAGEMENT_AGENCY"] = "property_management_agency";
|
|
15565
|
+
OrgNature2["SECURITY_AGENCY"] = "security_agency";
|
|
15566
|
+
return OrgNature2;
|
|
15567
|
+
})(OrgNature || {});
|
|
15568
|
+
var ANPRMode = /* @__PURE__ */ ((ANPRMode2) => {
|
|
15569
|
+
ANPRMode2["TRAFFIC_REDLIST"] = "TrafficRedList";
|
|
15570
|
+
ANPRMode2["TRAFFIC_BLACKLIST"] = "TrafficBlackList";
|
|
15571
|
+
return ANPRMode2;
|
|
15572
|
+
})(ANPRMode || {});
|
|
15498
15573
|
var vehicleSchema = Joi43.object({
|
|
15499
|
-
plateNumber: Joi43.
|
|
15500
|
-
|
|
15501
|
-
|
|
15574
|
+
plateNumber: Joi43.alternatives().try(
|
|
15575
|
+
Joi43.array().items(Joi43.string().trim().min(1)).min(1),
|
|
15576
|
+
Joi43.string().trim().min(1)
|
|
15577
|
+
).custom((value) => {
|
|
15578
|
+
if (typeof value === "string") {
|
|
15579
|
+
return value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
15580
|
+
}
|
|
15581
|
+
return value;
|
|
15582
|
+
}).required(),
|
|
15583
|
+
type: Joi43.string().required().valid(...Object.values(VehicleType)),
|
|
15584
|
+
category: Joi43.string().required().valid(...Object.values(VehicleCategory)),
|
|
15502
15585
|
name: Joi43.string().required(),
|
|
15503
15586
|
phoneNumber: Joi43.string().optional().allow("", null),
|
|
15504
15587
|
recNo: Joi43.string().optional().allow("", null),
|
|
@@ -15512,7 +15595,8 @@ var vehicleSchema = Joi43.object({
|
|
|
15512
15595
|
seasonPassType: Joi43.string().optional().allow("", null),
|
|
15513
15596
|
start: Joi43.date().optional().allow("", null),
|
|
15514
15597
|
end: Joi43.date().optional().allow("", null),
|
|
15515
|
-
unitName: Joi43.string().optional().allow("", null)
|
|
15598
|
+
unitName: Joi43.string().optional().allow("", null),
|
|
15599
|
+
status: Joi43.string().optional().valid(...Object.values(VehicleStatus)).allow("")
|
|
15516
15600
|
});
|
|
15517
15601
|
function MVehicle(value) {
|
|
15518
15602
|
const { error } = vehicleSchema.validate(value);
|
|
@@ -15541,6 +15625,11 @@ function MVehicle(value) {
|
|
|
15541
15625
|
throw new BadRequestError77("Invalid building unit ID format.");
|
|
15542
15626
|
}
|
|
15543
15627
|
}
|
|
15628
|
+
const createdAtDate = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
|
|
15629
|
+
const expiredDate = new Date(createdAtDate);
|
|
15630
|
+
expiredDate.setFullYear(expiredDate.getFullYear() + 10);
|
|
15631
|
+
const createdAt = createdAtDate.toISOString();
|
|
15632
|
+
const expiredAt = value.end ?? expiredDate.toISOString();
|
|
15544
15633
|
return {
|
|
15545
15634
|
plateNumber: value.plateNumber ?? "",
|
|
15546
15635
|
type: value.type ?? "",
|
|
@@ -15556,11 +15645,11 @@ function MVehicle(value) {
|
|
|
15556
15645
|
nric: value.nric ?? "",
|
|
15557
15646
|
remarks: value.remarks ?? "",
|
|
15558
15647
|
seasonPassType: value.seasonPassType ?? "",
|
|
15559
|
-
start: value.start ??
|
|
15560
|
-
end: value.end ??
|
|
15561
|
-
status: value.status ?? "active"
|
|
15648
|
+
start: value.start ?? createdAt,
|
|
15649
|
+
end: value.end ?? expiredAt,
|
|
15650
|
+
status: value.status ?? "active" /* ACTIVE */,
|
|
15562
15651
|
unitName: value.unitName ?? "",
|
|
15563
|
-
createdAt
|
|
15652
|
+
createdAt,
|
|
15564
15653
|
updatedAt: value.updatedAt ?? "",
|
|
15565
15654
|
deletedAt: value.deletedAt ?? ""
|
|
15566
15655
|
};
|
|
@@ -15618,7 +15707,7 @@ function useVehicleRepo() {
|
|
|
15618
15707
|
}
|
|
15619
15708
|
const namespace_collection = "vehicles";
|
|
15620
15709
|
const collection = db.collection(namespace_collection);
|
|
15621
|
-
const { delNamespace, setCache, getCache
|
|
15710
|
+
const { delNamespace, setCache, getCache } = useCache28(namespace_collection);
|
|
15622
15711
|
async function createIndex() {
|
|
15623
15712
|
try {
|
|
15624
15713
|
await collection.createIndexes([
|
|
@@ -15630,6 +15719,22 @@ function useVehicleRepo() {
|
|
|
15630
15719
|
throw new InternalServerError28("Failed to create index on vehicle.");
|
|
15631
15720
|
}
|
|
15632
15721
|
}
|
|
15722
|
+
async function createTextIndex() {
|
|
15723
|
+
try {
|
|
15724
|
+
await collection.createIndex({
|
|
15725
|
+
name: "text",
|
|
15726
|
+
plateNumber: "text",
|
|
15727
|
+
level: "text",
|
|
15728
|
+
unitName: "text",
|
|
15729
|
+
phoneNumber: "text",
|
|
15730
|
+
nric: "text"
|
|
15731
|
+
});
|
|
15732
|
+
} catch (error) {
|
|
15733
|
+
throw new InternalServerError28(
|
|
15734
|
+
"Failed to create text index on visitor transaction."
|
|
15735
|
+
);
|
|
15736
|
+
}
|
|
15737
|
+
}
|
|
15633
15738
|
async function add(value, session) {
|
|
15634
15739
|
try {
|
|
15635
15740
|
value = MVehicle(value);
|
|
@@ -15661,73 +15766,178 @@ function useVehicleRepo() {
|
|
|
15661
15766
|
search = "",
|
|
15662
15767
|
sort = {},
|
|
15663
15768
|
type = "",
|
|
15664
|
-
category = ""
|
|
15769
|
+
category = "",
|
|
15770
|
+
status = ""
|
|
15665
15771
|
}) {
|
|
15666
15772
|
page = page > 0 ? page - 1 : 0;
|
|
15667
|
-
const
|
|
15773
|
+
const baseQuery = {
|
|
15774
|
+
...status && { status },
|
|
15775
|
+
...type && { type },
|
|
15776
|
+
...category && { category }
|
|
15777
|
+
};
|
|
15778
|
+
let query = { ...baseQuery };
|
|
15779
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
15668
15780
|
const cacheOptions = {
|
|
15669
|
-
status
|
|
15781
|
+
...status && { status },
|
|
15782
|
+
...type && { type },
|
|
15783
|
+
...category && { category },
|
|
15670
15784
|
page: String(page),
|
|
15671
|
-
limit: String(limit)
|
|
15785
|
+
limit: String(limit),
|
|
15786
|
+
sort: JSON.stringify(sort)
|
|
15672
15787
|
};
|
|
15673
|
-
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
15674
|
-
cacheOptions.sort = JSON.stringify(sort);
|
|
15675
15788
|
if (search) {
|
|
15676
|
-
query.$
|
|
15789
|
+
query.$text = { $search: search };
|
|
15677
15790
|
cacheOptions.search = search;
|
|
15678
15791
|
}
|
|
15679
|
-
if (type) {
|
|
15680
|
-
query.type = type;
|
|
15681
|
-
cacheOptions.type = type;
|
|
15682
|
-
}
|
|
15683
|
-
if (category) {
|
|
15684
|
-
query.category = category;
|
|
15685
|
-
cacheOptions.category = category;
|
|
15686
|
-
}
|
|
15687
15792
|
const cacheKey = makeCacheKey26(namespace_collection, cacheOptions);
|
|
15688
15793
|
const cachedData = await getCache(cacheKey);
|
|
15689
15794
|
if (cachedData) {
|
|
15690
15795
|
logger62.info(`Cache hit for key: ${cacheKey}`);
|
|
15691
15796
|
return cachedData;
|
|
15692
15797
|
}
|
|
15693
|
-
|
|
15694
|
-
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15798
|
+
const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
15799
|
+
const buildGroupedPipeline = (matchQuery) => [
|
|
15800
|
+
{ $match: matchQuery },
|
|
15801
|
+
{
|
|
15802
|
+
$lookup: {
|
|
15803
|
+
from: "building-units",
|
|
15804
|
+
localField: "unit",
|
|
15805
|
+
foreignField: "_id",
|
|
15806
|
+
as: "units",
|
|
15807
|
+
pipeline: [{ $project: { name: 1 } }]
|
|
15808
|
+
}
|
|
15809
|
+
},
|
|
15810
|
+
{ $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
|
|
15811
|
+
{
|
|
15812
|
+
$project: {
|
|
15813
|
+
_id: 1,
|
|
15814
|
+
name: 1,
|
|
15815
|
+
type: 1,
|
|
15816
|
+
category: 1,
|
|
15817
|
+
status: 1,
|
|
15818
|
+
phoneNumber: 1,
|
|
15819
|
+
block: 1,
|
|
15820
|
+
level: 1,
|
|
15821
|
+
unit: "$units.name",
|
|
15822
|
+
recNo: 1,
|
|
15823
|
+
nric: 1,
|
|
15824
|
+
plateNumber: 1
|
|
15825
|
+
}
|
|
15826
|
+
},
|
|
15827
|
+
{
|
|
15828
|
+
$group: {
|
|
15829
|
+
_id: {
|
|
15830
|
+
$cond: [
|
|
15831
|
+
{
|
|
15832
|
+
$and: [{ $ne: ["$nric", null] }, { $ne: ["$nric", ""] }]
|
|
15833
|
+
},
|
|
15834
|
+
{
|
|
15835
|
+
nric: "$nric",
|
|
15836
|
+
block: "$block",
|
|
15837
|
+
level: "$level",
|
|
15838
|
+
unit: "$unit"
|
|
15839
|
+
},
|
|
15840
|
+
"$_id"
|
|
15841
|
+
]
|
|
15842
|
+
},
|
|
15843
|
+
vehicleId: { $first: "$_id" },
|
|
15844
|
+
name: { $first: "$name" },
|
|
15845
|
+
category: { $first: "$category" },
|
|
15846
|
+
phoneNumber: { $first: "$phoneNumber" },
|
|
15847
|
+
block: { $first: "$block" },
|
|
15848
|
+
level: { $first: "$level" },
|
|
15849
|
+
unit: { $first: "$unit" },
|
|
15850
|
+
nric: { $first: "$nric" },
|
|
15851
|
+
plates: {
|
|
15852
|
+
$addToSet: {
|
|
15853
|
+
plateNumber: "$plateNumber",
|
|
15854
|
+
recNo: "$recNo",
|
|
15855
|
+
status: "$status",
|
|
15856
|
+
type: "$type"
|
|
15857
|
+
}
|
|
15706
15858
|
}
|
|
15707
|
-
}
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
15716
|
-
|
|
15717
|
-
|
|
15718
|
-
|
|
15719
|
-
|
|
15720
|
-
|
|
15859
|
+
}
|
|
15860
|
+
},
|
|
15861
|
+
{
|
|
15862
|
+
$project: {
|
|
15863
|
+
_id: "$vehicleId",
|
|
15864
|
+
name: 1,
|
|
15865
|
+
type: 1,
|
|
15866
|
+
category: 1,
|
|
15867
|
+
status: 1,
|
|
15868
|
+
phoneNumber: 1,
|
|
15869
|
+
block: 1,
|
|
15870
|
+
level: 1,
|
|
15871
|
+
unit: 1,
|
|
15872
|
+
nric: 1,
|
|
15873
|
+
plates: {
|
|
15874
|
+
$filter: {
|
|
15875
|
+
input: "$plates",
|
|
15876
|
+
as: "plate",
|
|
15877
|
+
cond: {
|
|
15878
|
+
$and: [
|
|
15879
|
+
{ $ne: ["$$plate.plateNumber", null] },
|
|
15880
|
+
{ $ne: ["$$plate.plateNumber", ""] }
|
|
15881
|
+
]
|
|
15882
|
+
}
|
|
15883
|
+
}
|
|
15721
15884
|
}
|
|
15722
15885
|
}
|
|
15723
|
-
|
|
15724
|
-
|
|
15886
|
+
},
|
|
15887
|
+
{ $sort: sort },
|
|
15888
|
+
{ $skip: page * limit },
|
|
15889
|
+
{ $limit: limit }
|
|
15890
|
+
];
|
|
15891
|
+
const buildGroupedCountPipeline = (matchQuery) => [
|
|
15892
|
+
{ $match: matchQuery },
|
|
15893
|
+
{
|
|
15894
|
+
$group: {
|
|
15895
|
+
_id: {
|
|
15896
|
+
$cond: [
|
|
15897
|
+
{
|
|
15898
|
+
$and: [{ $ne: ["$nric", null] }, { $ne: ["$nric", ""] }]
|
|
15899
|
+
},
|
|
15900
|
+
{
|
|
15901
|
+
nric: "$nric",
|
|
15902
|
+
block: "$block",
|
|
15903
|
+
level: "$level",
|
|
15904
|
+
unit: "$unit"
|
|
15905
|
+
},
|
|
15906
|
+
"$_id"
|
|
15907
|
+
]
|
|
15908
|
+
}
|
|
15909
|
+
}
|
|
15910
|
+
},
|
|
15911
|
+
{ $count: "total" }
|
|
15912
|
+
];
|
|
15913
|
+
try {
|
|
15914
|
+
let items = [];
|
|
15915
|
+
let length = 0;
|
|
15916
|
+
items = await collection.aggregate(buildGroupedPipeline(query)).toArray();
|
|
15917
|
+
const countResult = await collection.aggregate(buildGroupedCountPipeline(query)).toArray();
|
|
15918
|
+
length = countResult[0]?.total || 0;
|
|
15919
|
+
if ((!items || items.length === 0) && search) {
|
|
15920
|
+
const escaped = escapeRegex(search);
|
|
15921
|
+
const regexQuery = {
|
|
15922
|
+
...baseQuery,
|
|
15923
|
+
$or: [
|
|
15924
|
+
{ name: { $regex: escaped, $options: "i" } },
|
|
15925
|
+
{ plateNumber: { $regex: escaped, $options: "i" } },
|
|
15926
|
+
{ company: { $regex: escaped, $options: "i" } },
|
|
15927
|
+
{ level: { $regex: escaped, $options: "i" } },
|
|
15928
|
+
{ unitName: { $regex: escaped, $options: "i" } },
|
|
15929
|
+
{ contact: { $regex: escaped, $options: "i" } },
|
|
15930
|
+
{ nric: { $regex: escaped, $options: "i" } }
|
|
15931
|
+
]
|
|
15932
|
+
};
|
|
15933
|
+
items = await collection.aggregate(buildGroupedPipeline(regexQuery)).toArray();
|
|
15934
|
+
const regexCountResult = await collection.aggregate(buildGroupedCountPipeline(regexQuery)).toArray();
|
|
15935
|
+
length = regexCountResult[0]?.total || 0;
|
|
15936
|
+
}
|
|
15725
15937
|
const data = paginate21(items, page, limit, length);
|
|
15726
|
-
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15727
|
-
logger62.
|
|
15728
|
-
|
|
15729
|
-
logger62.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15730
|
-
});
|
|
15938
|
+
setCache(cacheKey, data, 15 * 60).then(() => logger62.info(`Cache set for key: ${cacheKey}`)).catch(
|
|
15939
|
+
(err) => logger62.error(`Failed to set cache for key: ${cacheKey}`, err)
|
|
15940
|
+
);
|
|
15731
15941
|
return data;
|
|
15732
15942
|
} catch (error) {
|
|
15733
15943
|
throw error;
|
|
@@ -15800,40 +16010,176 @@ function useVehicleRepo() {
|
|
|
15800
16010
|
}
|
|
15801
16011
|
const data = await collection.aggregate([
|
|
15802
16012
|
{ $match: { _id } },
|
|
16013
|
+
{ $limit: 1 },
|
|
16014
|
+
{
|
|
16015
|
+
$project: {
|
|
16016
|
+
_id: 1,
|
|
16017
|
+
nric: 1,
|
|
16018
|
+
block: 1,
|
|
16019
|
+
level: 1,
|
|
16020
|
+
unit: 1
|
|
16021
|
+
}
|
|
16022
|
+
},
|
|
15803
16023
|
{
|
|
15804
16024
|
$lookup: {
|
|
15805
|
-
from:
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
|
|
15809
|
-
|
|
16025
|
+
from: collection.collectionName,
|
|
16026
|
+
let: {
|
|
16027
|
+
vehicleId: "$_id",
|
|
16028
|
+
vehicleNric: "$nric",
|
|
16029
|
+
vehicleBlock: "$block",
|
|
16030
|
+
vehicleLevel: "$level",
|
|
16031
|
+
vehicleUnit: "$unit"
|
|
16032
|
+
},
|
|
16033
|
+
pipeline: [
|
|
16034
|
+
{
|
|
16035
|
+
$match: {
|
|
16036
|
+
$expr: {
|
|
16037
|
+
$cond: [
|
|
16038
|
+
{
|
|
16039
|
+
$and: [
|
|
16040
|
+
{ $ne: ["$$vehicleNric", null] },
|
|
16041
|
+
{ $ne: ["$$vehicleNric", ""] }
|
|
16042
|
+
]
|
|
16043
|
+
},
|
|
16044
|
+
{
|
|
16045
|
+
$and: [
|
|
16046
|
+
{ $eq: ["$nric", "$$vehicleNric"] },
|
|
16047
|
+
{ $eq: ["$block", "$$vehicleBlock"] },
|
|
16048
|
+
{ $eq: ["$level", "$$vehicleLevel"] },
|
|
16049
|
+
{ $eq: ["$unit", "$$vehicleUnit"] }
|
|
16050
|
+
]
|
|
16051
|
+
},
|
|
16052
|
+
{ $eq: ["$_id", "$$vehicleId"] }
|
|
16053
|
+
]
|
|
16054
|
+
}
|
|
16055
|
+
}
|
|
16056
|
+
},
|
|
16057
|
+
{
|
|
16058
|
+
$lookup: {
|
|
16059
|
+
from: "building-units",
|
|
16060
|
+
localField: "unit",
|
|
16061
|
+
foreignField: "_id",
|
|
16062
|
+
as: "units",
|
|
16063
|
+
pipeline: [{ $project: { name: 1 } }]
|
|
16064
|
+
}
|
|
16065
|
+
},
|
|
16066
|
+
{
|
|
16067
|
+
$unwind: { path: "$units", preserveNullAndEmptyArrays: true }
|
|
16068
|
+
},
|
|
16069
|
+
{
|
|
16070
|
+
$project: {
|
|
16071
|
+
_id: 1,
|
|
16072
|
+
name: 1,
|
|
16073
|
+
phoneNumber: 1,
|
|
16074
|
+
plateNumber: 1,
|
|
16075
|
+
nric: 1,
|
|
16076
|
+
block: 1,
|
|
16077
|
+
level: 1,
|
|
16078
|
+
rawUnit: "$unit",
|
|
16079
|
+
unit: "$units.name",
|
|
16080
|
+
type: 1,
|
|
16081
|
+
category: 1,
|
|
16082
|
+
status: 1,
|
|
16083
|
+
recNo: 1,
|
|
16084
|
+
start: 1,
|
|
16085
|
+
end: 1,
|
|
16086
|
+
seasonPassType: 1
|
|
16087
|
+
}
|
|
16088
|
+
},
|
|
16089
|
+
{
|
|
16090
|
+
$group: {
|
|
16091
|
+
_id: {
|
|
16092
|
+
$cond: [
|
|
16093
|
+
{
|
|
16094
|
+
$and: [
|
|
16095
|
+
{ $ne: ["$nric", null] },
|
|
16096
|
+
{ $ne: ["$nric", ""] }
|
|
16097
|
+
]
|
|
16098
|
+
},
|
|
16099
|
+
{
|
|
16100
|
+
nric: "$nric",
|
|
16101
|
+
block: "$block",
|
|
16102
|
+
level: "$level",
|
|
16103
|
+
unit: "$unit"
|
|
16104
|
+
},
|
|
16105
|
+
"$_id"
|
|
16106
|
+
]
|
|
16107
|
+
},
|
|
16108
|
+
vehicleId: { $first: "$_id" },
|
|
16109
|
+
name: { $first: "$name" },
|
|
16110
|
+
category: { $first: "$category" },
|
|
16111
|
+
phoneNumber: { $first: "$phoneNumber" },
|
|
16112
|
+
block: { $first: "$block" },
|
|
16113
|
+
level: { $first: "$level" },
|
|
16114
|
+
unit: { $first: "$unit" },
|
|
16115
|
+
nric: { $first: "$nric" },
|
|
16116
|
+
start: { $first: "$start" },
|
|
16117
|
+
end: { $first: "$end" },
|
|
16118
|
+
seasonPassType: { $first: "$seasonPassType" },
|
|
16119
|
+
plates: {
|
|
16120
|
+
$addToSet: {
|
|
16121
|
+
plateNumber: "$plateNumber",
|
|
16122
|
+
recNo: "$recNo",
|
|
16123
|
+
status: "$status",
|
|
16124
|
+
type: "$type"
|
|
16125
|
+
}
|
|
16126
|
+
}
|
|
16127
|
+
}
|
|
16128
|
+
},
|
|
16129
|
+
{
|
|
16130
|
+
$project: {
|
|
16131
|
+
_id: "$vehicleId",
|
|
16132
|
+
name: 1,
|
|
16133
|
+
phoneNumber: 1,
|
|
16134
|
+
nric: 1,
|
|
16135
|
+
block: 1,
|
|
16136
|
+
level: 1,
|
|
16137
|
+
unit: 1,
|
|
16138
|
+
type: 1,
|
|
16139
|
+
category: 1,
|
|
16140
|
+
status: 1,
|
|
16141
|
+
start: 1,
|
|
16142
|
+
end: 1,
|
|
16143
|
+
seasonPassType: 1,
|
|
16144
|
+
plates: {
|
|
16145
|
+
$filter: {
|
|
16146
|
+
input: "$plates",
|
|
16147
|
+
as: "plate",
|
|
16148
|
+
cond: {
|
|
16149
|
+
$and: [
|
|
16150
|
+
{ $ne: ["$$plate.plateNumber", null] },
|
|
16151
|
+
{ $ne: ["$$plate.plateNumber", ""] }
|
|
16152
|
+
]
|
|
16153
|
+
}
|
|
16154
|
+
}
|
|
16155
|
+
}
|
|
16156
|
+
}
|
|
16157
|
+
}
|
|
16158
|
+
],
|
|
16159
|
+
as: "vehicle"
|
|
15810
16160
|
}
|
|
15811
16161
|
},
|
|
15812
|
-
{ $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
|
|
15813
16162
|
{
|
|
15814
16163
|
$project: {
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
15821
|
-
type: 1,
|
|
15822
|
-
category: 1,
|
|
15823
|
-
status: 1,
|
|
15824
|
-
recNo: 1
|
|
16164
|
+
vehicle: { $arrayElemAt: ["$vehicle", 0] }
|
|
16165
|
+
}
|
|
16166
|
+
},
|
|
16167
|
+
{
|
|
16168
|
+
$replaceRoot: {
|
|
16169
|
+
newRoot: "$vehicle"
|
|
15825
16170
|
}
|
|
15826
16171
|
}
|
|
15827
16172
|
]).toArray();
|
|
15828
16173
|
if (!data || !data.length) {
|
|
15829
16174
|
throw new NotFoundError19("Vehicle not found.");
|
|
15830
16175
|
}
|
|
15831
|
-
|
|
16176
|
+
const result = data[0];
|
|
16177
|
+
setCache(cacheKey, result, 15 * 60).then(() => {
|
|
15832
16178
|
logger62.info(`Cache set for key: ${cacheKey}`);
|
|
15833
16179
|
}).catch((err) => {
|
|
15834
16180
|
logger62.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15835
16181
|
});
|
|
15836
|
-
return
|
|
16182
|
+
return result;
|
|
15837
16183
|
} catch (error) {
|
|
15838
16184
|
throw error;
|
|
15839
16185
|
}
|
|
@@ -15870,7 +16216,7 @@ function useVehicleRepo() {
|
|
|
15870
16216
|
throw error2;
|
|
15871
16217
|
}
|
|
15872
16218
|
}
|
|
15873
|
-
async function updateVehicle(_id, value) {
|
|
16219
|
+
async function updateVehicle(_id, value, session) {
|
|
15874
16220
|
try {
|
|
15875
16221
|
_id = new ObjectId46(_id);
|
|
15876
16222
|
} catch (error) {
|
|
@@ -15881,7 +16227,11 @@ function useVehicleRepo() {
|
|
|
15881
16227
|
...value,
|
|
15882
16228
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15883
16229
|
};
|
|
15884
|
-
const res = await collection.updateOne(
|
|
16230
|
+
const res = await collection.updateOne(
|
|
16231
|
+
{ _id },
|
|
16232
|
+
{ $set: updateValue },
|
|
16233
|
+
{ session }
|
|
16234
|
+
);
|
|
15885
16235
|
if (res.modifiedCount === 0) {
|
|
15886
16236
|
throw new InternalServerError28("Unable to update vehicle.");
|
|
15887
16237
|
}
|
|
@@ -15960,8 +16310,94 @@ function useVehicleRepo() {
|
|
|
15960
16310
|
);
|
|
15961
16311
|
}
|
|
15962
16312
|
}
|
|
16313
|
+
async function getVehiclesByNRIC({
|
|
16314
|
+
page = 1,
|
|
16315
|
+
limit = 10,
|
|
16316
|
+
nric = "",
|
|
16317
|
+
sort = {}
|
|
16318
|
+
}) {
|
|
16319
|
+
page = page > 0 ? page - 1 : 0;
|
|
16320
|
+
if (!nric) {
|
|
16321
|
+
throw new BadRequestError78("NRIC is required.");
|
|
16322
|
+
}
|
|
16323
|
+
const _nric = nric.trim();
|
|
16324
|
+
const query = {
|
|
16325
|
+
deletedAt: "",
|
|
16326
|
+
nric: _nric
|
|
16327
|
+
};
|
|
16328
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
16329
|
+
const cacheOptions = {
|
|
16330
|
+
deletedAt: "",
|
|
16331
|
+
nric: _nric,
|
|
16332
|
+
page: String(page),
|
|
16333
|
+
limit: String(limit),
|
|
16334
|
+
sort: JSON.stringify(sort)
|
|
16335
|
+
};
|
|
16336
|
+
const cacheKey = makeCacheKey26(namespace_collection, cacheOptions);
|
|
16337
|
+
const cachedData = await getCache(cacheKey);
|
|
16338
|
+
if (cachedData) {
|
|
16339
|
+
logger62.info(`Cache hit for key: ${cacheKey}`);
|
|
16340
|
+
return cachedData;
|
|
16341
|
+
}
|
|
16342
|
+
try {
|
|
16343
|
+
const items = await collection.aggregate([
|
|
16344
|
+
{ $match: query },
|
|
16345
|
+
{ $sort: sort },
|
|
16346
|
+
{ $skip: page * limit },
|
|
16347
|
+
{ $limit: limit },
|
|
16348
|
+
{
|
|
16349
|
+
$project: {
|
|
16350
|
+
name: 1,
|
|
16351
|
+
phoneNumber: 1,
|
|
16352
|
+
plateNumber: 1,
|
|
16353
|
+
recNo: 1,
|
|
16354
|
+
nric: 1,
|
|
16355
|
+
status: 1,
|
|
16356
|
+
type: 1
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
16359
|
+
]).toArray();
|
|
16360
|
+
const length = await collection.countDocuments(query);
|
|
16361
|
+
const data = paginate21(items, page, limit, length);
|
|
16362
|
+
setCache(cacheKey, data, 15 * 60).then(() => logger62.info(`Cache set for key: ${cacheKey}`)).catch(
|
|
16363
|
+
(err) => logger62.error(`Failed to set cache for key: ${cacheKey}`, err)
|
|
16364
|
+
);
|
|
16365
|
+
return data;
|
|
16366
|
+
} catch (error) {
|
|
16367
|
+
throw error;
|
|
16368
|
+
}
|
|
16369
|
+
}
|
|
16370
|
+
async function deleteExpiredVehicles(session) {
|
|
16371
|
+
try {
|
|
16372
|
+
const tenYearsAgo = /* @__PURE__ */ new Date();
|
|
16373
|
+
tenYearsAgo.setFullYear(tenYearsAgo.getFullYear() - 10);
|
|
16374
|
+
const res = await collection.updateMany(
|
|
16375
|
+
{
|
|
16376
|
+
status: { $ne: "deleted" },
|
|
16377
|
+
end: { $exists: true, $lte: tenYearsAgo }
|
|
16378
|
+
// check only end
|
|
16379
|
+
},
|
|
16380
|
+
{ $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } },
|
|
16381
|
+
{ session }
|
|
16382
|
+
);
|
|
16383
|
+
if (res.modifiedCount === 0)
|
|
16384
|
+
throw new InternalServerError28("Unable to delete vehicle.");
|
|
16385
|
+
delNamespace().then(() => {
|
|
16386
|
+
logger62.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
16387
|
+
}).catch((err) => {
|
|
16388
|
+
logger62.error(
|
|
16389
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
16390
|
+
err
|
|
16391
|
+
);
|
|
16392
|
+
});
|
|
16393
|
+
return res.modifiedCount;
|
|
16394
|
+
} catch (error) {
|
|
16395
|
+
throw error;
|
|
16396
|
+
}
|
|
16397
|
+
}
|
|
15963
16398
|
return {
|
|
15964
16399
|
createIndex,
|
|
16400
|
+
createTextIndex,
|
|
15965
16401
|
add,
|
|
15966
16402
|
getVehicles,
|
|
15967
16403
|
getSeasonPassTypes,
|
|
@@ -15969,7 +16405,9 @@ function useVehicleRepo() {
|
|
|
15969
16405
|
updateVehicle,
|
|
15970
16406
|
deleteVehicle,
|
|
15971
16407
|
getByPlaceNumber,
|
|
15972
|
-
getVehicleByPlateNumber
|
|
16408
|
+
getVehicleByPlateNumber,
|
|
16409
|
+
getVehiclesByNRIC,
|
|
16410
|
+
deleteExpiredVehicles
|
|
15973
16411
|
};
|
|
15974
16412
|
}
|
|
15975
16413
|
|
|
@@ -16355,7 +16793,7 @@ function useDahuaService() {
|
|
|
16355
16793
|
username: Joi45.string().required(),
|
|
16356
16794
|
password: Joi45.string().required(),
|
|
16357
16795
|
plateNumber: Joi45.string().required(),
|
|
16358
|
-
mode: Joi45.string().valid(
|
|
16796
|
+
mode: Joi45.string().valid(...Object.values(ANPRMode)).required(),
|
|
16359
16797
|
start: Joi45.string().isoDate().optional().allow("", null),
|
|
16360
16798
|
end: Joi45.string().isoDate().optional().allow("", null),
|
|
16361
16799
|
owner: Joi45.string().optional().allow("", null)
|
|
@@ -16427,18 +16865,154 @@ import {
|
|
|
16427
16865
|
logger as logger64,
|
|
16428
16866
|
useAtlas as useAtlas39
|
|
16429
16867
|
} from "@7365admin1/node-server-utils";
|
|
16868
|
+
function formatDahuaDate(date) {
|
|
16869
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
16870
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(
|
|
16871
|
+
date.getDate()
|
|
16872
|
+
)} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(
|
|
16873
|
+
date.getSeconds()
|
|
16874
|
+
)}`;
|
|
16875
|
+
}
|
|
16430
16876
|
function useVehicleService() {
|
|
16431
|
-
const {
|
|
16877
|
+
const {
|
|
16878
|
+
add: _add,
|
|
16879
|
+
deleteVehicle: _deleteVehicle,
|
|
16880
|
+
updateVehicle: _updateVehicle,
|
|
16881
|
+
getVehicleById: _getVehicleById,
|
|
16882
|
+
deleteExpiredVehicles: _deleteExpiredVehicles
|
|
16883
|
+
} = useVehicleRepo();
|
|
16432
16884
|
const {
|
|
16433
16885
|
addPlateNumber: _addPlateNumber,
|
|
16434
16886
|
removePlateNumber: _removePlateNumber
|
|
16435
16887
|
} = useDahuaService();
|
|
16436
16888
|
const { getAllCameraWithPassword: _getAllSiteCameras } = useSiteCameraRepo();
|
|
16889
|
+
const { getById: _getById } = useOrgRepo();
|
|
16437
16890
|
async function add(value) {
|
|
16438
16891
|
const session = useAtlas39.getClient()?.startSession();
|
|
16439
16892
|
if (!session) {
|
|
16440
16893
|
throw new Error("Unable to start session for vehicle service.");
|
|
16441
16894
|
}
|
|
16895
|
+
const org = await _getById(value.org);
|
|
16896
|
+
if (!org)
|
|
16897
|
+
throw new BadRequestError80("Org not found");
|
|
16898
|
+
if (!Object.values(OrgNature).includes(org.nature)) {
|
|
16899
|
+
throw new BadRequestError80(
|
|
16900
|
+
"This organization is not allowed to add vehicles."
|
|
16901
|
+
);
|
|
16902
|
+
}
|
|
16903
|
+
value.status = "active" /* ACTIVE */;
|
|
16904
|
+
const addedBySecurity = org.nature === "security_agency" /* SECURITY_AGENCY */;
|
|
16905
|
+
const addedByPM = org.nature === "property_management_agency" /* PROPERTY_MANAGEMENT_AGENCY */;
|
|
16906
|
+
if (addedBySecurity)
|
|
16907
|
+
value.status = "pending" /* PENDING */;
|
|
16908
|
+
const isBlocklist = value.type === "blocklist" /* BLOCKLIST */;
|
|
16909
|
+
if (isBlocklist)
|
|
16910
|
+
value.status = "inactive" /* INACTIVE */;
|
|
16911
|
+
const _type = value.type;
|
|
16912
|
+
const _mode = isBlocklist ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
|
|
16913
|
+
const hasStart = typeof value.start === "string" ? value.start.trim() !== "" : !!value.start;
|
|
16914
|
+
const hasEnd = typeof value.end === "string" ? value.end.trim() !== "" : !!value.end;
|
|
16915
|
+
let startDate = null;
|
|
16916
|
+
let endDate = null;
|
|
16917
|
+
let start = "";
|
|
16918
|
+
let end = "";
|
|
16919
|
+
let startDateDahua;
|
|
16920
|
+
let endDateDahua;
|
|
16921
|
+
if (addedByPM && !hasStart && !hasEnd) {
|
|
16922
|
+
startDate = /* @__PURE__ */ new Date();
|
|
16923
|
+
endDate = new Date(startDate);
|
|
16924
|
+
endDate.setFullYear(endDate.getFullYear() + 10);
|
|
16925
|
+
start = startDate.toISOString();
|
|
16926
|
+
end = endDate.toISOString();
|
|
16927
|
+
startDateDahua = formatDahuaDate(startDate);
|
|
16928
|
+
endDateDahua = formatDahuaDate(endDate);
|
|
16929
|
+
} else {
|
|
16930
|
+
if (hasStart) {
|
|
16931
|
+
startDate = new Date(value.start);
|
|
16932
|
+
start = startDate.toISOString();
|
|
16933
|
+
startDateDahua = formatDahuaDate(startDate);
|
|
16934
|
+
}
|
|
16935
|
+
if (hasEnd) {
|
|
16936
|
+
endDate = new Date(value.end);
|
|
16937
|
+
end = endDate.toISOString();
|
|
16938
|
+
endDateDahua = formatDahuaDate(endDate);
|
|
16939
|
+
}
|
|
16940
|
+
}
|
|
16941
|
+
const owner = String(value.name ?? "").substring(0, 15);
|
|
16942
|
+
const plateNumbers = value.plateNumber;
|
|
16943
|
+
try {
|
|
16944
|
+
session.startTransaction();
|
|
16945
|
+
let message = "Vehicle plate number needs approval from property management.";
|
|
16946
|
+
let siteCameras = [];
|
|
16947
|
+
if (value.status && value.status !== "pending" /* PENDING */) {
|
|
16948
|
+
let page = 1;
|
|
16949
|
+
let pages = 1;
|
|
16950
|
+
const limit = 20;
|
|
16951
|
+
do {
|
|
16952
|
+
const siteCameraReq = await _getAllSiteCameras({
|
|
16953
|
+
site: value.site,
|
|
16954
|
+
type: "anpr",
|
|
16955
|
+
direction: ["both", "entry"],
|
|
16956
|
+
page,
|
|
16957
|
+
limit
|
|
16958
|
+
});
|
|
16959
|
+
pages = siteCameraReq.pages || 1;
|
|
16960
|
+
siteCameras.push(...siteCameraReq.items);
|
|
16961
|
+
page++;
|
|
16962
|
+
} while (page <= pages);
|
|
16963
|
+
if (!siteCameras.length) {
|
|
16964
|
+
throw new BadRequestError80("No site cameras found.");
|
|
16965
|
+
}
|
|
16966
|
+
}
|
|
16967
|
+
for (const plateNumber of plateNumbers) {
|
|
16968
|
+
const vehicleValue = {
|
|
16969
|
+
...value,
|
|
16970
|
+
plateNumber,
|
|
16971
|
+
start,
|
|
16972
|
+
end
|
|
16973
|
+
};
|
|
16974
|
+
if (vehicleValue.status && vehicleValue.status !== "pending" /* PENDING */) {
|
|
16975
|
+
for (const camera of siteCameras) {
|
|
16976
|
+
const { host, username, password } = camera;
|
|
16977
|
+
const dahuaPayload = {
|
|
16978
|
+
host,
|
|
16979
|
+
username,
|
|
16980
|
+
password,
|
|
16981
|
+
plateNumber,
|
|
16982
|
+
mode: _mode,
|
|
16983
|
+
owner,
|
|
16984
|
+
...startDateDahua ? { start: startDateDahua } : {},
|
|
16985
|
+
...endDateDahua ? { end: endDateDahua } : {}
|
|
16986
|
+
};
|
|
16987
|
+
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
16988
|
+
if (dahuaResponse?.statusCode !== 200) {
|
|
16989
|
+
throw new BadRequestError80(
|
|
16990
|
+
`Failed to add plate number to ANPR ${_type}`
|
|
16991
|
+
);
|
|
16992
|
+
}
|
|
16993
|
+
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
16994
|
+
vehicleValue.recNo = responseData.split("=")[1]?.trim();
|
|
16995
|
+
}
|
|
16996
|
+
message = `Vehicle plate number added to ${_type} successfully.`;
|
|
16997
|
+
}
|
|
16998
|
+
await _add(vehicleValue, session);
|
|
16999
|
+
}
|
|
17000
|
+
await session.commitTransaction();
|
|
17001
|
+
return message;
|
|
17002
|
+
} catch (error) {
|
|
17003
|
+
logger64.error("Error in vehicle service add:", error);
|
|
17004
|
+
await session.abortTransaction();
|
|
17005
|
+
throw error;
|
|
17006
|
+
} finally {
|
|
17007
|
+
session.endSession();
|
|
17008
|
+
}
|
|
17009
|
+
}
|
|
17010
|
+
async function deleteVehicle(_id, recno, site, type, bypass = false) {
|
|
17011
|
+
const session = useAtlas39.getClient()?.startSession();
|
|
17012
|
+
if (!session) {
|
|
17013
|
+
throw new Error("Unable to start session for vehicle service.");
|
|
17014
|
+
}
|
|
17015
|
+
const _mode = type !== "whitelist" /* WHITELIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
|
|
16442
17016
|
try {
|
|
16443
17017
|
session.startTransaction();
|
|
16444
17018
|
const siteCameras = [];
|
|
@@ -16447,9 +17021,8 @@ function useVehicleService() {
|
|
|
16447
17021
|
const limit = 20;
|
|
16448
17022
|
do {
|
|
16449
17023
|
const siteCameraReq = await _getAllSiteCameras({
|
|
16450
|
-
site
|
|
17024
|
+
site,
|
|
16451
17025
|
type: "anpr",
|
|
16452
|
-
direction: ["both", "entry"],
|
|
16453
17026
|
page,
|
|
16454
17027
|
limit
|
|
16455
17028
|
});
|
|
@@ -16461,55 +17034,82 @@ function useVehicleService() {
|
|
|
16461
17034
|
throw new BadRequestError80("No site cameras found.");
|
|
16462
17035
|
}
|
|
16463
17036
|
for (const camera of siteCameras) {
|
|
16464
|
-
const
|
|
17037
|
+
const host = camera.host;
|
|
17038
|
+
const username = camera.username;
|
|
17039
|
+
const password = camera.password;
|
|
16465
17040
|
const dahuaPayload = {
|
|
16466
17041
|
host,
|
|
16467
17042
|
username,
|
|
16468
17043
|
password,
|
|
16469
|
-
|
|
16470
|
-
mode:
|
|
16471
|
-
start: value.start ? new Date(value.start).toISOString() : "",
|
|
16472
|
-
end: value.end ? new Date(value.end).toISOString() : "",
|
|
16473
|
-
owner: value.name
|
|
17044
|
+
recno,
|
|
17045
|
+
mode: _mode
|
|
16474
17046
|
};
|
|
16475
|
-
const dahuaResponse = await
|
|
16476
|
-
if (dahuaResponse?.statusCode !== 200) {
|
|
16477
|
-
throw new BadRequestError80(
|
|
17047
|
+
const dahuaResponse = await _removePlateNumber(dahuaPayload);
|
|
17048
|
+
if (!bypass && dahuaResponse?.statusCode !== 200) {
|
|
17049
|
+
throw new BadRequestError80(
|
|
17050
|
+
`Failed to remove plate number to ANPR from ${type}`
|
|
17051
|
+
);
|
|
16478
17052
|
}
|
|
16479
|
-
const responseData = dahuaResponse?.data.toString("utf-8");
|
|
16480
|
-
value.recNo = responseData.split("=")[1]?.trim();
|
|
16481
|
-
const formattedValue = {
|
|
16482
|
-
...value,
|
|
16483
|
-
start: value.start ? new Date(value.start).toISOString() : "",
|
|
16484
|
-
end: value.end ? new Date(value.end).toISOString() : ""
|
|
16485
|
-
};
|
|
16486
|
-
await _add(formattedValue, session);
|
|
16487
17053
|
}
|
|
17054
|
+
await _deleteVehicle(_id, session);
|
|
16488
17055
|
await session.commitTransaction();
|
|
16489
|
-
return
|
|
17056
|
+
return `Vehicle plate number deleted from ${type} record successfully.`;
|
|
16490
17057
|
} catch (error) {
|
|
16491
|
-
logger64.error("Error in vehicle service add:", error);
|
|
16492
17058
|
await session.abortTransaction();
|
|
16493
|
-
|
|
17059
|
+
if (error instanceof AppError14)
|
|
17060
|
+
throw error;
|
|
17061
|
+
throw new InternalServerError29("Failed to delete vehicle");
|
|
16494
17062
|
} finally {
|
|
16495
17063
|
session.endSession();
|
|
16496
17064
|
}
|
|
16497
17065
|
}
|
|
16498
|
-
async function
|
|
17066
|
+
async function approveVehicleById(id, orgId, siteId) {
|
|
16499
17067
|
const session = useAtlas39.getClient()?.startSession();
|
|
16500
17068
|
if (!session) {
|
|
16501
17069
|
throw new Error("Unable to start session for vehicle service.");
|
|
16502
17070
|
}
|
|
17071
|
+
const org = await _getById(orgId);
|
|
17072
|
+
if (!org)
|
|
17073
|
+
throw new BadRequestError80("Org not found");
|
|
17074
|
+
const allowedNatures2 = ["property_management_agency"];
|
|
17075
|
+
if (!allowedNatures2.includes(org.nature)) {
|
|
17076
|
+
throw new BadRequestError80(
|
|
17077
|
+
"Only property management can approve vehicles."
|
|
17078
|
+
);
|
|
17079
|
+
}
|
|
17080
|
+
const vehicle = await _getVehicleById(id);
|
|
17081
|
+
if (!vehicle) {
|
|
17082
|
+
throw new BadRequestError80("Vehicle not found");
|
|
17083
|
+
}
|
|
17084
|
+
const owner = vehicle.name.substring(0, 15);
|
|
17085
|
+
const hasStart = typeof vehicle.start === "string" ? vehicle.start.trim() !== "" : !!vehicle.start;
|
|
17086
|
+
const hasEnd = typeof vehicle.end === "string" ? vehicle.end.trim() !== "" : !!vehicle.end;
|
|
17087
|
+
let startDate = null;
|
|
17088
|
+
let endDate = null;
|
|
17089
|
+
if (!hasStart && !hasEnd) {
|
|
17090
|
+
startDate = /* @__PURE__ */ new Date();
|
|
17091
|
+
endDate = new Date(startDate);
|
|
17092
|
+
endDate.setFullYear(endDate.getFullYear() + 10);
|
|
17093
|
+
} else {
|
|
17094
|
+
startDate = hasStart ? new Date(vehicle.start) : null;
|
|
17095
|
+
endDate = hasEnd ? new Date(vehicle.end) : null;
|
|
17096
|
+
}
|
|
17097
|
+
const startIso = startDate ? startDate.toISOString() : "";
|
|
17098
|
+
const endIso = endDate ? endDate.toISOString() : "";
|
|
17099
|
+
const startDahua = startDate ? formatDahuaDate(startDate) : "";
|
|
17100
|
+
const endDahua = endDate ? formatDahuaDate(endDate) : "";
|
|
16503
17101
|
try {
|
|
16504
17102
|
session.startTransaction();
|
|
16505
17103
|
const siteCameras = [];
|
|
16506
17104
|
let page = 1;
|
|
16507
17105
|
let pages = 1;
|
|
16508
17106
|
const limit = 20;
|
|
17107
|
+
let value = {};
|
|
16509
17108
|
do {
|
|
16510
17109
|
const siteCameraReq = await _getAllSiteCameras({
|
|
16511
|
-
site,
|
|
17110
|
+
site: siteId,
|
|
16512
17111
|
type: "anpr",
|
|
17112
|
+
direction: ["both", "entry"],
|
|
16513
17113
|
page,
|
|
16514
17114
|
limit
|
|
16515
17115
|
});
|
|
@@ -16521,24 +17121,51 @@ function useVehicleService() {
|
|
|
16521
17121
|
throw new BadRequestError80("No site cameras found.");
|
|
16522
17122
|
}
|
|
16523
17123
|
for (const camera of siteCameras) {
|
|
16524
|
-
const host = camera
|
|
16525
|
-
const username = camera.username;
|
|
16526
|
-
const password = camera.password;
|
|
17124
|
+
const { host, username, password } = camera;
|
|
16527
17125
|
const dahuaPayload = {
|
|
16528
17126
|
host,
|
|
16529
17127
|
username,
|
|
16530
17128
|
password,
|
|
16531
|
-
|
|
16532
|
-
mode: "TrafficRedList"
|
|
17129
|
+
plateNumber: vehicle.plateNumber,
|
|
17130
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
17131
|
+
start: startDahua,
|
|
17132
|
+
end: endDahua,
|
|
17133
|
+
owner
|
|
16533
17134
|
};
|
|
16534
|
-
const dahuaResponse = await
|
|
16535
|
-
if (
|
|
16536
|
-
throw new BadRequestError80("Failed to
|
|
17135
|
+
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
17136
|
+
if (dahuaResponse?.statusCode !== 200) {
|
|
17137
|
+
throw new BadRequestError80("Failed to add plate number to ANPR");
|
|
16537
17138
|
}
|
|
17139
|
+
const responseData = dahuaResponse?.data.toString("utf-8");
|
|
17140
|
+
value.recNo = responseData.split("=")[1]?.trim();
|
|
16538
17141
|
}
|
|
16539
|
-
|
|
17142
|
+
value.status = "active";
|
|
17143
|
+
const formattedValue = {
|
|
17144
|
+
...value,
|
|
17145
|
+
start: startIso,
|
|
17146
|
+
end: endIso
|
|
17147
|
+
};
|
|
17148
|
+
await _updateVehicle(id, formattedValue, session);
|
|
16540
17149
|
await session.commitTransaction();
|
|
16541
|
-
return "Vehicle plate number
|
|
17150
|
+
return "Vehicle plate number approved and added successfully.";
|
|
17151
|
+
} catch (error) {
|
|
17152
|
+
logger64.error("Error in vehicle service add:", error);
|
|
17153
|
+
await session.abortTransaction();
|
|
17154
|
+
throw error;
|
|
17155
|
+
} finally {
|
|
17156
|
+
session.endSession();
|
|
17157
|
+
}
|
|
17158
|
+
}
|
|
17159
|
+
async function processDeletingExpiredVehicles() {
|
|
17160
|
+
const session = useAtlas39.getClient()?.startSession();
|
|
17161
|
+
if (!session) {
|
|
17162
|
+
throw new Error("Unable to start session for vehicle service.");
|
|
17163
|
+
}
|
|
17164
|
+
try {
|
|
17165
|
+
session.startTransaction();
|
|
17166
|
+
await _deleteExpiredVehicles();
|
|
17167
|
+
await session.commitTransaction();
|
|
17168
|
+
return `Expired Vehicle plate numbers deleted successfully.`;
|
|
16542
17169
|
} catch (error) {
|
|
16543
17170
|
await session.abortTransaction();
|
|
16544
17171
|
if (error instanceof AppError14)
|
|
@@ -16550,7 +17177,9 @@ function useVehicleService() {
|
|
|
16550
17177
|
}
|
|
16551
17178
|
return {
|
|
16552
17179
|
add,
|
|
16553
|
-
deleteVehicle
|
|
17180
|
+
deleteVehicle,
|
|
17181
|
+
approveVehicleById,
|
|
17182
|
+
processDeletingExpiredVehicles
|
|
16554
17183
|
};
|
|
16555
17184
|
}
|
|
16556
17185
|
|
|
@@ -16558,23 +17187,28 @@ function useVehicleService() {
|
|
|
16558
17187
|
import { BadRequestError as BadRequestError81, logger as logger65 } from "@7365admin1/node-server-utils";
|
|
16559
17188
|
import Joi46 from "joi";
|
|
16560
17189
|
function useVehicleController() {
|
|
16561
|
-
const {
|
|
17190
|
+
const {
|
|
17191
|
+
add: _add,
|
|
17192
|
+
deleteVehicle: _deleteVehicle,
|
|
17193
|
+
approveVehicleById: _approveVehicleById
|
|
17194
|
+
} = useVehicleService();
|
|
16562
17195
|
const {
|
|
16563
17196
|
getSeasonPassTypes: _getSeasonPassTypes,
|
|
16564
17197
|
getVehicles: _getVehicles,
|
|
16565
17198
|
getVehicleById: _getVehicleById,
|
|
16566
|
-
updateVehicle: _updateVehicle
|
|
17199
|
+
updateVehicle: _updateVehicle,
|
|
17200
|
+
getVehiclesByNRIC: _getVehiclesByNRIC
|
|
16567
17201
|
} = useVehicleRepo();
|
|
16568
17202
|
async function add(req, res, next) {
|
|
16569
17203
|
const payload = req.body;
|
|
16570
|
-
const { error } = vehicleSchema.validate(payload);
|
|
17204
|
+
const { error, value } = vehicleSchema.validate(payload);
|
|
16571
17205
|
if (error) {
|
|
16572
17206
|
logger65.log({ level: "error", message: error.message });
|
|
16573
17207
|
next(new BadRequestError81(error.message));
|
|
16574
17208
|
return;
|
|
16575
17209
|
}
|
|
16576
17210
|
try {
|
|
16577
|
-
const data = await _add(
|
|
17211
|
+
const data = await _add(value);
|
|
16578
17212
|
res.status(201).json({
|
|
16579
17213
|
message: "Vehicle added successfully.",
|
|
16580
17214
|
data
|
|
@@ -16594,8 +17228,9 @@ function useVehicleController() {
|
|
|
16594
17228
|
limit: Joi46.number().integer().min(1).max(100).allow("", null).default(10),
|
|
16595
17229
|
sort: Joi46.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
|
|
16596
17230
|
order: Joi46.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
|
|
16597
|
-
type: Joi46.string().optional().allow(""
|
|
16598
|
-
category: Joi46.string().optional().allow("",
|
|
17231
|
+
type: Joi46.string().optional().valid(...Object.values(VehicleType)).allow(null, ""),
|
|
17232
|
+
category: Joi46.string().optional().valid(...Object.values(VehicleCategory)).allow(null, ""),
|
|
17233
|
+
status: Joi46.string().optional().valid(...Object.values(VehicleStatus)).allow(null, "")
|
|
16599
17234
|
});
|
|
16600
17235
|
const query = { ...req.query };
|
|
16601
17236
|
const { error } = validation.validate(query);
|
|
@@ -16609,6 +17244,7 @@ function useVehicleController() {
|
|
|
16609
17244
|
const limit = parseInt(req.query.limit ?? "10");
|
|
16610
17245
|
const type = req.query.type ?? "";
|
|
16611
17246
|
const category = req.query.category ?? "";
|
|
17247
|
+
const status = req.query.status ?? "";
|
|
16612
17248
|
const sortObj = {};
|
|
16613
17249
|
const sortFields = String(req.query.sort).split(",");
|
|
16614
17250
|
const sortOrders = String(req.query.order).split(",");
|
|
@@ -16625,7 +17261,8 @@ function useVehicleController() {
|
|
|
16625
17261
|
limit,
|
|
16626
17262
|
sort: sortObj,
|
|
16627
17263
|
type,
|
|
16628
|
-
category
|
|
17264
|
+
category,
|
|
17265
|
+
status
|
|
16629
17266
|
});
|
|
16630
17267
|
res.json(data);
|
|
16631
17268
|
return;
|
|
@@ -16681,7 +17318,8 @@ function useVehicleController() {
|
|
|
16681
17318
|
block: Joi46.number().integer().optional().allow(0, null),
|
|
16682
17319
|
level: Joi46.string().optional().allow("", null),
|
|
16683
17320
|
unit: Joi46.string().optional().allow("", null),
|
|
16684
|
-
plateNumber: Joi46.string().optional().allow("", null)
|
|
17321
|
+
plateNumber: Joi46.string().optional().allow("", null),
|
|
17322
|
+
nric: Joi46.string().optional().allow("", null)
|
|
16685
17323
|
});
|
|
16686
17324
|
const _id = req.params.id;
|
|
16687
17325
|
const payload = { ...req.body };
|
|
@@ -16702,23 +17340,23 @@ function useVehicleController() {
|
|
|
16702
17340
|
}
|
|
16703
17341
|
}
|
|
16704
17342
|
async function deleteVehicle(req, res, next) {
|
|
16705
|
-
const
|
|
16706
|
-
const _id = req.params.id ?? "";
|
|
16707
|
-
const recno = req.params.recno ?? "";
|
|
16708
|
-
const bypass = req.query.bypass === "true";
|
|
17343
|
+
const _id = req.params.id;
|
|
16709
17344
|
const deleteVehicleSchema = Joi46.object({
|
|
16710
|
-
_id: Joi46.string().hex().required(),
|
|
17345
|
+
_id: Joi46.string().hex().length(24).required(),
|
|
16711
17346
|
recno: Joi46.string().required(),
|
|
16712
|
-
|
|
17347
|
+
site: Joi46.string().hex().length(24).required(),
|
|
17348
|
+
type: Joi46.string().valid("whitelist", "blocklist").required(),
|
|
17349
|
+
bypass: Joi46.boolean().optional().default(true)
|
|
16713
17350
|
});
|
|
16714
|
-
const { error } = deleteVehicleSchema.validate({ _id,
|
|
17351
|
+
const { error, value } = deleteVehicleSchema.validate({ _id, ...req.body });
|
|
16715
17352
|
if (error) {
|
|
16716
17353
|
logger65.log({ level: "error", message: error.message });
|
|
16717
17354
|
next(new BadRequestError81(error.message));
|
|
16718
17355
|
return;
|
|
16719
17356
|
}
|
|
17357
|
+
const { recno, site, type, bypass } = value;
|
|
16720
17358
|
try {
|
|
16721
|
-
const data = await _deleteVehicle(_id, recno, site, bypass);
|
|
17359
|
+
const data = await _deleteVehicle(_id, recno, site, type, bypass);
|
|
16722
17360
|
res.json({
|
|
16723
17361
|
message: "Vehicle deleted successfully.",
|
|
16724
17362
|
data
|
|
@@ -16729,13 +17367,83 @@ function useVehicleController() {
|
|
|
16729
17367
|
return;
|
|
16730
17368
|
}
|
|
16731
17369
|
}
|
|
17370
|
+
async function approveVehicleById(req, res, next) {
|
|
17371
|
+
const validation = Joi46.object({
|
|
17372
|
+
_id: Joi46.string().hex().length(24).required(),
|
|
17373
|
+
org: Joi46.string().hex().length(24).required(),
|
|
17374
|
+
site: Joi46.string().hex().length(24).required()
|
|
17375
|
+
});
|
|
17376
|
+
const _id = req.params.id;
|
|
17377
|
+
const payload = { ...req.body };
|
|
17378
|
+
const { error, value } = validation.validate({ _id, ...payload });
|
|
17379
|
+
if (error) {
|
|
17380
|
+
logger65.log({ level: "error", message: error.message });
|
|
17381
|
+
next(new BadRequestError81(error.message));
|
|
17382
|
+
return;
|
|
17383
|
+
}
|
|
17384
|
+
try {
|
|
17385
|
+
await _approveVehicleById(value._id, value.org, value.site);
|
|
17386
|
+
res.json({ message: "Successfully approved and updated vehicle." });
|
|
17387
|
+
return;
|
|
17388
|
+
} catch (error2) {
|
|
17389
|
+
logger65.log({ level: "error", message: error2.message });
|
|
17390
|
+
next(error2);
|
|
17391
|
+
return;
|
|
17392
|
+
}
|
|
17393
|
+
}
|
|
17394
|
+
async function getVehiclesByNRIC(req, res, next) {
|
|
17395
|
+
const allowedFields = ["start", "end"];
|
|
17396
|
+
const allowedOrder = ["asc", "desc"];
|
|
17397
|
+
const validation = Joi46.object({
|
|
17398
|
+
nric: Joi46.string().optional().allow("", null),
|
|
17399
|
+
page: Joi46.number().integer().min(1).allow("", null).default(1),
|
|
17400
|
+
limit: Joi46.number().integer().min(1).max(100).allow("", null).default(10),
|
|
17401
|
+
sort: Joi46.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
|
|
17402
|
+
order: Joi46.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder)
|
|
17403
|
+
});
|
|
17404
|
+
const query = { ...req.query };
|
|
17405
|
+
const { error } = validation.validate(query);
|
|
17406
|
+
if (error) {
|
|
17407
|
+
logger65.log({ level: "error", message: error.message });
|
|
17408
|
+
next(new BadRequestError81(error.message));
|
|
17409
|
+
return;
|
|
17410
|
+
}
|
|
17411
|
+
const nric = req.query.nric ?? "";
|
|
17412
|
+
const page = parseInt(req.query.page ?? "1");
|
|
17413
|
+
const limit = parseInt(req.query.limit ?? "10");
|
|
17414
|
+
const sortObj = {};
|
|
17415
|
+
const sortFields = String(req.query.sort).split(",");
|
|
17416
|
+
const sortOrders = String(req.query.order).split(",");
|
|
17417
|
+
sortFields.forEach((field, index) => {
|
|
17418
|
+
if (allowedFields.includes(field)) {
|
|
17419
|
+
const order = sortOrders[index] === "asc" ? 1 : -1;
|
|
17420
|
+
sortObj[field] = order;
|
|
17421
|
+
}
|
|
17422
|
+
});
|
|
17423
|
+
try {
|
|
17424
|
+
const data = await _getVehiclesByNRIC({
|
|
17425
|
+
nric,
|
|
17426
|
+
page,
|
|
17427
|
+
limit,
|
|
17428
|
+
sort: sortObj
|
|
17429
|
+
});
|
|
17430
|
+
res.json(data);
|
|
17431
|
+
return;
|
|
17432
|
+
} catch (error2) {
|
|
17433
|
+
logger65.log({ level: "error", message: error2.message });
|
|
17434
|
+
next(error2);
|
|
17435
|
+
return;
|
|
17436
|
+
}
|
|
17437
|
+
}
|
|
16732
17438
|
return {
|
|
16733
17439
|
add,
|
|
16734
17440
|
getVehicles,
|
|
16735
17441
|
getSeasonPassTypes,
|
|
16736
17442
|
getVehicleById,
|
|
16737
17443
|
updateVehicle,
|
|
16738
|
-
deleteVehicle
|
|
17444
|
+
deleteVehicle,
|
|
17445
|
+
approveVehicleById,
|
|
17446
|
+
getVehiclesByNRIC
|
|
16739
17447
|
};
|
|
16740
17448
|
}
|
|
16741
17449
|
|
|
@@ -16837,7 +17545,7 @@ function useSiteCameraController() {
|
|
|
16837
17545
|
}
|
|
16838
17546
|
async function getAll(req, res, next) {
|
|
16839
17547
|
const query = req.query;
|
|
16840
|
-
const
|
|
17548
|
+
const allowedTypes = ["ip", "exit", "entry", "both"];
|
|
16841
17549
|
const validation = Joi47.object({
|
|
16842
17550
|
type: Joi47.string().optional(),
|
|
16843
17551
|
site: Joi47.string().optional(),
|
|
@@ -19363,25 +20071,15 @@ function usePersonService() {
|
|
|
19363
20071
|
}
|
|
19364
20072
|
async function plateValidity(value) {
|
|
19365
20073
|
const now = /* @__PURE__ */ new Date();
|
|
19366
|
-
if (
|
|
19367
|
-
|
|
19368
|
-
|
|
19369
|
-
if (value.type === "resident") {
|
|
20074
|
+
if (["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
|
|
20075
|
+
value?.type
|
|
20076
|
+
)) {
|
|
19370
20077
|
const end = new Date(now);
|
|
19371
20078
|
end.setFullYear(end.getFullYear() + 10);
|
|
19372
|
-
return {
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
if (!unit) {
|
|
19377
|
-
throw new BadRequestError97("Building unit not found for tenant.");
|
|
19378
|
-
}
|
|
19379
|
-
if (unit.leaseStart && unit.leaseEnd) {
|
|
19380
|
-
return {
|
|
19381
|
-
start: new Date(unit.leaseStart).toISOString(),
|
|
19382
|
-
end: new Date(unit.leaseEnd).toISOString()
|
|
19383
|
-
};
|
|
19384
|
-
}
|
|
20079
|
+
return {
|
|
20080
|
+
start: now.toISOString(),
|
|
20081
|
+
end: end.toISOString()
|
|
20082
|
+
};
|
|
19385
20083
|
}
|
|
19386
20084
|
return { start: "", end: "" };
|
|
19387
20085
|
}
|
|
@@ -19400,7 +20098,8 @@ function usePersonController() {
|
|
|
19400
20098
|
getPersonByPhoneNumber: _getPersonByPhoneNumber,
|
|
19401
20099
|
getPeopleByUnit: _getPeopleByUnit,
|
|
19402
20100
|
getCompany: _getCompany,
|
|
19403
|
-
getPeopleByPlateNumber: _getPeopleByPlateNumber
|
|
20101
|
+
getPeopleByPlateNumber: _getPeopleByPlateNumber,
|
|
20102
|
+
getPeopleByNRIC: _getPeopleByNRIC
|
|
19404
20103
|
} = usePersonRepo();
|
|
19405
20104
|
const { add: _add, updateById: _updateById } = usePersonService();
|
|
19406
20105
|
async function add(req, res, next) {
|
|
@@ -19635,6 +20334,53 @@ function usePersonController() {
|
|
|
19635
20334
|
return;
|
|
19636
20335
|
}
|
|
19637
20336
|
}
|
|
20337
|
+
async function getPeopleByNRIC(req, res, next) {
|
|
20338
|
+
const allowedFields = ["start", "end"];
|
|
20339
|
+
const allowedOrder = ["asc", "desc"];
|
|
20340
|
+
const validation = Joi58.object({
|
|
20341
|
+
nric: Joi58.string().optional().allow("", null),
|
|
20342
|
+
page: Joi58.number().integer().min(1).allow("", null).default(1),
|
|
20343
|
+
limit: Joi58.number().integer().min(1).max(100).allow("", null).default(10),
|
|
20344
|
+
sort: Joi58.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
|
|
20345
|
+
order: Joi58.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
|
|
20346
|
+
site: Joi58.string().hex().length(24).required()
|
|
20347
|
+
});
|
|
20348
|
+
const query = { ...req.query };
|
|
20349
|
+
const { error } = validation.validate(query);
|
|
20350
|
+
if (error) {
|
|
20351
|
+
logger81.log({ level: "error", message: error.message });
|
|
20352
|
+
next(new BadRequestError98(error.message));
|
|
20353
|
+
return;
|
|
20354
|
+
}
|
|
20355
|
+
const nric = req.query.nric ?? "";
|
|
20356
|
+
const page = parseInt(req.query.page ?? "1");
|
|
20357
|
+
const limit = parseInt(req.query.limit ?? "10");
|
|
20358
|
+
const site = req.query.site ?? "";
|
|
20359
|
+
const sortObj = {};
|
|
20360
|
+
const sortFields = String(req.query.sort).split(",");
|
|
20361
|
+
const sortOrders = String(req.query.order).split(",");
|
|
20362
|
+
sortFields.forEach((field, index) => {
|
|
20363
|
+
if (allowedFields.includes(field)) {
|
|
20364
|
+
const order = sortOrders[index] === "asc" ? 1 : -1;
|
|
20365
|
+
sortObj[field] = order;
|
|
20366
|
+
}
|
|
20367
|
+
});
|
|
20368
|
+
try {
|
|
20369
|
+
const data = await _getPeopleByNRIC({
|
|
20370
|
+
nric,
|
|
20371
|
+
page,
|
|
20372
|
+
limit,
|
|
20373
|
+
sort: sortObj,
|
|
20374
|
+
site
|
|
20375
|
+
});
|
|
20376
|
+
res.json(data);
|
|
20377
|
+
return;
|
|
20378
|
+
} catch (error2) {
|
|
20379
|
+
logger81.log({ level: "error", message: error2.message });
|
|
20380
|
+
next(error2);
|
|
20381
|
+
return;
|
|
20382
|
+
}
|
|
20383
|
+
}
|
|
19638
20384
|
return {
|
|
19639
20385
|
add,
|
|
19640
20386
|
getAll,
|
|
@@ -19644,7 +20390,8 @@ function usePersonController() {
|
|
|
19644
20390
|
getPersonByPhoneNumber,
|
|
19645
20391
|
getPeopleByUnit,
|
|
19646
20392
|
getCompany,
|
|
19647
|
-
getPeopleByPlateNumber
|
|
20393
|
+
getPeopleByPlateNumber,
|
|
20394
|
+
getPeopleByNRIC
|
|
19648
20395
|
};
|
|
19649
20396
|
}
|
|
19650
20397
|
|
|
@@ -24322,20 +25069,19 @@ function useDocumentManagementController() {
|
|
|
24322
25069
|
// src/models/bulletin-board.model.ts
|
|
24323
25070
|
import Joi75 from "joi";
|
|
24324
25071
|
import { ObjectId as ObjectId71 } from "mongodb";
|
|
25072
|
+
var BULLETIN_RECIPIENTS = [
|
|
25073
|
+
"resident",
|
|
25074
|
+
"security_agency",
|
|
25075
|
+
"cleaning_services",
|
|
25076
|
+
"mechanical_electrical",
|
|
25077
|
+
"property_management_agency"
|
|
25078
|
+
];
|
|
25079
|
+
var STATUS_VALUES = ["active", "expired", "deleted"];
|
|
24325
25080
|
var schemaBulletinBoard = Joi75.object({
|
|
24326
25081
|
_id: Joi75.string().hex().optional().allow("", null),
|
|
24327
25082
|
site: Joi75.string().hex().optional().allow("", null),
|
|
24328
25083
|
orgId: Joi75.string().hex().optional().allow("", null),
|
|
24329
|
-
recipients: Joi75.array().items(
|
|
24330
|
-
Joi75.string().valid(
|
|
24331
|
-
"admin",
|
|
24332
|
-
"organization",
|
|
24333
|
-
"site",
|
|
24334
|
-
"service-provider",
|
|
24335
|
-
"service-provider-member",
|
|
24336
|
-
"resident"
|
|
24337
|
-
)
|
|
24338
|
-
).optional(),
|
|
25084
|
+
recipients: Joi75.array().items(Joi75.string().valid(...BULLETIN_RECIPIENTS)).unique().optional(),
|
|
24339
25085
|
title: Joi75.string().optional().allow("", null),
|
|
24340
25086
|
content: Joi75.string().optional().allow("", null),
|
|
24341
25087
|
file: Joi75.array().items(
|
|
@@ -24348,23 +25094,14 @@ var schemaBulletinBoard = Joi75.object({
|
|
|
24348
25094
|
noExpiration: Joi75.boolean().optional(),
|
|
24349
25095
|
startDate: Joi75.date().optional().allow("", null),
|
|
24350
25096
|
endDate: Joi75.date().optional().allow("", null),
|
|
24351
|
-
status: Joi75.string().
|
|
25097
|
+
status: Joi75.string().valid(...STATUS_VALUES).optional(),
|
|
24352
25098
|
createdAt: Joi75.date().optional().allow(null),
|
|
24353
25099
|
updatedAt: Joi75.date().optional().allow(null),
|
|
24354
25100
|
deletedAt: Joi75.date().optional().allow(null)
|
|
24355
25101
|
});
|
|
24356
25102
|
var schemaUpdateBulletinBoard = Joi75.object({
|
|
24357
25103
|
_id: Joi75.string().hex().required(),
|
|
24358
|
-
recipients: Joi75.array().items(
|
|
24359
|
-
Joi75.string().valid(
|
|
24360
|
-
"admin",
|
|
24361
|
-
"organization",
|
|
24362
|
-
"site",
|
|
24363
|
-
"service-provider",
|
|
24364
|
-
"service-provider-member",
|
|
24365
|
-
"resident"
|
|
24366
|
-
)
|
|
24367
|
-
).optional(),
|
|
25104
|
+
recipients: Joi75.array().items(Joi75.string().valid(...BULLETIN_RECIPIENTS)).unique().optional(),
|
|
24368
25105
|
title: Joi75.string().optional().allow("", null),
|
|
24369
25106
|
content: Joi75.string().optional().allow("", null),
|
|
24370
25107
|
file: Joi75.array().items(
|
|
@@ -24377,7 +25114,7 @@ var schemaUpdateBulletinBoard = Joi75.object({
|
|
|
24377
25114
|
noExpiration: Joi75.boolean().optional(),
|
|
24378
25115
|
startDate: Joi75.date().optional().allow("", null),
|
|
24379
25116
|
endDate: Joi75.date().optional().allow("", null),
|
|
24380
|
-
status: Joi75.string().
|
|
25117
|
+
status: Joi75.string().valid(...STATUS_VALUES).optional()
|
|
24381
25118
|
});
|
|
24382
25119
|
function MBulletinBoard(value) {
|
|
24383
25120
|
const { error } = schemaBulletinBoard.validate(value);
|
|
@@ -24477,7 +25214,8 @@ function useBulletinBoardRepo() {
|
|
|
24477
25214
|
limit = 10,
|
|
24478
25215
|
sort = {},
|
|
24479
25216
|
site = "",
|
|
24480
|
-
status = "active"
|
|
25217
|
+
status = "active",
|
|
25218
|
+
recipients = []
|
|
24481
25219
|
}, session) {
|
|
24482
25220
|
page = page > 0 ? page - 1 : 0;
|
|
24483
25221
|
try {
|
|
@@ -24487,7 +25225,9 @@ function useBulletinBoardRepo() {
|
|
|
24487
25225
|
}
|
|
24488
25226
|
const query = {
|
|
24489
25227
|
site,
|
|
24490
|
-
status
|
|
25228
|
+
status,
|
|
25229
|
+
...search && { $text: { $search: search } },
|
|
25230
|
+
...recipients?.length && { recipients: { $in: recipients } }
|
|
24491
25231
|
};
|
|
24492
25232
|
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
24493
25233
|
const cacheOptions = {
|
|
@@ -24495,12 +25235,10 @@ function useBulletinBoardRepo() {
|
|
|
24495
25235
|
status,
|
|
24496
25236
|
sort: JSON.stringify(sort),
|
|
24497
25237
|
page,
|
|
24498
|
-
limit
|
|
25238
|
+
limit,
|
|
25239
|
+
...search && { search },
|
|
25240
|
+
...recipients?.length && { recipients: recipients.sort().join(",") }
|
|
24499
25241
|
};
|
|
24500
|
-
if (search) {
|
|
24501
|
-
query.$text = { $search: search };
|
|
24502
|
-
cacheOptions.search = search;
|
|
24503
|
-
}
|
|
24504
25242
|
const cacheKey = makeCacheKey39(namespace_collection, cacheOptions);
|
|
24505
25243
|
const cachedData = await getCache(cacheKey);
|
|
24506
25244
|
if (cachedData) {
|
|
@@ -24592,7 +25330,7 @@ function useBulletinBoardRepo() {
|
|
|
24592
25330
|
throw error;
|
|
24593
25331
|
}
|
|
24594
25332
|
}
|
|
24595
|
-
async function deleteBulletinBoardById(_id) {
|
|
25333
|
+
async function deleteBulletinBoardById(_id, session) {
|
|
24596
25334
|
try {
|
|
24597
25335
|
_id = new ObjectId72(_id);
|
|
24598
25336
|
} catch (error) {
|
|
@@ -24604,7 +25342,11 @@ function useBulletinBoardRepo() {
|
|
|
24604
25342
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24605
25343
|
deletedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24606
25344
|
};
|
|
24607
|
-
const res = await collection.updateOne(
|
|
25345
|
+
const res = await collection.updateOne(
|
|
25346
|
+
{ _id },
|
|
25347
|
+
{ $set: updateValue },
|
|
25348
|
+
{ session }
|
|
25349
|
+
);
|
|
24608
25350
|
if (res.modifiedCount === 0) {
|
|
24609
25351
|
throw new InternalServerError42("Unable to delete bulletin board.");
|
|
24610
25352
|
}
|
|
@@ -24670,8 +25412,11 @@ function useBulletinBoardService() {
|
|
|
24670
25412
|
const {
|
|
24671
25413
|
add: _add,
|
|
24672
25414
|
updateBulletinBoardById: _updateBulletinBoardById,
|
|
24673
|
-
processExpiredBulletinBoards: _processExpiredBulletinBoards
|
|
25415
|
+
processExpiredBulletinBoards: _processExpiredBulletinBoards,
|
|
25416
|
+
deleteBulletinBoardById: _deleteBulletinBoardById,
|
|
25417
|
+
getBulletinBoardById: _getBulletinBoardById
|
|
24674
25418
|
} = useBulletinBoardRepo();
|
|
25419
|
+
const { deleteFileById: _deleteFileById } = useFileRepo();
|
|
24675
25420
|
async function add(value) {
|
|
24676
25421
|
const session = useAtlas65.getClient()?.startSession();
|
|
24677
25422
|
session?.startTransaction();
|
|
@@ -24714,10 +25459,31 @@ function useBulletinBoardService() {
|
|
|
24714
25459
|
session?.endSession();
|
|
24715
25460
|
}
|
|
24716
25461
|
}
|
|
25462
|
+
async function deleteBulletinBoardById(id) {
|
|
25463
|
+
const session = useAtlas65.getClient()?.startSession();
|
|
25464
|
+
session?.startTransaction();
|
|
25465
|
+
try {
|
|
25466
|
+
const existingBulletinBoard = await _getBulletinBoardById(id);
|
|
25467
|
+
if (Array.isArray(existingBulletinBoard.file)) {
|
|
25468
|
+
for (const file of existingBulletinBoard.file) {
|
|
25469
|
+
await _deleteFileById(file, session);
|
|
25470
|
+
}
|
|
25471
|
+
}
|
|
25472
|
+
await _deleteBulletinBoardById(id, session);
|
|
25473
|
+
await session?.commitTransaction();
|
|
25474
|
+
return "Successfully deleted bulletin board.";
|
|
25475
|
+
} catch (error) {
|
|
25476
|
+
await session?.abortTransaction();
|
|
25477
|
+
throw error;
|
|
25478
|
+
} finally {
|
|
25479
|
+
session?.endSession();
|
|
25480
|
+
}
|
|
25481
|
+
}
|
|
24717
25482
|
return {
|
|
24718
25483
|
add,
|
|
24719
25484
|
updateBulletinBoardById,
|
|
24720
|
-
processExpiredBulletinBoards
|
|
25485
|
+
processExpiredBulletinBoards,
|
|
25486
|
+
deleteBulletinBoardById
|
|
24721
25487
|
};
|
|
24722
25488
|
}
|
|
24723
25489
|
|
|
@@ -24725,12 +25491,12 @@ function useBulletinBoardService() {
|
|
|
24725
25491
|
import { BadRequestError as BadRequestError123, logger as logger104 } from "@7365admin1/node-server-utils";
|
|
24726
25492
|
import Joi76 from "joi";
|
|
24727
25493
|
function useBulletinBoardController() {
|
|
24728
|
-
const { add: _add, updateBulletinBoardById: _updateBulletinBoardById } = useBulletinBoardService();
|
|
24729
25494
|
const {
|
|
24730
|
-
|
|
24731
|
-
|
|
25495
|
+
add: _add,
|
|
25496
|
+
updateBulletinBoardById: _updateBulletinBoardById,
|
|
24732
25497
|
deleteBulletinBoardById: _deleteBulletinBoardById
|
|
24733
|
-
} =
|
|
25498
|
+
} = useBulletinBoardService();
|
|
25499
|
+
const { getAll: _getAll, getBulletinBoardById: _getBulletinBoardById } = useBulletinBoardRepo();
|
|
24734
25500
|
async function add(req, res, next) {
|
|
24735
25501
|
const payload = { ...req.body };
|
|
24736
25502
|
const { error } = schemaBulletinBoard.validate(payload, {
|
|
@@ -24762,12 +25528,23 @@ function useBulletinBoardController() {
|
|
|
24762
25528
|
sort: Joi76.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
|
|
24763
25529
|
order: Joi76.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
|
|
24764
25530
|
site: Joi76.string().hex().required(),
|
|
24765
|
-
status: Joi76.string().optional().allow(null, "")
|
|
25531
|
+
status: Joi76.string().optional().allow(null, ""),
|
|
25532
|
+
recipients: Joi76.string().optional().allow("", null).custom((value, helpers) => {
|
|
25533
|
+
const parsed = value.split(",").map((v) => v.trim()).filter(Boolean);
|
|
25534
|
+
for (const r of parsed) {
|
|
25535
|
+
if (!BULLETIN_RECIPIENTS.includes(r)) {
|
|
25536
|
+
return helpers.error("any.only");
|
|
25537
|
+
}
|
|
25538
|
+
}
|
|
25539
|
+
return value;
|
|
25540
|
+
}).messages({
|
|
25541
|
+
"any.only": `Recipients must be one of: ${BULLETIN_RECIPIENTS.join(
|
|
25542
|
+
", "
|
|
25543
|
+
)}`
|
|
25544
|
+
})
|
|
24766
25545
|
});
|
|
24767
25546
|
const query = { ...req.query };
|
|
24768
|
-
const { error } = validation.validate(query, {
|
|
24769
|
-
abortEarly: false
|
|
24770
|
-
});
|
|
25547
|
+
const { error } = validation.validate(query, { abortEarly: false });
|
|
24771
25548
|
if (error) {
|
|
24772
25549
|
const messages = error.details.map((d) => d.message).join(", ");
|
|
24773
25550
|
logger104.log({ level: "error", message: messages });
|
|
@@ -24779,9 +25556,15 @@ function useBulletinBoardController() {
|
|
|
24779
25556
|
const limit = parseInt(req.query.limit ?? "10");
|
|
24780
25557
|
const site = req.query.site ?? "";
|
|
24781
25558
|
const status = req.query.status ?? "active";
|
|
25559
|
+
const recipientsRaw = req.query.recipients ?? "";
|
|
25560
|
+
const recipients = recipientsRaw && typeof recipientsRaw === "string" ? Array.from(
|
|
25561
|
+
new Set(
|
|
25562
|
+
recipientsRaw.split(",").map((r) => r.trim()).filter(Boolean)
|
|
25563
|
+
)
|
|
25564
|
+
) : [];
|
|
24782
25565
|
const sortObj = {};
|
|
24783
|
-
const sortFields = String(req.query.sort).split(",");
|
|
24784
|
-
const sortOrders = String(req.query.order).split(",");
|
|
25566
|
+
const sortFields = String(req.query.sort ?? "").split(",").filter(Boolean);
|
|
25567
|
+
const sortOrders = String(req.query.order ?? "").split(",").filter(Boolean);
|
|
24785
25568
|
sortFields.forEach((field, index) => {
|
|
24786
25569
|
if (allowedFields.includes(field)) {
|
|
24787
25570
|
const order = sortOrders[index] === "asc" ? 1 : -1;
|
|
@@ -24795,7 +25578,8 @@ function useBulletinBoardController() {
|
|
|
24795
25578
|
limit,
|
|
24796
25579
|
sort: sortObj,
|
|
24797
25580
|
site,
|
|
24798
|
-
status
|
|
25581
|
+
status,
|
|
25582
|
+
recipients
|
|
24799
25583
|
});
|
|
24800
25584
|
res.status(200).json(data);
|
|
24801
25585
|
return;
|
|
@@ -24878,6 +25662,16 @@ function useBulletinBoardController() {
|
|
|
24878
25662
|
import { BadRequestError as BadRequestError124, logger as logger105 } from "@7365admin1/node-server-utils";
|
|
24879
25663
|
import { ObjectId as ObjectId73 } from "mongodb";
|
|
24880
25664
|
import Joi77 from "joi";
|
|
25665
|
+
|
|
25666
|
+
// src/types/enums/billing-frequency.enum.ts
|
|
25667
|
+
var EBillingFrequency = /* @__PURE__ */ ((EBillingFrequency2) => {
|
|
25668
|
+
EBillingFrequency2["MONTHLY"] = "monthly";
|
|
25669
|
+
EBillingFrequency2["QAURTERLY"] = "quarterly";
|
|
25670
|
+
EBillingFrequency2["ANNUALLY"] = "annually";
|
|
25671
|
+
return EBillingFrequency2;
|
|
25672
|
+
})(EBillingFrequency || {});
|
|
25673
|
+
|
|
25674
|
+
// src/models/site-billing-item.model.ts
|
|
24881
25675
|
var schemaUnits = Joi77.object({
|
|
24882
25676
|
_id: Joi77.string().hex().optional(),
|
|
24883
25677
|
name: Joi77.string().optional()
|
|
@@ -24888,7 +25682,7 @@ var schemaBillingItem = Joi77.object({
|
|
|
24888
25682
|
org: Joi77.string().hex().required(),
|
|
24889
25683
|
name: Joi77.string().required(),
|
|
24890
25684
|
amount: Joi77.number().required(),
|
|
24891
|
-
frequency: Joi77.string().valid(
|
|
25685
|
+
frequency: Joi77.string().valid(...Object.values(EBillingFrequency)).required(),
|
|
24892
25686
|
billingType: Joi77.string().valid("recurring", "non-recurring").required(),
|
|
24893
25687
|
dueInDays: Joi77.number().optional().allow(null, ""),
|
|
24894
25688
|
date: Joi77.string().required(),
|
|
@@ -24913,7 +25707,7 @@ var schemaUpdateSiteBillingItem = Joi77.object({
|
|
|
24913
25707
|
org: Joi77.string().hex().optional().allow(null, ""),
|
|
24914
25708
|
name: Joi77.string().optional().allow(null, ""),
|
|
24915
25709
|
amount: Joi77.number().optional().allow(null, ""),
|
|
24916
|
-
frequency: Joi77.string().valid(
|
|
25710
|
+
frequency: Joi77.string().valid(...Object.values(EBillingFrequency)).optional().allow(null, ""),
|
|
24917
25711
|
billingType: Joi77.string().valid("recurring", "non-recurring").optional().allow(null, ""),
|
|
24918
25712
|
dueInDays: Joi77.number().optional().allow(null, ""),
|
|
24919
25713
|
date: Joi77.string().optional().allow(null, ""),
|
|
@@ -25067,7 +25861,16 @@ function useSiteBillingItemRepo() {
|
|
|
25067
25861
|
...search && {
|
|
25068
25862
|
$or: [
|
|
25069
25863
|
{ name: { $regex: search, $options: "i" } },
|
|
25070
|
-
{ frequency: { $regex: search, $options: "i" } }
|
|
25864
|
+
{ frequency: { $regex: search, $options: "i" } },
|
|
25865
|
+
{
|
|
25866
|
+
$expr: {
|
|
25867
|
+
$regexMatch: {
|
|
25868
|
+
input: { $toString: "$totalAmount" },
|
|
25869
|
+
regex: search,
|
|
25870
|
+
options: "i"
|
|
25871
|
+
}
|
|
25872
|
+
}
|
|
25873
|
+
}
|
|
25071
25874
|
]
|
|
25072
25875
|
},
|
|
25073
25876
|
...ObjectId74.isValid(site) && { site: new ObjectId74(site) }
|
|
@@ -26243,7 +27046,8 @@ function useEventManagementRepo() {
|
|
|
26243
27046
|
sort = {},
|
|
26244
27047
|
site = "",
|
|
26245
27048
|
status = "",
|
|
26246
|
-
type = ""
|
|
27049
|
+
type = "",
|
|
27050
|
+
date = ""
|
|
26247
27051
|
}, session) {
|
|
26248
27052
|
page = page > 0 ? page - 1 : 0;
|
|
26249
27053
|
try {
|
|
@@ -26254,7 +27058,13 @@ function useEventManagementRepo() {
|
|
|
26254
27058
|
const baseQuery = {
|
|
26255
27059
|
site,
|
|
26256
27060
|
status: status ? status : { $ne: "deleted" },
|
|
26257
|
-
...type && { type }
|
|
27061
|
+
...type && { type },
|
|
27062
|
+
...date && {
|
|
27063
|
+
dateTime: {
|
|
27064
|
+
$gte: `${date}T00:00:00.000Z`,
|
|
27065
|
+
$lt: `${date}T23:59:59.999Z`
|
|
27066
|
+
}
|
|
27067
|
+
}
|
|
26258
27068
|
};
|
|
26259
27069
|
let query = { ...baseQuery };
|
|
26260
27070
|
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
@@ -26552,7 +27362,8 @@ function useEventManagementController() {
|
|
|
26552
27362
|
order: Joi82.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
|
|
26553
27363
|
site: Joi82.string().hex().required(),
|
|
26554
27364
|
status: Joi82.string().optional(),
|
|
26555
|
-
type: Joi82.string().optional().valid("TASK", "EVENT").allow(null, "")
|
|
27365
|
+
type: Joi82.string().optional().valid("TASK", "EVENT").allow(null, ""),
|
|
27366
|
+
date: Joi82.string().optional().allow(null, "")
|
|
26556
27367
|
});
|
|
26557
27368
|
const query = { ...req.query };
|
|
26558
27369
|
const { error } = validation.validate(query, {
|
|
@@ -26570,6 +27381,7 @@ function useEventManagementController() {
|
|
|
26570
27381
|
const site = req.query.site ?? "";
|
|
26571
27382
|
const status = req.query.status ?? "";
|
|
26572
27383
|
const type = req.query.type ?? "";
|
|
27384
|
+
const date = req.query.date ?? "";
|
|
26573
27385
|
const sortObj = {};
|
|
26574
27386
|
const sortFields = String(req.query.sort).split(",");
|
|
26575
27387
|
const sortOrders = String(req.query.order).split(",");
|
|
@@ -26587,7 +27399,8 @@ function useEventManagementController() {
|
|
|
26587
27399
|
sort: sortObj,
|
|
26588
27400
|
site,
|
|
26589
27401
|
status,
|
|
26590
|
-
type
|
|
27402
|
+
type,
|
|
27403
|
+
date
|
|
26591
27404
|
});
|
|
26592
27405
|
res.status(200).json(data);
|
|
26593
27406
|
return;
|
|
@@ -26913,6 +27726,7 @@ function useSiteUnitBillingRepo() {
|
|
|
26913
27726
|
}
|
|
26914
27727
|
};
|
|
26915
27728
|
}
|
|
27729
|
+
const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
|
|
26916
27730
|
const query = {
|
|
26917
27731
|
paymentStatus,
|
|
26918
27732
|
status,
|
|
@@ -26920,7 +27734,16 @@ function useSiteUnitBillingRepo() {
|
|
|
26920
27734
|
$or: [
|
|
26921
27735
|
{ unitOwner: { $regex: search, $options: "i" } },
|
|
26922
27736
|
{ billName: { $regex: search, $options: "i" } },
|
|
26923
|
-
{ unit: { $regex:
|
|
27737
|
+
{ unit: { $regex: unitSearchRegex, $options: "i" } },
|
|
27738
|
+
{
|
|
27739
|
+
$expr: {
|
|
27740
|
+
$regexMatch: {
|
|
27741
|
+
input: { $toString: "$amountPaid" },
|
|
27742
|
+
regex: search,
|
|
27743
|
+
options: "i"
|
|
27744
|
+
}
|
|
27745
|
+
}
|
|
27746
|
+
}
|
|
26924
27747
|
]
|
|
26925
27748
|
},
|
|
26926
27749
|
...ObjectId80.isValid(site) && { site: new ObjectId80(site) },
|
|
@@ -27347,14 +28170,14 @@ function useSiteUnitBillingService() {
|
|
|
27347
28170
|
function isBillingChecker(billing_item, todayMonth, todayDate) {
|
|
27348
28171
|
const billingMonth = Number(billing_item.month);
|
|
27349
28172
|
const billingDay = Number(billing_item.date);
|
|
27350
|
-
if (billing_item.frequency === "
|
|
28173
|
+
if (billing_item.frequency === "monthly" /* MONTHLY */) {
|
|
27351
28174
|
return todayDate === billingDay;
|
|
27352
28175
|
}
|
|
27353
|
-
if (billing_item.frequency === "
|
|
28176
|
+
if (billing_item.frequency === "quarterly" /* QAURTERLY */) {
|
|
27354
28177
|
const monthDiff = todayMonth - billingMonth;
|
|
27355
28178
|
return monthDiff % 3 === 0 && todayDate === billingDay;
|
|
27356
28179
|
}
|
|
27357
|
-
if (billing_item.frequency === "
|
|
28180
|
+
if (billing_item.frequency === "annually" /* ANNUALLY */) {
|
|
27358
28181
|
return todayMonth === billingMonth && todayDate === billingDay;
|
|
27359
28182
|
}
|
|
27360
28183
|
return false;
|
|
@@ -28150,7 +28973,7 @@ function UseAccessManagementRepo() {
|
|
|
28150
28973
|
{
|
|
28151
28974
|
$match: {
|
|
28152
28975
|
...defaultQuery,
|
|
28153
|
-
status: { $
|
|
28976
|
+
status: { $eq: "active" }
|
|
28154
28977
|
}
|
|
28155
28978
|
},
|
|
28156
28979
|
// ✅ Only project needed fields before heavy lookups
|
|
@@ -28270,7 +29093,7 @@ function UseAccessManagementRepo() {
|
|
|
28270
29093
|
userType
|
|
28271
29094
|
}
|
|
28272
29095
|
},
|
|
28273
|
-
{ $project: { _id: 1, userId: 1, type: 1, cardNo: 1, isActivated: 1 } }
|
|
29096
|
+
{ $project: { _id: 1, userId: 1, type: 1, cardNo: 1, isActivated: 1, replacementStatus: 1 } }
|
|
28274
29097
|
],
|
|
28275
29098
|
as: "accessCards"
|
|
28276
29099
|
}
|
|
@@ -28289,7 +29112,21 @@ function UseAccessManagementRepo() {
|
|
|
28289
29112
|
$filter: {
|
|
28290
29113
|
input: "$accessCards",
|
|
28291
29114
|
as: "card",
|
|
28292
|
-
cond: { $ne: ["$$card.userId", null] }
|
|
29115
|
+
cond: { $and: [{ $ne: ["$$card.userId", null] }, { $eq: ["$$card.isActivated", true] }] }
|
|
29116
|
+
}
|
|
29117
|
+
},
|
|
29118
|
+
f_replaced: {
|
|
29119
|
+
$filter: {
|
|
29120
|
+
input: "$accessCards",
|
|
29121
|
+
as: "card",
|
|
29122
|
+
cond: { $and: [{ $eq: ["$$card.isActivated", false] }, { $ne: ["$$card.replacementStatus", null] }] }
|
|
29123
|
+
}
|
|
29124
|
+
},
|
|
29125
|
+
f_deleted: {
|
|
29126
|
+
$filter: {
|
|
29127
|
+
input: "$accessCards",
|
|
29128
|
+
as: "card",
|
|
29129
|
+
cond: { $and: [{ $eq: ["$$card.isActivated", false] }, { $eq: ["$$card.replacementStatus", null] }] }
|
|
28293
29130
|
}
|
|
28294
29131
|
}
|
|
28295
29132
|
}
|
|
@@ -28321,6 +29158,14 @@ function UseAccessManagementRepo() {
|
|
|
28321
29158
|
non_physical: { $size: { $filter: { input: { $ifNull: ["$f_Assigned", []] }, as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } } }
|
|
28322
29159
|
}
|
|
28323
29160
|
},
|
|
29161
|
+
replaced: {
|
|
29162
|
+
physical: { $filter: { input: "$f_replaced", as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } },
|
|
29163
|
+
non_physical: { $filter: { input: "$f_replaced", as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } }
|
|
29164
|
+
},
|
|
29165
|
+
deleted: {
|
|
29166
|
+
physical: { $filter: { input: "$f_deleted", as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } },
|
|
29167
|
+
non_physical: { $filter: { input: "$f_deleted", as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } }
|
|
29168
|
+
},
|
|
28324
29169
|
totalCardCount: {
|
|
28325
29170
|
$add: [
|
|
28326
29171
|
{ $size: { $filter: { input: { $ifNull: ["$f_Available", []] }, as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } } },
|
|
@@ -28777,16 +29622,26 @@ function UseAccessManagementRepo() {
|
|
|
28777
29622
|
async function cardReplacementRepo(params) {
|
|
28778
29623
|
const session = useAtlas74.getClient()?.startSession();
|
|
28779
29624
|
try {
|
|
28780
|
-
const { cardId, remarks } = params;
|
|
29625
|
+
const { cardId, remarks, issuedCardId, unitId, userId } = params;
|
|
28781
29626
|
const id = new ObjectId83(cardId);
|
|
29627
|
+
const newCardId = new ObjectId83(issuedCardId);
|
|
29628
|
+
const unit = new ObjectId83(unitId);
|
|
29629
|
+
const user = new ObjectId83(userId);
|
|
28782
29630
|
session?.startTransaction();
|
|
28783
|
-
const
|
|
28784
|
-
|
|
28785
|
-
|
|
28786
|
-
|
|
28787
|
-
|
|
29631
|
+
const sessionResult = await Promise.all([
|
|
29632
|
+
await collection().findOneAndUpdate(
|
|
29633
|
+
{ _id: id },
|
|
29634
|
+
{ $set: { remarks, replacementStatus: "Complete", requestDate: /* @__PURE__ */ new Date(), isActivated: false } },
|
|
29635
|
+
{ returnDocument: "after", session }
|
|
29636
|
+
),
|
|
29637
|
+
await collection().findOneAndUpdate(
|
|
29638
|
+
{ _id: newCardId },
|
|
29639
|
+
{ $set: { updatedAt: /* @__PURE__ */ new Date(), assignedUnit: unit, userId: user } },
|
|
29640
|
+
{ returnDocument: "after", session }
|
|
29641
|
+
)
|
|
29642
|
+
]);
|
|
28788
29643
|
await session?.commitTransaction();
|
|
28789
|
-
return
|
|
29644
|
+
return sessionResult;
|
|
28790
29645
|
} catch (error) {
|
|
28791
29646
|
await session?.abortTransaction();
|
|
28792
29647
|
throw new Error(error.message);
|
|
@@ -28804,7 +29659,8 @@ function UseAccessManagementRepo() {
|
|
|
28804
29659
|
const query = {
|
|
28805
29660
|
site: siteId,
|
|
28806
29661
|
assignedUnit: { $ne: null },
|
|
28807
|
-
type: "NFC" /* NFC
|
|
29662
|
+
type: "NFC" /* NFC */,
|
|
29663
|
+
isActivated: true
|
|
28808
29664
|
};
|
|
28809
29665
|
if (search) {
|
|
28810
29666
|
query.$or = [{ accessLevel: { $regex: search, $options: "i" } }, { cardNo: { $regex: search, $options: "i" } }];
|
|
@@ -28812,7 +29668,7 @@ function UseAccessManagementRepo() {
|
|
|
28812
29668
|
if (type) {
|
|
28813
29669
|
query.userType = type;
|
|
28814
29670
|
} else {
|
|
28815
|
-
query.userType = { $ne: "Resident
|
|
29671
|
+
query.userType = { $ne: "Visitor/Resident" /* DEFAULT */ };
|
|
28816
29672
|
}
|
|
28817
29673
|
const res = await collection().aggregate([
|
|
28818
29674
|
{
|
|
@@ -29035,7 +29891,8 @@ function UseAccessManagementRepo() {
|
|
|
29035
29891
|
rawItems.map(async (item) => {
|
|
29036
29892
|
const date = new Date(item["startDate (format MM/DD/YYYY)"]);
|
|
29037
29893
|
const endDate = new Date(date.setFullYear(date.getFullYear() + 10));
|
|
29038
|
-
const cardNumber = item["cardNo (number 0-65535 ex. 301)"]
|
|
29894
|
+
const cardNumber = String(Number(item["cardNo (number 0-65535 ex. 301)"] || 0)).padStart(6, "0");
|
|
29895
|
+
const facilityCode = String(Number(item["facilityCode (number 0-255 ex. 11)"] || 0)).padStart(4, "0");
|
|
29039
29896
|
const pin = item["pin (number 6 digits only)"] ? item["pin (number 6 digits only)"].toString().padStart(6, "0") : "123456";
|
|
29040
29897
|
const match = item["accessLevel (number ex. 1)"];
|
|
29041
29898
|
const accessLevel = match ? match : null;
|
|
@@ -29049,9 +29906,9 @@ function UseAccessManagementRepo() {
|
|
|
29049
29906
|
accessLevel,
|
|
29050
29907
|
accessGroup,
|
|
29051
29908
|
accessType: "Normal" /* NORMAL */,
|
|
29052
|
-
cardNo: cardNumber
|
|
29909
|
+
cardNo: `${facilityCode}${cardNumber}`,
|
|
29053
29910
|
pin,
|
|
29054
|
-
qrData: await createQrData({ cardNumber }),
|
|
29911
|
+
qrData: await createQrData({ cardNumber: `${facilityCode}${cardNumber}` }),
|
|
29055
29912
|
startDate: new Date(item["startDate (format MM/DD/YYYY)"]),
|
|
29056
29913
|
endDate: new Date(item["endDate (format MM/DD/YYYY)"] || endDate),
|
|
29057
29914
|
isActivated: true,
|
|
@@ -29125,6 +29982,112 @@ function UseAccessManagementRepo() {
|
|
|
29125
29982
|
session?.endSession();
|
|
29126
29983
|
}
|
|
29127
29984
|
}
|
|
29985
|
+
async function deleteCardRepo(params) {
|
|
29986
|
+
try {
|
|
29987
|
+
const { cardId, remarks } = params;
|
|
29988
|
+
const id = new ObjectId83(cardId);
|
|
29989
|
+
const result = await collection().findOneAndUpdate({ _id: id }, { $set: { isActivated: false, updatedAt: /* @__PURE__ */ new Date(), remarks, requestDate: /* @__PURE__ */ new Date() } }, { returnDocument: "after" });
|
|
29990
|
+
return result;
|
|
29991
|
+
} catch (error) {
|
|
29992
|
+
throw new Error(error.message);
|
|
29993
|
+
}
|
|
29994
|
+
}
|
|
29995
|
+
async function getCardDetailsRepo(params) {
|
|
29996
|
+
try {
|
|
29997
|
+
const { siteId, cardId } = params;
|
|
29998
|
+
const convertedSiteId = new ObjectId83(siteId);
|
|
29999
|
+
const convertedCardId = new ObjectId83(cardId);
|
|
30000
|
+
const card = await collection().findOne(
|
|
30001
|
+
{
|
|
30002
|
+
_id: convertedCardId,
|
|
30003
|
+
site: convertedSiteId,
|
|
30004
|
+
type: "NFC" /* NFC */,
|
|
30005
|
+
userType: "Visitor/Resident" /* DEFAULT */
|
|
30006
|
+
},
|
|
30007
|
+
{
|
|
30008
|
+
projection: {
|
|
30009
|
+
userId: 1,
|
|
30010
|
+
site: 1,
|
|
30011
|
+
type: 1,
|
|
30012
|
+
userType: 1,
|
|
30013
|
+
cardNo: 1,
|
|
30014
|
+
isActivated: 1,
|
|
30015
|
+
replacementStatus: 1,
|
|
30016
|
+
vmsRemarks: 1,
|
|
30017
|
+
remarks: 1,
|
|
30018
|
+
requestDate: 1,
|
|
30019
|
+
createdAt: 1,
|
|
30020
|
+
updatedAt: 1
|
|
30021
|
+
}
|
|
30022
|
+
}
|
|
30023
|
+
);
|
|
30024
|
+
if (!card)
|
|
30025
|
+
return null;
|
|
30026
|
+
const site = await collectionName("sites").findOne(
|
|
30027
|
+
{ _id: card.site },
|
|
30028
|
+
{ projection: { name: 1, status: 1 } }
|
|
30029
|
+
);
|
|
30030
|
+
const user = card.userId ? await collectionName("users").findOne(
|
|
30031
|
+
{ _id: card.userId },
|
|
30032
|
+
{ projection: { name: 1, email: 1 } }
|
|
30033
|
+
) : null;
|
|
30034
|
+
const status = card.userId === null && card.isActivated === true ? "available" : card.userId !== null && card.isActivated === true ? "assigned" : card.isActivated === false && card.replacementStatus !== null ? "replaced" : "deleted";
|
|
30035
|
+
return {
|
|
30036
|
+
...card,
|
|
30037
|
+
status,
|
|
30038
|
+
site,
|
|
30039
|
+
user
|
|
30040
|
+
};
|
|
30041
|
+
} catch (error) {
|
|
30042
|
+
throw new Error(error.message);
|
|
30043
|
+
}
|
|
30044
|
+
}
|
|
30045
|
+
async function addQrTagRepo(params) {
|
|
30046
|
+
try {
|
|
30047
|
+
const { site, payload } = params;
|
|
30048
|
+
const id = new ObjectId83(site);
|
|
30049
|
+
const highestCardNo = await collection().aggregate([
|
|
30050
|
+
{ $match: { site: id, type: "NFC" /* NFC */, userType: "Visitor/Resident" /* DEFAULT */ } },
|
|
30051
|
+
{
|
|
30052
|
+
$addFields: {
|
|
30053
|
+
qrTagCardNoNumeric: { $toInt: "$qrTagCardNo" }
|
|
30054
|
+
// Convert string to integer
|
|
30055
|
+
}
|
|
30056
|
+
},
|
|
30057
|
+
{
|
|
30058
|
+
$sort: { qrTagCardNoNumeric: -1 }
|
|
30059
|
+
// Sort in descending order
|
|
30060
|
+
},
|
|
30061
|
+
{
|
|
30062
|
+
$limit: 1
|
|
30063
|
+
// Get the highest
|
|
30064
|
+
}
|
|
30065
|
+
]).toArray();
|
|
30066
|
+
let start = 0;
|
|
30067
|
+
if (highestCardNo.length > 0) {
|
|
30068
|
+
start = highestCardNo[0].qrTagCardNoNumeric || 0;
|
|
30069
|
+
}
|
|
30070
|
+
const nextCardNumbers = Array.from({ length: payload.length }, (_, i) => String(start + i + 1).padStart(5, "0"));
|
|
30071
|
+
const bulkOps = await Promise.all(
|
|
30072
|
+
payload.map(async (doc, index) => {
|
|
30073
|
+
const id2 = new ObjectId83(doc._id);
|
|
30074
|
+
return {
|
|
30075
|
+
updateOne: {
|
|
30076
|
+
filter: { _id: id2 },
|
|
30077
|
+
update: { $set: { qrTag: doc.qrTag, qrTagCardNo: nextCardNumbers[index] } }
|
|
30078
|
+
}
|
|
30079
|
+
};
|
|
30080
|
+
})
|
|
30081
|
+
);
|
|
30082
|
+
let result;
|
|
30083
|
+
if (bulkOps.length > 0) {
|
|
30084
|
+
result = await collection().bulkWrite(bulkOps);
|
|
30085
|
+
}
|
|
30086
|
+
return result;
|
|
30087
|
+
} catch (error) {
|
|
30088
|
+
throw new Error(error.message);
|
|
30089
|
+
}
|
|
30090
|
+
}
|
|
29128
30091
|
return {
|
|
29129
30092
|
createIndexes,
|
|
29130
30093
|
createIndexForEntrypass,
|
|
@@ -29145,7 +30108,10 @@ function UseAccessManagementRepo() {
|
|
|
29145
30108
|
getCardReplacementRepo,
|
|
29146
30109
|
getAccessManagementSettingsRepo,
|
|
29147
30110
|
bulkPhysicalAccessCardRepo,
|
|
29148
|
-
assignAccessCardToUnitRepo
|
|
30111
|
+
assignAccessCardToUnitRepo,
|
|
30112
|
+
deleteCardRepo,
|
|
30113
|
+
getCardDetailsRepo,
|
|
30114
|
+
addQrTagRepo
|
|
29149
30115
|
};
|
|
29150
30116
|
}
|
|
29151
30117
|
|
|
@@ -29177,7 +30143,10 @@ function useAccessManagementSvc() {
|
|
|
29177
30143
|
getCardReplacementRepo,
|
|
29178
30144
|
getAccessManagementSettingsRepo,
|
|
29179
30145
|
bulkPhysicalAccessCardRepo,
|
|
29180
|
-
assignAccessCardToUnitRepo
|
|
30146
|
+
assignAccessCardToUnitRepo,
|
|
30147
|
+
deleteCardRepo,
|
|
30148
|
+
getCardDetailsRepo,
|
|
30149
|
+
addQrTagRepo
|
|
29181
30150
|
} = UseAccessManagementRepo();
|
|
29182
30151
|
const addPhysicalCardSvc = async (payload) => {
|
|
29183
30152
|
try {
|
|
@@ -29425,6 +30394,30 @@ function useAccessManagementSvc() {
|
|
|
29425
30394
|
throw new Error(err.message);
|
|
29426
30395
|
}
|
|
29427
30396
|
};
|
|
30397
|
+
const deleteCardSvc = async (params) => {
|
|
30398
|
+
try {
|
|
30399
|
+
const response = await deleteCardRepo({ ...params });
|
|
30400
|
+
return response;
|
|
30401
|
+
} catch (err) {
|
|
30402
|
+
throw new Error(err.message);
|
|
30403
|
+
}
|
|
30404
|
+
};
|
|
30405
|
+
const getCardDetailsSvc = async (params) => {
|
|
30406
|
+
try {
|
|
30407
|
+
const response = await getCardDetailsRepo({ ...params });
|
|
30408
|
+
return response;
|
|
30409
|
+
} catch (err) {
|
|
30410
|
+
throw new Error(err.message);
|
|
30411
|
+
}
|
|
30412
|
+
};
|
|
30413
|
+
const addQrTagSvc = async (params) => {
|
|
30414
|
+
try {
|
|
30415
|
+
const response = await addQrTagRepo({ ...params });
|
|
30416
|
+
return response;
|
|
30417
|
+
} catch (err) {
|
|
30418
|
+
throw new Error(err.message);
|
|
30419
|
+
}
|
|
30420
|
+
};
|
|
29428
30421
|
return {
|
|
29429
30422
|
addPhysicalCardSvc,
|
|
29430
30423
|
addNonPhysicalCardSvc,
|
|
@@ -29449,7 +30442,10 @@ function useAccessManagementSvc() {
|
|
|
29449
30442
|
getAccessManagementSettingsSvc,
|
|
29450
30443
|
convertBufferFile,
|
|
29451
30444
|
bulkPhysicalAccessCardSvc,
|
|
29452
|
-
assignAccessCardToUnitSvc
|
|
30445
|
+
assignAccessCardToUnitSvc,
|
|
30446
|
+
deleteCardSvc,
|
|
30447
|
+
getCardDetailsSvc,
|
|
30448
|
+
addQrTagSvc
|
|
29453
30449
|
};
|
|
29454
30450
|
}
|
|
29455
30451
|
|
|
@@ -29481,7 +30477,10 @@ function useAccessManagementController() {
|
|
|
29481
30477
|
getAccessManagementSettingsSvc,
|
|
29482
30478
|
convertBufferFile,
|
|
29483
30479
|
bulkPhysicalAccessCardSvc,
|
|
29484
|
-
assignAccessCardToUnitSvc
|
|
30480
|
+
assignAccessCardToUnitSvc,
|
|
30481
|
+
deleteCardSvc,
|
|
30482
|
+
getCardDetailsSvc,
|
|
30483
|
+
addQrTagSvc
|
|
29485
30484
|
} = useAccessManagementSvc();
|
|
29486
30485
|
const addPhysicalCard = async (req, res) => {
|
|
29487
30486
|
try {
|
|
@@ -29710,11 +30709,24 @@ function useAccessManagementController() {
|
|
|
29710
30709
|
userType: Joi85.string().optional().allow("", null),
|
|
29711
30710
|
type: Joi85.string().optional().allow("", null)
|
|
29712
30711
|
});
|
|
30712
|
+
const user = req.cookies?.sid;
|
|
29713
30713
|
const { error } = schema2.validate({ site, userType, type });
|
|
29714
30714
|
if (error) {
|
|
29715
30715
|
return res.status(400).json({ message: error.message });
|
|
29716
30716
|
}
|
|
30717
|
+
const key = `${namespace2}:${user}:available-access-cards`;
|
|
30718
|
+
const listKey = `${namespace2}:${user}:list`;
|
|
30719
|
+
const { redis } = useCache48(key);
|
|
30720
|
+
const cachedData = await getCache({ key, redis });
|
|
30721
|
+
if (cachedData) {
|
|
30722
|
+
console.log("\u26A1 Cache hit:", key);
|
|
30723
|
+
redis.expire(key, 60).catch(console.error);
|
|
30724
|
+
redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
|
|
30725
|
+
return res.status(200).json({ message: "Success", data: cachedData });
|
|
30726
|
+
}
|
|
29717
30727
|
const result = await availableAccessCardsSvc({ site, userType, type });
|
|
30728
|
+
await setCache({ key, data: result, ttlSeconds: 60, redis });
|
|
30729
|
+
redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
|
|
29718
30730
|
return res.status(200).json({ message: "Success", data: result });
|
|
29719
30731
|
} catch (error) {
|
|
29720
30732
|
return res.status(400).json({
|
|
@@ -29896,16 +30908,19 @@ function useAccessManagementController() {
|
|
|
29896
30908
|
};
|
|
29897
30909
|
const cardReplacement = async (req, res) => {
|
|
29898
30910
|
try {
|
|
29899
|
-
const { cardId, remarks } = req.body;
|
|
30911
|
+
const { cardId, remarks, unitId, issuedCardId, userId } = req.body;
|
|
29900
30912
|
const schema2 = Joi85.object({
|
|
29901
30913
|
cardId: Joi85.string().required(),
|
|
29902
|
-
remarks: Joi85.string().optional().allow("", null)
|
|
30914
|
+
remarks: Joi85.string().optional().allow("", null),
|
|
30915
|
+
unitId: Joi85.string().hex().required(),
|
|
30916
|
+
issuedCardId: Joi85.string().required(),
|
|
30917
|
+
userId: Joi85.string().hex().required()
|
|
29903
30918
|
});
|
|
29904
|
-
const { error } = schema2.validate({ cardId, remarks });
|
|
30919
|
+
const { error } = schema2.validate({ cardId, remarks, unitId, issuedCardId, userId });
|
|
29905
30920
|
if (error) {
|
|
29906
30921
|
return res.status(400).json({ message: error.message });
|
|
29907
30922
|
}
|
|
29908
|
-
const result = await cardReplacementSvc({ cardId, remarks });
|
|
30923
|
+
const result = await cardReplacementSvc({ cardId, remarks, unitId, issuedCardId, userId });
|
|
29909
30924
|
return res.status(200).json({ message: "Success", data: result });
|
|
29910
30925
|
} catch (error) {
|
|
29911
30926
|
return res.status(500).json({
|
|
@@ -30036,6 +31051,74 @@ function useAccessManagementController() {
|
|
|
30036
31051
|
});
|
|
30037
31052
|
}
|
|
30038
31053
|
};
|
|
31054
|
+
const deleteCard = async (req, res) => {
|
|
31055
|
+
try {
|
|
31056
|
+
const { cardId, remarks } = req.body;
|
|
31057
|
+
const schema2 = Joi85.object({
|
|
31058
|
+
cardId: Joi85.string().hex().required(),
|
|
31059
|
+
remarks: Joi85.string().optional().allow("", null)
|
|
31060
|
+
});
|
|
31061
|
+
const { error } = schema2.validate({ cardId, remarks });
|
|
31062
|
+
if (error) {
|
|
31063
|
+
return res.status(400).json({ message: error.message });
|
|
31064
|
+
}
|
|
31065
|
+
const result = await deleteCardSvc({ cardId, remarks });
|
|
31066
|
+
return res.status(200).json({ message: "Success", data: result });
|
|
31067
|
+
} catch (error) {
|
|
31068
|
+
return res.status(500).json({
|
|
31069
|
+
data: null,
|
|
31070
|
+
message: error.message
|
|
31071
|
+
});
|
|
31072
|
+
}
|
|
31073
|
+
};
|
|
31074
|
+
const getCardDetails = async (req, res) => {
|
|
31075
|
+
try {
|
|
31076
|
+
const { siteId, cardId } = req.query;
|
|
31077
|
+
const schema2 = Joi85.object({
|
|
31078
|
+
siteId: Joi85.string().hex().required(),
|
|
31079
|
+
cardId: Joi85.string().hex().required()
|
|
31080
|
+
});
|
|
31081
|
+
const { error } = schema2.validate({ siteId, cardId });
|
|
31082
|
+
if (error) {
|
|
31083
|
+
return res.status(400).json({ message: error.message });
|
|
31084
|
+
}
|
|
31085
|
+
const result = await getCardDetailsSvc({ siteId, cardId });
|
|
31086
|
+
return res.status(200).json({ message: "Success", data: result });
|
|
31087
|
+
} catch (error) {
|
|
31088
|
+
return res.status(500).json({
|
|
31089
|
+
data: null,
|
|
31090
|
+
message: error.message
|
|
31091
|
+
});
|
|
31092
|
+
}
|
|
31093
|
+
};
|
|
31094
|
+
const addQrTag = async (req, res) => {
|
|
31095
|
+
try {
|
|
31096
|
+
const payload = req.body;
|
|
31097
|
+
const site = req.params.site;
|
|
31098
|
+
const schema2 = Joi85.object({
|
|
31099
|
+
site: Joi85.string().hex().required(),
|
|
31100
|
+
payload: Joi85.array().items(
|
|
31101
|
+
Joi85.object({
|
|
31102
|
+
_id: Joi85.string().hex().required(),
|
|
31103
|
+
cardNo: Joi85.string().required(),
|
|
31104
|
+
qrTag: Joi85.string().required(),
|
|
31105
|
+
qrTagCardNo: Joi85.string().required()
|
|
31106
|
+
})
|
|
31107
|
+
).required()
|
|
31108
|
+
});
|
|
31109
|
+
const { error } = schema2.validate({ site, payload });
|
|
31110
|
+
if (error) {
|
|
31111
|
+
return res.status(400).json({ message: error.message });
|
|
31112
|
+
}
|
|
31113
|
+
const result = await addQrTagSvc({ payload, site });
|
|
31114
|
+
return res.status(200).json({ message: "Success", data: result });
|
|
31115
|
+
} catch (error) {
|
|
31116
|
+
return res.status(500).json({
|
|
31117
|
+
data: null,
|
|
31118
|
+
message: error.message
|
|
31119
|
+
});
|
|
31120
|
+
}
|
|
31121
|
+
};
|
|
30039
31122
|
return {
|
|
30040
31123
|
addPhysicalCard,
|
|
30041
31124
|
addNonPhysicalCard,
|
|
@@ -30057,7 +31140,10 @@ function useAccessManagementController() {
|
|
|
30057
31140
|
getCardReplacement,
|
|
30058
31141
|
getAccessManagementSettings,
|
|
30059
31142
|
bulkPhysicalAccessCard,
|
|
30060
|
-
assignAccessCardToUnit
|
|
31143
|
+
assignAccessCardToUnit,
|
|
31144
|
+
deleteCard,
|
|
31145
|
+
getCardDetails,
|
|
31146
|
+
addQrTag
|
|
30061
31147
|
};
|
|
30062
31148
|
}
|
|
30063
31149
|
|
|
@@ -31809,9 +32895,9 @@ function useStatementOfAccountRepo() {
|
|
|
31809
32895
|
page = page > 0 ? page - 1 : 0;
|
|
31810
32896
|
let dateExpr = {};
|
|
31811
32897
|
if (dateFrom && dateTo) {
|
|
31812
|
-
|
|
32898
|
+
let startDate = new Date(dateFrom);
|
|
31813
32899
|
startDate.setHours(0, 0, 0, 0);
|
|
31814
|
-
|
|
32900
|
+
let endDate = new Date(dateTo);
|
|
31815
32901
|
endDate.setHours(23, 59, 59, 999);
|
|
31816
32902
|
dateExpr = {
|
|
31817
32903
|
$expr: {
|
|
@@ -31822,13 +32908,15 @@ function useStatementOfAccountRepo() {
|
|
|
31822
32908
|
}
|
|
31823
32909
|
};
|
|
31824
32910
|
}
|
|
32911
|
+
const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
|
|
31825
32912
|
const query = {
|
|
31826
32913
|
...status && status !== "all" && { status },
|
|
31827
32914
|
...search && {
|
|
31828
32915
|
$or: [
|
|
31829
32916
|
{ unitOwner: { $regex: search, $options: "i" } },
|
|
31830
|
-
{ unit: { $regex:
|
|
31831
|
-
{ email: { $regex: search, $options: "i" } }
|
|
32917
|
+
{ unit: { $regex: unitSearchRegex, $options: "i" } },
|
|
32918
|
+
{ email: { $regex: search, $options: "i" } },
|
|
32919
|
+
{ category: { $regex: search, $options: "i" } }
|
|
31832
32920
|
]
|
|
31833
32921
|
},
|
|
31834
32922
|
...ObjectId91.isValid(site) && { site: new ObjectId91(site) },
|
|
@@ -34122,6 +35210,9 @@ function useIncidentReportRepo() {
|
|
|
34122
35210
|
$regex: search,
|
|
34123
35211
|
$options: "i"
|
|
34124
35212
|
}
|
|
35213
|
+
},
|
|
35214
|
+
{
|
|
35215
|
+
approvedByName: { $regex: search, $options: "i" }
|
|
34125
35216
|
}
|
|
34126
35217
|
]
|
|
34127
35218
|
},
|
|
@@ -36677,7 +37768,9 @@ function useNfcPatrolLogController() {
|
|
|
36677
37768
|
};
|
|
36678
37769
|
}
|
|
36679
37770
|
export {
|
|
37771
|
+
ANPRMode,
|
|
36680
37772
|
AccessTypeProps,
|
|
37773
|
+
BULLETIN_RECIPIENTS,
|
|
36681
37774
|
DEVICE_STATUS,
|
|
36682
37775
|
EAccessCardTypes,
|
|
36683
37776
|
EAccessCardUserTypes,
|
|
@@ -36736,12 +37829,15 @@ export {
|
|
|
36736
37829
|
MVerification,
|
|
36737
37830
|
MVisitorTransaction,
|
|
36738
37831
|
MWorkOrder,
|
|
37832
|
+
OrgNature,
|
|
36739
37833
|
PERSON_TYPES,
|
|
37834
|
+
STATUS_VALUES,
|
|
36740
37835
|
UseAccessManagementRepo,
|
|
36741
|
-
|
|
37836
|
+
VehicleCategory,
|
|
37837
|
+
VehicleStatus,
|
|
37838
|
+
VehicleType,
|
|
36742
37839
|
allowedFieldsSite,
|
|
36743
37840
|
allowedNatures,
|
|
36744
|
-
allowedTypes,
|
|
36745
37841
|
attendanceSchema,
|
|
36746
37842
|
attendanceSettingsSchema,
|
|
36747
37843
|
chatSchema,
|