@7365admin1/core 3.36.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +39 -6
- package/dist/index.js +2939 -2746
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2106 -1913
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -24955,6 +24955,16 @@ import {
|
|
|
24955
24955
|
toObjectId as toObjectId10
|
|
24956
24956
|
} from "@7365admin1/node-server-utils";
|
|
24957
24957
|
import { ObjectId as ObjectId45 } from "mongodb";
|
|
24958
|
+
|
|
24959
|
+
// src/utils/sanitizer.ts
|
|
24960
|
+
var makeContainsRegex = (input) => {
|
|
24961
|
+
if (!input)
|
|
24962
|
+
return "";
|
|
24963
|
+
const sanitized = input.replace(/\s+/g, "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24964
|
+
return sanitized;
|
|
24965
|
+
};
|
|
24966
|
+
|
|
24967
|
+
// src/repositories/person.repo.ts
|
|
24958
24968
|
var site_people_namespace_collection = "site.people";
|
|
24959
24969
|
function usePersonRepo() {
|
|
24960
24970
|
const db = useAtlas33.getDb();
|
|
@@ -25027,7 +25037,10 @@ function usePersonRepo() {
|
|
|
25027
25037
|
dateTo,
|
|
25028
25038
|
org = "",
|
|
25029
25039
|
site = "",
|
|
25030
|
-
type = ""
|
|
25040
|
+
type = "",
|
|
25041
|
+
nric = "",
|
|
25042
|
+
contact = "",
|
|
25043
|
+
plateNumber = ""
|
|
25031
25044
|
}, session) {
|
|
25032
25045
|
page = page > 0 ? page - 1 : 0;
|
|
25033
25046
|
const start = dateFrom ? { $gte: new Date(dateFrom).toISOString() } : void 0;
|
|
@@ -25046,12 +25059,28 @@ function usePersonRepo() {
|
|
|
25046
25059
|
{ name: { $regex: search, $options: "i" } },
|
|
25047
25060
|
{ email: { $regex: search, $options: "i" } },
|
|
25048
25061
|
{ nric: { $regex: search, $options: "i" } },
|
|
25049
|
-
{ "plates.plateNumber": { $regex: search, $options: "i" } }
|
|
25062
|
+
{ "plates.plateNumber": { $regex: search, $options: "i" } },
|
|
25063
|
+
{ contact: { $regex: makeContainsRegex(contact) } },
|
|
25064
|
+
{ contacts: { $regex: makeContainsRegex(contact) } },
|
|
25065
|
+
{ plateNumbers: { $regex: makeContainsRegex(plateNumber) } }
|
|
25050
25066
|
]
|
|
25051
25067
|
},
|
|
25052
25068
|
...ObjectId45.isValid(org) && { org: new ObjectId45(org) },
|
|
25053
25069
|
...ObjectId45.isValid(site) && { site: new ObjectId45(site) },
|
|
25054
|
-
...PERSON_TYPES.includes(type) && { type }
|
|
25070
|
+
...PERSON_TYPES.includes(type) && { type },
|
|
25071
|
+
...nric && { nric: { $regex: makeContainsRegex(nric), $options: "i" } },
|
|
25072
|
+
...contact && {
|
|
25073
|
+
$or: [
|
|
25074
|
+
{ contact: { $regex: makeContainsRegex(contact) } },
|
|
25075
|
+
{ contacts: { $regex: makeContainsRegex(contact) } }
|
|
25076
|
+
]
|
|
25077
|
+
},
|
|
25078
|
+
...plateNumber && {
|
|
25079
|
+
$or: [
|
|
25080
|
+
{ "plates.plateNumber": { $regex: makeContainsRegex(plateNumber) } },
|
|
25081
|
+
{ plateNumbers: { $regex: makeContainsRegex(plateNumber) } }
|
|
25082
|
+
]
|
|
25083
|
+
}
|
|
25055
25084
|
};
|
|
25056
25085
|
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
25057
25086
|
try {
|
|
@@ -25129,6 +25158,9 @@ function usePersonRepo() {
|
|
|
25129
25158
|
throw new BadRequestError72("Invalid person transaction ID format.");
|
|
25130
25159
|
}
|
|
25131
25160
|
value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25161
|
+
if (value.updatedBy) {
|
|
25162
|
+
value.updatedBy = await convertObjectIdUtil2(value.updatedBy, "updatedBy");
|
|
25163
|
+
}
|
|
25132
25164
|
try {
|
|
25133
25165
|
const result = await collection.updateOne(
|
|
25134
25166
|
{ _id },
|
|
@@ -25637,6 +25669,31 @@ function usePersonRepo() {
|
|
|
25637
25669
|
throw error;
|
|
25638
25670
|
}
|
|
25639
25671
|
}
|
|
25672
|
+
async function findFirstTen(query, session) {
|
|
25673
|
+
try {
|
|
25674
|
+
query.site = await convertObjectIdUtil2(query.site, "site");
|
|
25675
|
+
const data = await collection.find(query, { session }).limit(10).toArray();
|
|
25676
|
+
return data;
|
|
25677
|
+
} catch (error) {
|
|
25678
|
+
throw new InternalServerError26("Failed to find many persons.");
|
|
25679
|
+
}
|
|
25680
|
+
}
|
|
25681
|
+
async function addFromVisitor(value, session) {
|
|
25682
|
+
value.site = await convertObjectIdUtil2(value.site, "site");
|
|
25683
|
+
if (value.updatedBy) {
|
|
25684
|
+
value.updatedBy = await convertObjectIdUtil2(value.updatedBy, "updatedBy");
|
|
25685
|
+
}
|
|
25686
|
+
if (value.createdBy) {
|
|
25687
|
+
value.createdBy = await convertObjectIdUtil2(value.createdBy, "createdBy");
|
|
25688
|
+
}
|
|
25689
|
+
const res = await collection.insertOne({
|
|
25690
|
+
...value,
|
|
25691
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25692
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25693
|
+
}, { session });
|
|
25694
|
+
console.log("person.repo addFromVisitor res", res);
|
|
25695
|
+
return res;
|
|
25696
|
+
}
|
|
25640
25697
|
return {
|
|
25641
25698
|
add,
|
|
25642
25699
|
getAll,
|
|
@@ -25656,7 +25713,9 @@ function usePersonRepo() {
|
|
|
25656
25713
|
pushVehicleById,
|
|
25657
25714
|
pullVehicleByRecNo,
|
|
25658
25715
|
getByUserId,
|
|
25659
|
-
reviewResidentPerson
|
|
25716
|
+
reviewResidentPerson,
|
|
25717
|
+
addFromVisitor,
|
|
25718
|
+
findFirstTen
|
|
25660
25719
|
};
|
|
25661
25720
|
}
|
|
25662
25721
|
|
|
@@ -26830,8 +26889,7 @@ function useVehicleService() {
|
|
|
26830
26889
|
throw new Error("Unable to start session for vehicle service.");
|
|
26831
26890
|
}
|
|
26832
26891
|
}
|
|
26833
|
-
const
|
|
26834
|
-
const [_vehiclePlateNumber] = plateNumbers;
|
|
26892
|
+
const [_vehiclePlateNumber] = value.plateNumber;
|
|
26835
26893
|
const [existingPlateNumber, org] = await Promise.all([
|
|
26836
26894
|
_getVehicleByPlateNumber(_vehiclePlateNumber),
|
|
26837
26895
|
_getById(value.org)
|
|
@@ -26896,6 +26954,7 @@ function useVehicleService() {
|
|
|
26896
26954
|
}
|
|
26897
26955
|
}
|
|
26898
26956
|
const owner = value.name;
|
|
26957
|
+
const plateNumbers = value.plateNumber;
|
|
26899
26958
|
try {
|
|
26900
26959
|
if (!isExternalSession) {
|
|
26901
26960
|
session.startTransaction();
|
|
@@ -27112,10 +27171,7 @@ function useVehicleService() {
|
|
|
27112
27171
|
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
27113
27172
|
if (dahuaResponse?.statusCode != 200) {
|
|
27114
27173
|
console.log("dahuaResponse", dahuaResponse);
|
|
27115
|
-
console.log(
|
|
27116
|
-
"approveVehicleById dahuaResponse dahuaResponse?.statusCode != 200",
|
|
27117
|
-
dahuaResponse?.statusCode
|
|
27118
|
-
);
|
|
27174
|
+
console.log("approveVehicleById dahuaResponse dahuaResponse?.statusCode != 200", dahuaResponse?.statusCode);
|
|
27119
27175
|
throw new BadRequestError75("Failed to add plate number to ANPR");
|
|
27120
27176
|
}
|
|
27121
27177
|
const responseData = dahuaResponse?.data.toString("utf-8");
|
|
@@ -27322,19 +27378,7 @@ function useVehicleService() {
|
|
|
27322
27378
|
if (value.peopleId) {
|
|
27323
27379
|
value.peopleId = new ObjectId49(value.peopleId);
|
|
27324
27380
|
}
|
|
27325
|
-
const {
|
|
27326
|
-
name,
|
|
27327
|
-
plateNumber,
|
|
27328
|
-
start,
|
|
27329
|
-
end,
|
|
27330
|
-
recNo,
|
|
27331
|
-
type,
|
|
27332
|
-
unit,
|
|
27333
|
-
site,
|
|
27334
|
-
block,
|
|
27335
|
-
level,
|
|
27336
|
-
...rest
|
|
27337
|
-
} = value;
|
|
27381
|
+
const { name, plateNumber, start, end, recNo, type, unit, site, block, level, ...rest } = value;
|
|
27338
27382
|
if (_name != name || _plateNumber != plateNumber) {
|
|
27339
27383
|
const startDahua = value.start ? formatDahuaDate(new Date(value.start)) : formatDahuaDate(new Date(_start));
|
|
27340
27384
|
const endDahua = value.end ? formatDahuaDate(new Date(value.end)) : formatDahuaDate(new Date(_end));
|
|
@@ -27361,11 +27405,7 @@ function useVehicleService() {
|
|
|
27361
27405
|
for (const camera of siteCameras) {
|
|
27362
27406
|
const { host, username, password } = camera;
|
|
27363
27407
|
if (_plateNumber != plateNumber) {
|
|
27364
|
-
console.log(
|
|
27365
|
-
"updateVehicleById _plateNumber != plateNumber",
|
|
27366
|
-
_plateNumber,
|
|
27367
|
-
plateNumber
|
|
27368
|
-
);
|
|
27408
|
+
console.log("updateVehicleById _plateNumber != plateNumber", _plateNumber, plateNumber);
|
|
27369
27409
|
const removePlateNumber = {
|
|
27370
27410
|
host,
|
|
27371
27411
|
username,
|
|
@@ -27412,11 +27452,7 @@ function useVehicleService() {
|
|
|
27412
27452
|
);
|
|
27413
27453
|
}
|
|
27414
27454
|
} else {
|
|
27415
|
-
console.log(
|
|
27416
|
-
"updateVehicleById _plateNumber == plateNumber",
|
|
27417
|
-
_plateNumber,
|
|
27418
|
-
plateNumber
|
|
27419
|
-
);
|
|
27455
|
+
console.log("updateVehicleById _plateNumber == plateNumber", _plateNumber, plateNumber);
|
|
27420
27456
|
const dahuaPayload = {
|
|
27421
27457
|
host,
|
|
27422
27458
|
username,
|
|
@@ -33593,294 +33629,856 @@ function useAttendanceController() {
|
|
|
33593
33629
|
}
|
|
33594
33630
|
|
|
33595
33631
|
// src/services/visitor-transaction.service.ts
|
|
33632
|
+
import {
|
|
33633
|
+
BadRequestError as BadRequestError101,
|
|
33634
|
+
logger as logger81,
|
|
33635
|
+
useAtlas as useAtlas52,
|
|
33636
|
+
useMailer as useMailer3
|
|
33637
|
+
} from "@7365admin1/node-server-utils";
|
|
33638
|
+
import { ObjectId as ObjectId64 } from "mongodb";
|
|
33639
|
+
|
|
33640
|
+
// src/services/person.service.ts
|
|
33596
33641
|
import {
|
|
33597
33642
|
BadRequestError as BadRequestError100,
|
|
33598
33643
|
logger as logger80,
|
|
33599
|
-
|
|
33600
|
-
|
|
33644
|
+
NotFoundError as NotFoundError23,
|
|
33645
|
+
useAtlas as useAtlas50,
|
|
33646
|
+
hashPassword as hashPassword3,
|
|
33647
|
+
useMailer as useMailer2,
|
|
33648
|
+
compileHandlebar as compileHandlebar2,
|
|
33649
|
+
getDirectory as getDirectory2
|
|
33601
33650
|
} from "@7365admin1/node-server-utils";
|
|
33602
|
-
import { ObjectId as ObjectId63 } from "mongodb";
|
|
33603
|
-
|
|
33604
|
-
// src/repositories/key.repo.ts
|
|
33605
|
-
import { ObjectId as ObjectId62 } from "mongodb";
|
|
33606
|
-
|
|
33607
|
-
// src/utils/mongo.ts
|
|
33608
|
-
import { useAtlas as useAtlas50 } from "@7365admin1/node-server-utils";
|
|
33609
|
-
var getDB = () => {
|
|
33610
|
-
const db = useAtlas50.getDb();
|
|
33611
|
-
if (!db) {
|
|
33612
|
-
throw new Error("Database not initialized! Check if useAtlas.initialize finished.");
|
|
33613
|
-
}
|
|
33614
|
-
return db;
|
|
33615
|
-
};
|
|
33616
|
-
|
|
33617
|
-
// src/models/key.model.ts
|
|
33618
33651
|
import { ObjectId as ObjectId61 } from "mongodb";
|
|
33619
|
-
|
|
33620
|
-
|
|
33621
|
-
|
|
33622
|
-
|
|
33623
|
-
|
|
33624
|
-
|
|
33625
|
-
|
|
33626
|
-
|
|
33627
|
-
// isTaken = false,
|
|
33628
|
-
site = "",
|
|
33629
|
-
organization = "",
|
|
33630
|
-
serviceProvider = "",
|
|
33631
|
-
createdAt = /* @__PURE__ */ new Date(),
|
|
33632
|
-
updatedAt = /* @__PURE__ */ new Date(),
|
|
33633
|
-
template,
|
|
33634
|
-
status = "Available",
|
|
33635
|
-
updatedBy = "",
|
|
33636
|
-
qty,
|
|
33637
|
-
description,
|
|
33638
|
-
location,
|
|
33639
|
-
prefix,
|
|
33640
|
-
keyNo,
|
|
33641
|
-
parentId
|
|
33642
|
-
} = {}) {
|
|
33643
|
-
this._id = _id;
|
|
33644
|
-
this.name = name;
|
|
33645
|
-
this.codes = codes;
|
|
33646
|
-
this.passType = passType;
|
|
33647
|
-
this.passId = passId;
|
|
33648
|
-
this.code = code;
|
|
33649
|
-
this.site = site;
|
|
33650
|
-
this.organization = organization;
|
|
33651
|
-
this.serviceProvider = serviceProvider;
|
|
33652
|
-
this.createdAt = createdAt;
|
|
33653
|
-
this.updatedAt = updatedAt;
|
|
33654
|
-
this.template = template;
|
|
33655
|
-
this.status = status;
|
|
33656
|
-
this.updatedBy = updatedBy;
|
|
33657
|
-
this.qty = qty;
|
|
33658
|
-
this.description = description;
|
|
33659
|
-
this.location = location;
|
|
33660
|
-
this.prefix = prefix;
|
|
33661
|
-
this.parentId = parentId;
|
|
33662
|
-
this.keyNo = keyNo;
|
|
33663
|
-
}
|
|
33664
|
-
};
|
|
33665
|
-
|
|
33666
|
-
// src/utils/pagination.util.ts
|
|
33667
|
-
function paginationUtil(items, page, limit, length) {
|
|
33668
|
-
const startIndex = length ? page * limit + 1 : 0;
|
|
33669
|
-
const endIndex = Math.min(startIndex + limit - 1, length);
|
|
33670
|
-
return {
|
|
33671
|
-
items,
|
|
33672
|
-
pages: Math.ceil(length / limit),
|
|
33673
|
-
pageRange: `${startIndex}-${endIndex} of ${length}`
|
|
33652
|
+
function usePersonService() {
|
|
33653
|
+
const MailerConfig = {
|
|
33654
|
+
host: MAILER_TRANSPORT_HOST,
|
|
33655
|
+
port: MAILER_TRANSPORT_PORT,
|
|
33656
|
+
secure: MAILER_TRANSPORT_SECURE,
|
|
33657
|
+
email: MAILER_EMAIL,
|
|
33658
|
+
from: MAILER_FROM_EMAIL,
|
|
33659
|
+
password: MAILER_PASSWORD
|
|
33674
33660
|
};
|
|
33675
|
-
|
|
33676
|
-
|
|
33677
|
-
|
|
33678
|
-
|
|
33679
|
-
|
|
33680
|
-
|
|
33681
|
-
|
|
33682
|
-
|
|
33683
|
-
|
|
33684
|
-
|
|
33685
|
-
|
|
33686
|
-
|
|
33687
|
-
|
|
33688
|
-
|
|
33689
|
-
|
|
33690
|
-
|
|
33691
|
-
|
|
33692
|
-
|
|
33693
|
-
|
|
33694
|
-
|
|
33661
|
+
const mailer = new useMailer2(MailerConfig);
|
|
33662
|
+
const {
|
|
33663
|
+
add: _add,
|
|
33664
|
+
getById: _getById,
|
|
33665
|
+
updateById: _updateById,
|
|
33666
|
+
getByUserId: _getByUserId,
|
|
33667
|
+
reviewResidentPerson: _reviewResidentPerson,
|
|
33668
|
+
addFromVisitor: _addFromVisitor,
|
|
33669
|
+
findFirstTen,
|
|
33670
|
+
getApprovedPersonByPlateNumber
|
|
33671
|
+
} = usePersonRepo();
|
|
33672
|
+
const { addPlateNumber: _addPlateNumber } = useDahuaService();
|
|
33673
|
+
const {
|
|
33674
|
+
createUser: addUser,
|
|
33675
|
+
getUserByEmail,
|
|
33676
|
+
updateUserFieldById: _updateUserFieldById,
|
|
33677
|
+
getUserById,
|
|
33678
|
+
updateUserOrgById: _updateUserOrgById,
|
|
33679
|
+
updateUserUnitById: _updateUserUnitById
|
|
33680
|
+
} = useUserRepo();
|
|
33681
|
+
const { add: addMember } = useMemberRepo();
|
|
33682
|
+
const { getById: _getUnitById, updateById: updateUnitById } = useBuildingUnitRepo();
|
|
33683
|
+
const { getAllCameraWithPassword: _getAllSiteCameras, getCamerasBySiteId } = useSiteCameraRepo();
|
|
33684
|
+
const { updateStatusById } = useFileRepo();
|
|
33685
|
+
const { getById: getOrgById } = useOrgRepo();
|
|
33686
|
+
const { getSiteById } = useSiteRepo();
|
|
33687
|
+
const { add: addVehicle } = useVehicleService();
|
|
33688
|
+
async function add(value) {
|
|
33689
|
+
const session = useAtlas50.getClient()?.startSession();
|
|
33690
|
+
if (!session) {
|
|
33691
|
+
throw new Error("Unable to start session for people service.");
|
|
33695
33692
|
}
|
|
33696
|
-
}
|
|
33697
|
-
static async getKeyWithSequenceNumber(site, _id) {
|
|
33698
33693
|
try {
|
|
33699
|
-
|
|
33700
|
-
const
|
|
33701
|
-
|
|
33702
|
-
|
|
33703
|
-
|
|
33704
|
-
|
|
33705
|
-
|
|
33706
|
-
|
|
33707
|
-
|
|
33708
|
-
|
|
33694
|
+
session.startTransaction();
|
|
33695
|
+
const personFiles = value?.files ?? [];
|
|
33696
|
+
if (personFiles.length > 0) {
|
|
33697
|
+
for (const personFile of personFiles) {
|
|
33698
|
+
const file = await updateStatusById(
|
|
33699
|
+
personFile.id,
|
|
33700
|
+
{ status: "active" },
|
|
33701
|
+
session
|
|
33702
|
+
);
|
|
33703
|
+
if (!file) {
|
|
33704
|
+
throw new NotFoundError23("File not found.");
|
|
33705
|
+
}
|
|
33709
33706
|
}
|
|
33710
33707
|
}
|
|
33711
|
-
|
|
33712
|
-
|
|
33713
|
-
return Promise.reject("Server internal error. " + error);
|
|
33714
|
-
}
|
|
33715
|
-
}
|
|
33716
|
-
static async countByTemplateId(template, passType, site) {
|
|
33717
|
-
try {
|
|
33718
|
-
template = await convertObjectIdUtil(template);
|
|
33719
|
-
site = await convertObjectIdUtil(site);
|
|
33720
|
-
let passTypeQuery = { passType };
|
|
33721
|
-
if (passType != "pass-key")
|
|
33722
|
-
passTypeQuery = {
|
|
33723
|
-
passType: { $in: ["visitor-pass", "contractor-pass", "agent-pass"] }
|
|
33724
|
-
};
|
|
33725
|
-
const query = [
|
|
33726
|
-
{ $match: { template, ...passTypeQuery, site } },
|
|
33727
|
-
{ $addFields: { nameAsNumber: { $toDouble: "$name" } } },
|
|
33728
|
-
{ $sort: { nameAsNumber: -1 } },
|
|
33729
|
-
{ $limit: 1 },
|
|
33730
|
-
{ $project: { nameAsNumber: 1 } }
|
|
33731
|
-
];
|
|
33732
|
-
const result = await this.collection().aggregate(query).toArray();
|
|
33733
|
-
const highestName = result.length > 0 && result[0].nameAsNumber > 0 ? result[0].nameAsNumber : 0;
|
|
33734
|
-
return highestName;
|
|
33735
|
-
} catch (error) {
|
|
33736
|
-
return Promise.reject("Server internal error. " + error);
|
|
33737
|
-
}
|
|
33738
|
-
}
|
|
33739
|
-
static async createIndexes() {
|
|
33740
|
-
try {
|
|
33741
|
-
await this.collection().createIndex(
|
|
33742
|
-
{ passType: "text", name: "text" },
|
|
33743
|
-
{ name: "key_search" }
|
|
33708
|
+
const isValidType = ["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
|
|
33709
|
+
value?.type
|
|
33744
33710
|
);
|
|
33745
|
-
|
|
33746
|
-
|
|
33747
|
-
|
|
33748
|
-
|
|
33749
|
-
|
|
33750
|
-
|
|
33751
|
-
|
|
33752
|
-
|
|
33753
|
-
|
|
33754
|
-
|
|
33755
|
-
|
|
33756
|
-
|
|
33757
|
-
|
|
33758
|
-
|
|
33759
|
-
|
|
33760
|
-
|
|
33761
|
-
|
|
33762
|
-
|
|
33763
|
-
|
|
33764
|
-
|
|
33765
|
-
|
|
33766
|
-
|
|
33767
|
-
|
|
33768
|
-
|
|
33711
|
+
if (value.email && value.password && value.name && isValidType) {
|
|
33712
|
+
const _user = await getUserByEmail(value.email);
|
|
33713
|
+
if (_user) {
|
|
33714
|
+
throw new BadRequestError100(`User already exists: ${value.email}.`);
|
|
33715
|
+
}
|
|
33716
|
+
const hashedPassword = await hashPassword3(value.password);
|
|
33717
|
+
const user = {
|
|
33718
|
+
email: value.email,
|
|
33719
|
+
password: hashedPassword,
|
|
33720
|
+
name: value.name,
|
|
33721
|
+
status: value.platform == "mobile" ? "pending" : "active",
|
|
33722
|
+
defaultOrg: value.org?.toString() || "",
|
|
33723
|
+
block: value.block,
|
|
33724
|
+
level: value.level,
|
|
33725
|
+
type: value.type,
|
|
33726
|
+
unitName: value.unitName,
|
|
33727
|
+
contact: value.contact,
|
|
33728
|
+
site: value.site?.toString() || "",
|
|
33729
|
+
unitId: value.unit?.toString() || ""
|
|
33730
|
+
};
|
|
33731
|
+
const userId = await addUser(user, session);
|
|
33732
|
+
value.user = userId.toString();
|
|
33733
|
+
let org = null;
|
|
33734
|
+
if (userId) {
|
|
33735
|
+
if (value?.org) {
|
|
33736
|
+
org = await getOrgById(value.org);
|
|
33737
|
+
if (!org) {
|
|
33738
|
+
value.org = "";
|
|
33739
|
+
}
|
|
33740
|
+
}
|
|
33741
|
+
let site;
|
|
33742
|
+
if (value.site) {
|
|
33743
|
+
site = await getSiteById(value.site);
|
|
33744
|
+
}
|
|
33745
|
+
await addMember(
|
|
33746
|
+
{
|
|
33747
|
+
org: value.org?.toString() || "",
|
|
33748
|
+
orgName: org?.name || "",
|
|
33749
|
+
user: userId.toString(),
|
|
33750
|
+
name: value.name,
|
|
33751
|
+
role: "",
|
|
33752
|
+
type: "resident",
|
|
33753
|
+
siteId: value.site?.toString() || "",
|
|
33754
|
+
siteName: site?.name || ""
|
|
33755
|
+
},
|
|
33756
|
+
session
|
|
33757
|
+
);
|
|
33758
|
+
}
|
|
33759
|
+
}
|
|
33760
|
+
if (value.isOwner && value.unit) {
|
|
33761
|
+
const unit = await _getUnitById(value.unit.toString());
|
|
33762
|
+
await updateUnitById(
|
|
33763
|
+
unit?._id?.toString() || "",
|
|
33764
|
+
{ ownerName: value.name, owner: value._id },
|
|
33765
|
+
session
|
|
33766
|
+
);
|
|
33767
|
+
}
|
|
33768
|
+
await _add(value, session);
|
|
33769
|
+
await session.commitTransaction();
|
|
33770
|
+
if (value.email && isValidType && value.platform === "mobile") {
|
|
33771
|
+
const emailContent = compileHandlebar2({
|
|
33772
|
+
context: { name: value.name },
|
|
33773
|
+
filePath: getDirectory2(
|
|
33774
|
+
__dirname,
|
|
33775
|
+
"./public/handlebars/registration-pending"
|
|
33776
|
+
)
|
|
33777
|
+
});
|
|
33778
|
+
await mailer.sendMail({
|
|
33779
|
+
to: value.email,
|
|
33780
|
+
subject: "Registration Received \u2013 Pending Review",
|
|
33781
|
+
html: emailContent,
|
|
33782
|
+
sender: "iService365"
|
|
33783
|
+
}).catch((error) => {
|
|
33784
|
+
logger80.error(`Failed to send registration pending email: ${error}`);
|
|
33785
|
+
});
|
|
33786
|
+
}
|
|
33787
|
+
return "People added successfully.";
|
|
33769
33788
|
} catch (error) {
|
|
33770
|
-
|
|
33789
|
+
logger80.error("Error in people service add:", error);
|
|
33790
|
+
await session.abortTransaction();
|
|
33791
|
+
throw error;
|
|
33792
|
+
} finally {
|
|
33793
|
+
session.endSession();
|
|
33771
33794
|
}
|
|
33772
33795
|
}
|
|
33773
|
-
|
|
33796
|
+
async function updateById(_id, value) {
|
|
33797
|
+
const session = useAtlas50.getClient()?.startSession();
|
|
33798
|
+
if (!session) {
|
|
33799
|
+
throw new BadRequestError100("Database session not available.");
|
|
33800
|
+
}
|
|
33774
33801
|
try {
|
|
33775
|
-
|
|
33776
|
-
|
|
33777
|
-
|
|
33778
|
-
|
|
33779
|
-
|
|
33780
|
-
|
|
33781
|
-
|
|
33782
|
-
|
|
33783
|
-
|
|
33784
|
-
|
|
33785
|
-
|
|
33786
|
-
|
|
33787
|
-
|
|
33788
|
-
|
|
33789
|
-
|
|
33790
|
-
|
|
33791
|
-
|
|
33792
|
-
|
|
33793
|
-
|
|
33794
|
-
|
|
33795
|
-
|
|
33796
|
-
|
|
33797
|
-
|
|
33798
|
-
|
|
33799
|
-
|
|
33800
|
-
|
|
33801
|
-
|
|
33802
|
-
|
|
33803
|
-
|
|
33804
|
-
|
|
33805
|
-
|
|
33806
|
-
|
|
33807
|
-
|
|
33808
|
-
|
|
33809
|
-
|
|
33810
|
-
|
|
33811
|
-
|
|
33812
|
-
|
|
33813
|
-
|
|
33814
|
-
|
|
33815
|
-
|
|
33816
|
-
|
|
33817
|
-
|
|
33802
|
+
await session.startTransaction();
|
|
33803
|
+
const person = await _getById(_id.toString());
|
|
33804
|
+
if (!person) {
|
|
33805
|
+
throw new BadRequestError100("Person not found.");
|
|
33806
|
+
}
|
|
33807
|
+
const isNameUpdated = typeof value.name === "string" && value.name.trim() !== person.name;
|
|
33808
|
+
const isOwnerChanged = value.isOwner !== person.isOwner;
|
|
33809
|
+
if (value.org && typeof value.org === "string") {
|
|
33810
|
+
value.org = new ObjectId61(value.org);
|
|
33811
|
+
}
|
|
33812
|
+
if (value.site && typeof value.site === "string") {
|
|
33813
|
+
value.site = new ObjectId61(value.site);
|
|
33814
|
+
}
|
|
33815
|
+
if (value.block && typeof value.block === "string") {
|
|
33816
|
+
value.block = new ObjectId61(value.block);
|
|
33817
|
+
}
|
|
33818
|
+
if (value.level && typeof value.level === "string") {
|
|
33819
|
+
value.level = new ObjectId61(value.level);
|
|
33820
|
+
}
|
|
33821
|
+
if (value.unit && typeof value.unit === "string") {
|
|
33822
|
+
value.unit = new ObjectId61(value.unit);
|
|
33823
|
+
}
|
|
33824
|
+
const isOrgChanged = value.org && person.org?.toString() !== value.org.toString();
|
|
33825
|
+
const toKey = (v) => v === null || v === void 0 || v === "" ? "" : v.toString();
|
|
33826
|
+
const isBlockChanged = "block" in value && toKey(value.block) !== toKey(person.block);
|
|
33827
|
+
const isLevelChanged = "level" in value && toKey(value.level) !== toKey(person.level);
|
|
33828
|
+
const isUnitChanged = "unit" in value && toKey(value.unit) !== toKey(person.unit);
|
|
33829
|
+
await _updateById(_id, value, session);
|
|
33830
|
+
if (isOrgChanged && person.user) {
|
|
33831
|
+
await _updateUserOrgById(
|
|
33832
|
+
person.user.toString(),
|
|
33833
|
+
value.org,
|
|
33834
|
+
session
|
|
33835
|
+
);
|
|
33836
|
+
}
|
|
33837
|
+
if ((isBlockChanged || isLevelChanged || isUnitChanged) && person.user) {
|
|
33838
|
+
const userUnitPayload = {};
|
|
33839
|
+
if (isBlockChanged) {
|
|
33840
|
+
userUnitPayload.block = value.block ?? null;
|
|
33841
|
+
}
|
|
33842
|
+
if (isLevelChanged) {
|
|
33843
|
+
userUnitPayload.level = value.level ?? null;
|
|
33844
|
+
}
|
|
33845
|
+
if (isUnitChanged) {
|
|
33846
|
+
userUnitPayload.unitId = value.unit ?? null;
|
|
33847
|
+
if ("unitName" in value) {
|
|
33848
|
+
userUnitPayload.unitName = value.unitName ?? "";
|
|
33818
33849
|
}
|
|
33819
|
-
}
|
|
33820
|
-
|
|
33821
|
-
|
|
33822
|
-
|
|
33823
|
-
|
|
33824
|
-
|
|
33825
|
-
|
|
33826
|
-
|
|
33827
|
-
|
|
33850
|
+
}
|
|
33851
|
+
await _updateUserUnitById(
|
|
33852
|
+
person.user.toString(),
|
|
33853
|
+
userUnitPayload,
|
|
33854
|
+
session
|
|
33855
|
+
);
|
|
33856
|
+
}
|
|
33857
|
+
if (value.unit && (isNameUpdated || isOwnerChanged || value.isOwner)) {
|
|
33858
|
+
const unit = await _getUnitById(value.unit.toString());
|
|
33859
|
+
if (unit) {
|
|
33860
|
+
let updatePayload = {};
|
|
33861
|
+
if (isOwnerChanged) {
|
|
33862
|
+
if (value.isOwner) {
|
|
33863
|
+
updatePayload.owner = _id;
|
|
33864
|
+
updatePayload.ownerName = value.name || "";
|
|
33865
|
+
} else {
|
|
33866
|
+
updatePayload.owner = "";
|
|
33867
|
+
updatePayload.ownerName = "";
|
|
33828
33868
|
}
|
|
33829
33869
|
}
|
|
33830
|
-
|
|
33831
|
-
|
|
33832
|
-
{
|
|
33833
|
-
$match: {
|
|
33834
|
-
prefixAndName: {
|
|
33835
|
-
$regex: `^${prefixAndName}$`,
|
|
33836
|
-
$options: "i"
|
|
33837
|
-
}
|
|
33838
|
-
}
|
|
33870
|
+
if (isNameUpdated && value.isOwner && unit.owner?.toString() === _id.toString()) {
|
|
33871
|
+
updatePayload.ownerName = value.name;
|
|
33839
33872
|
}
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
33843
|
-
QRTemplateInfo: 0
|
|
33873
|
+
if (value.isOwner && (!unit.owner || !unit.ownerName)) {
|
|
33874
|
+
updatePayload.owner = _id;
|
|
33875
|
+
updatePayload.ownerName = value.name || "";
|
|
33844
33876
|
}
|
|
33845
|
-
|
|
33846
|
-
|
|
33847
|
-
|
|
33848
|
-
|
|
33849
|
-
|
|
33850
|
-
|
|
33851
|
-
|
|
33852
|
-
|
|
33853
|
-
|
|
33854
|
-
|
|
33855
|
-
|
|
33856
|
-
|
|
33857
|
-
|
|
33858
|
-
|
|
33859
|
-
|
|
33860
|
-
|
|
33861
|
-
|
|
33862
|
-
|
|
33863
|
-
|
|
33864
|
-
|
|
33865
|
-
|
|
33866
|
-
|
|
33867
|
-
|
|
33868
|
-
|
|
33869
|
-
|
|
33870
|
-
|
|
33871
|
-
|
|
33872
|
-
|
|
33873
|
-
|
|
33874
|
-
|
|
33875
|
-
|
|
33876
|
-
|
|
33877
|
-
|
|
33878
|
-
|
|
33879
|
-
|
|
33880
|
-
|
|
33881
|
-
|
|
33882
|
-
|
|
33883
|
-
|
|
33877
|
+
if (Object.keys(updatePayload).length > 0 && unit && unit._id) {
|
|
33878
|
+
await updateUnitById(
|
|
33879
|
+
unit._id.toString(),
|
|
33880
|
+
updatePayload,
|
|
33881
|
+
session
|
|
33882
|
+
);
|
|
33883
|
+
}
|
|
33884
|
+
}
|
|
33885
|
+
}
|
|
33886
|
+
if (isUnitChanged && person.isOwner && person.unit) {
|
|
33887
|
+
const previousUnit = await _getUnitById(person.unit.toString());
|
|
33888
|
+
if (previousUnit && previousUnit._id && previousUnit.owner?.toString() === person?._id?.toString()) {
|
|
33889
|
+
await updateUnitById(
|
|
33890
|
+
previousUnit._id.toString(),
|
|
33891
|
+
{ owner: "", ownerName: "" },
|
|
33892
|
+
session
|
|
33893
|
+
);
|
|
33894
|
+
}
|
|
33895
|
+
}
|
|
33896
|
+
await session.commitTransaction();
|
|
33897
|
+
return "Person updated successfully.";
|
|
33898
|
+
} catch (error) {
|
|
33899
|
+
logger80.log({
|
|
33900
|
+
level: "error",
|
|
33901
|
+
message: `Failed to add site billing item: ${error.message}`
|
|
33902
|
+
});
|
|
33903
|
+
await session.abortTransaction();
|
|
33904
|
+
throw error;
|
|
33905
|
+
} finally {
|
|
33906
|
+
session.endSession();
|
|
33907
|
+
}
|
|
33908
|
+
}
|
|
33909
|
+
async function plateValidity(value) {
|
|
33910
|
+
const now = /* @__PURE__ */ new Date();
|
|
33911
|
+
if (["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
|
|
33912
|
+
value?.type
|
|
33913
|
+
)) {
|
|
33914
|
+
const end = new Date(now);
|
|
33915
|
+
end.setFullYear(end.getFullYear() + 10);
|
|
33916
|
+
return {
|
|
33917
|
+
start: now.toISOString(),
|
|
33918
|
+
end: end.toISOString()
|
|
33919
|
+
};
|
|
33920
|
+
}
|
|
33921
|
+
return { start: "", end: "" };
|
|
33922
|
+
}
|
|
33923
|
+
async function reviewResidentPerson(id, value) {
|
|
33924
|
+
const session = useAtlas50.getClient()?.startSession();
|
|
33925
|
+
session?.startTransaction();
|
|
33926
|
+
try {
|
|
33927
|
+
const person = await _getById(id.toString());
|
|
33928
|
+
if (!person)
|
|
33929
|
+
throw new BadRequestError100("Person not found.");
|
|
33930
|
+
if (value.approvedBy) {
|
|
33931
|
+
const approvedBy = await getUserById(value.approvedBy.id);
|
|
33932
|
+
if (!approvedBy || !approvedBy.name)
|
|
33933
|
+
throw new BadRequestError100("Created by not found.");
|
|
33934
|
+
value.approvedBy.name = approvedBy.name;
|
|
33935
|
+
value.approvedBy.id = approvedBy._id;
|
|
33936
|
+
}
|
|
33937
|
+
const vehicle = {
|
|
33938
|
+
plateNumber: person.plateNumber ?? "",
|
|
33939
|
+
type: "whitelist",
|
|
33940
|
+
category: person.type || "",
|
|
33941
|
+
name: person.name,
|
|
33942
|
+
phoneNumber: person.contact,
|
|
33943
|
+
org: person?.org?.toString(),
|
|
33944
|
+
site: person.site.toString(),
|
|
33945
|
+
block: person.block?.toString() ?? void 0,
|
|
33946
|
+
level: person.level?.toString() ?? void 0,
|
|
33947
|
+
unit: person.unit?.toString() ?? void 0,
|
|
33948
|
+
nric: person.nric,
|
|
33949
|
+
status: "active",
|
|
33950
|
+
peopleId: person._id?.toString()
|
|
33951
|
+
};
|
|
33952
|
+
const reviewStatus = value.status;
|
|
33953
|
+
if (reviewStatus === "approved") {
|
|
33954
|
+
value.status = "active";
|
|
33955
|
+
}
|
|
33956
|
+
await _reviewResidentPerson(id, value, session);
|
|
33957
|
+
const logoPath = `${STORAGE_API}/seven-365.svg`;
|
|
33958
|
+
const statusIconPath = STORAGE_API;
|
|
33959
|
+
let title = "";
|
|
33960
|
+
let message2 = "";
|
|
33961
|
+
let hideLogin = true;
|
|
33962
|
+
let logo = logoPath;
|
|
33963
|
+
let statusLogo = statusIconPath;
|
|
33964
|
+
if (person.user) {
|
|
33965
|
+
let userStatus = "";
|
|
33966
|
+
switch (reviewStatus) {
|
|
33967
|
+
case "approved":
|
|
33968
|
+
userStatus = "active";
|
|
33969
|
+
if (person.plateNumber && person.plateNumber.trim() !== "") {
|
|
33970
|
+
const anprCameraList = await getCamerasBySiteId(
|
|
33971
|
+
person.site.toString()
|
|
33972
|
+
);
|
|
33973
|
+
const existingPerson = await getApprovedPersonByPlateNumber(
|
|
33974
|
+
person.plateNumber,
|
|
33975
|
+
id
|
|
33976
|
+
);
|
|
33977
|
+
if (anprCameraList.length > 0 && !existingPerson) {
|
|
33978
|
+
await addVehicle(vehicle, session);
|
|
33979
|
+
}
|
|
33980
|
+
}
|
|
33981
|
+
title = "Your account has been approved";
|
|
33982
|
+
hideLogin = false;
|
|
33983
|
+
statusLogo = `${statusIconPath}/approved-icon.png`;
|
|
33984
|
+
message2 = `Welcome, ${person.name}! Thank you for signing up; we're excited to have you join us!`;
|
|
33985
|
+
break;
|
|
33986
|
+
case "resubmit":
|
|
33987
|
+
userStatus = "resubmit";
|
|
33988
|
+
title = "We request resubmission for your application";
|
|
33989
|
+
hideLogin = false;
|
|
33990
|
+
statusLogo = `${statusIconPath}/resubmission-icon.png`;
|
|
33991
|
+
message2 = `Hi ${person.name}, please resubmit your application with the required updates.${value.remarks ? ` Remarks: ${value.remarks}` : ""}`;
|
|
33992
|
+
break;
|
|
33993
|
+
case "rejected":
|
|
33994
|
+
userStatus = "rejected";
|
|
33995
|
+
title = "Your account request for application has been rejected.";
|
|
33996
|
+
statusLogo = `${statusIconPath}/deleted-icon.png`;
|
|
33997
|
+
message2 = `Hi ${person.name}, unfortunately your application has been rejected.${value.remarks ? ` Remarks: ${value.remarks}` : " Please contact support for more information."}`;
|
|
33998
|
+
break;
|
|
33999
|
+
default:
|
|
34000
|
+
throw new Error("Invalid review status");
|
|
34001
|
+
}
|
|
34002
|
+
const dir = __dirname;
|
|
34003
|
+
const filePath = getDirectory2(
|
|
34004
|
+
dir,
|
|
34005
|
+
"./public/handlebars/resident-remarks-email"
|
|
34006
|
+
);
|
|
34007
|
+
const emailContent = compileHandlebar2({
|
|
34008
|
+
context: {
|
|
34009
|
+
title,
|
|
34010
|
+
message2,
|
|
34011
|
+
hideLogin,
|
|
34012
|
+
logo,
|
|
34013
|
+
statusLogo
|
|
34014
|
+
},
|
|
34015
|
+
filePath
|
|
34016
|
+
});
|
|
34017
|
+
await mailer.sendMail({
|
|
34018
|
+
to: person.email,
|
|
34019
|
+
subject: title,
|
|
34020
|
+
html: emailContent,
|
|
34021
|
+
sender: "iService365"
|
|
34022
|
+
}).catch((error) => {
|
|
34023
|
+
logger80.log({
|
|
34024
|
+
level: "error",
|
|
34025
|
+
message: `Error sending user ${reviewStatus} email: ${error}`
|
|
34026
|
+
});
|
|
34027
|
+
});
|
|
34028
|
+
await _updateUserFieldById(
|
|
34029
|
+
{ _id: person.user.toString(), field: "status", value: userStatus },
|
|
34030
|
+
session
|
|
34031
|
+
);
|
|
34032
|
+
}
|
|
34033
|
+
await session?.commitTransaction();
|
|
34034
|
+
return `Successfully ${reviewStatus} person`;
|
|
34035
|
+
} catch (error) {
|
|
34036
|
+
await session?.abortTransaction();
|
|
34037
|
+
throw error;
|
|
34038
|
+
} finally {
|
|
34039
|
+
session?.endSession();
|
|
34040
|
+
}
|
|
34041
|
+
}
|
|
34042
|
+
async function suspendResidentById(id) {
|
|
34043
|
+
const session = useAtlas50.getClient()?.startSession();
|
|
34044
|
+
if (!session) {
|
|
34045
|
+
throw new BadRequestError100("Database session not available.");
|
|
34046
|
+
}
|
|
34047
|
+
try {
|
|
34048
|
+
session.startTransaction();
|
|
34049
|
+
const person = await _getByUserId(id.toString());
|
|
34050
|
+
if (!person)
|
|
34051
|
+
throw new NotFoundError23("Person not found.");
|
|
34052
|
+
await _updateById(person._id, { status: "deleted" }, session);
|
|
34053
|
+
if (person.user) {
|
|
34054
|
+
await _updateUserFieldById(
|
|
34055
|
+
{ _id: person.user.toString(), field: "status", value: "suspended" },
|
|
34056
|
+
session
|
|
34057
|
+
);
|
|
34058
|
+
}
|
|
34059
|
+
await session.commitTransaction();
|
|
34060
|
+
return "Resident suspended successfully.";
|
|
34061
|
+
} catch (error) {
|
|
34062
|
+
await session.abortTransaction();
|
|
34063
|
+
throw error;
|
|
34064
|
+
} finally {
|
|
34065
|
+
session.endSession();
|
|
34066
|
+
}
|
|
34067
|
+
}
|
|
34068
|
+
async function addUpdateFromVisitor(value, session) {
|
|
34069
|
+
value.type = value.type == "walk-in" ? "guest" : value.type;
|
|
34070
|
+
let updateId = null;
|
|
34071
|
+
let add2 = true;
|
|
34072
|
+
let set = {};
|
|
34073
|
+
const cleanInput = (str) => str.replace(/\s+/g, "");
|
|
34074
|
+
const cleanPhone = (str) => str.replace(/\D/g, "");
|
|
34075
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
34076
|
+
const nric = value.nric ? cleanInput(value.nric) : "";
|
|
34077
|
+
const name = value.name?.trim();
|
|
34078
|
+
const contact = value.contact?.trim();
|
|
34079
|
+
const plateNumber = value.plateNumber ? cleanInput(value.plateNumber) : "";
|
|
34080
|
+
const email = value.email?.trim();
|
|
34081
|
+
const type = value.type?.trim();
|
|
34082
|
+
const company = value.company?.trim();
|
|
34083
|
+
const nricQuery = nric ? { nric: { $regex: `^${escapeRegex(nric)}$`, $options: "i" } } : null;
|
|
34084
|
+
const nameQuery = name ? { name: { $regex: `^${escapeRegex(name)}$`, $options: "i" } } : null;
|
|
34085
|
+
let nricCondition = null;
|
|
34086
|
+
if (nric) {
|
|
34087
|
+
if (nric.length < 9) {
|
|
34088
|
+
if (nameQuery) {
|
|
34089
|
+
nricCondition = { $and: [nricQuery, nameQuery] };
|
|
34090
|
+
}
|
|
34091
|
+
} else {
|
|
34092
|
+
nricCondition = nricQuery;
|
|
34093
|
+
}
|
|
34094
|
+
}
|
|
34095
|
+
const orConditions = [
|
|
34096
|
+
...nricCondition ? [nricCondition] : [],
|
|
34097
|
+
...contact ? [{ contacts: { $regex: `^\\+?${cleanPhone(escapeRegex(contact))}$`, $options: "i" } }] : [],
|
|
34098
|
+
...plateNumber ? [{ plateNumbers: { $regex: `^${escapeRegex(plateNumber)}$`, $options: "i" } }] : []
|
|
34099
|
+
];
|
|
34100
|
+
const existingPerson2 = await findFirstTen(
|
|
34101
|
+
{
|
|
34102
|
+
type,
|
|
34103
|
+
site: value.site,
|
|
34104
|
+
...orConditions.length > 0 && { $or: orConditions }
|
|
34105
|
+
},
|
|
34106
|
+
session
|
|
34107
|
+
);
|
|
34108
|
+
if (Array.isArray(existingPerson2) && existingPerson2.length > 0) {
|
|
34109
|
+
let matchedPerson = nric ? existingPerson2.find((person) => person.nric === nric) : null;
|
|
34110
|
+
if (matchedPerson) {
|
|
34111
|
+
add2 = false;
|
|
34112
|
+
updateId = matchedPerson._id;
|
|
34113
|
+
if (contact && Array.isArray(matchedPerson.contacts) && !matchedPerson.contacts.includes(contact)) {
|
|
34114
|
+
set = { contacts: [...matchedPerson.contacts, contact] };
|
|
34115
|
+
}
|
|
34116
|
+
if (plateNumber && Array.isArray(matchedPerson.plateNumbers) && !matchedPerson.plateNumbers.includes(plateNumber)) {
|
|
34117
|
+
set = { ...set, plateNumbers: [...matchedPerson.plateNumbers, plateNumber] };
|
|
34118
|
+
}
|
|
34119
|
+
if (company && Array.isArray(matchedPerson.companyName) && !matchedPerson.companyName.includes(company)) {
|
|
34120
|
+
set = { ...set, companyName: [...matchedPerson.companyName, company] };
|
|
34121
|
+
}
|
|
34122
|
+
if (name && name != matchedPerson.name) {
|
|
34123
|
+
set = { ...set, name };
|
|
34124
|
+
}
|
|
34125
|
+
if (email && email != matchedPerson.email) {
|
|
34126
|
+
set = { ...set, email };
|
|
34127
|
+
}
|
|
34128
|
+
} else {
|
|
34129
|
+
matchedPerson = contact ? existingPerson2.find((person) => Array.isArray(person.contacts) && person.contacts.includes(contact)) : null;
|
|
34130
|
+
}
|
|
34131
|
+
if (matchedPerson) {
|
|
34132
|
+
add2 = false;
|
|
34133
|
+
updateId = matchedPerson._id;
|
|
34134
|
+
if (nric && nric !== matchedPerson.nric) {
|
|
34135
|
+
set = { ...set, nric };
|
|
34136
|
+
}
|
|
34137
|
+
if (plateNumber && Array.isArray(matchedPerson.plateNumbers) && !matchedPerson.plateNumbers.includes(plateNumber)) {
|
|
34138
|
+
set = { ...set, plateNumbers: [...matchedPerson.plateNumbers, plateNumber] };
|
|
34139
|
+
}
|
|
34140
|
+
if (company && Array.isArray(matchedPerson.companyName) && !matchedPerson.companyName.includes(company)) {
|
|
34141
|
+
set = { ...set, companyName: [...matchedPerson.companyName, company] };
|
|
34142
|
+
}
|
|
34143
|
+
if (name && name !== matchedPerson.name) {
|
|
34144
|
+
set = { ...set, name };
|
|
34145
|
+
}
|
|
34146
|
+
if (email && email !== matchedPerson.email) {
|
|
34147
|
+
set = { ...set, email };
|
|
34148
|
+
}
|
|
34149
|
+
} else {
|
|
34150
|
+
matchedPerson = plateNumber ? existingPerson2.find((person) => Array.isArray(person.plateNumbers) && person.plateNumbers.includes(plateNumber)) : null;
|
|
34151
|
+
}
|
|
34152
|
+
if (matchedPerson && (contact && Array.isArray(matchedPerson.contacts) && !matchedPerson.contacts.includes(contact) && (nric && nric != matchedPerson.nric) || !nric && !contact)) {
|
|
34153
|
+
add2 = false;
|
|
34154
|
+
updateId = matchedPerson._id;
|
|
34155
|
+
if (nric && nric !== matchedPerson.nric) {
|
|
34156
|
+
set = { ...set, nric };
|
|
34157
|
+
}
|
|
34158
|
+
if (contact && Array.isArray(matchedPerson.contacts) && !matchedPerson.contacts.includes(contact)) {
|
|
34159
|
+
set = { contacts: [...matchedPerson.contacts, contact] };
|
|
34160
|
+
}
|
|
34161
|
+
if (company && Array.isArray(matchedPerson.companyName) && !matchedPerson.companyName.includes(company)) {
|
|
34162
|
+
set = { ...set, companyName: [...matchedPerson.companyName, company] };
|
|
34163
|
+
}
|
|
34164
|
+
if (name && name !== matchedPerson.name) {
|
|
34165
|
+
set = { ...set, name };
|
|
34166
|
+
}
|
|
34167
|
+
if (email && email !== matchedPerson.email) {
|
|
34168
|
+
set = { ...set, email };
|
|
34169
|
+
}
|
|
34170
|
+
}
|
|
34171
|
+
}
|
|
34172
|
+
if (add2) {
|
|
34173
|
+
const data = {
|
|
34174
|
+
nric: value.nric,
|
|
34175
|
+
name: value.name,
|
|
34176
|
+
contacts: value.contact ? [value.contact] : [],
|
|
34177
|
+
plateNumbers: value.plateNumber ? [value.plateNumber] : [],
|
|
34178
|
+
type,
|
|
34179
|
+
email: value.email,
|
|
34180
|
+
site: value.site,
|
|
34181
|
+
status: "active",
|
|
34182
|
+
companyName: value.company ? [value.company] : [],
|
|
34183
|
+
createdBy: value.updatedBy,
|
|
34184
|
+
updatedBy: value.updatedBy
|
|
34185
|
+
};
|
|
34186
|
+
const res_addFromVisitor = await _addFromVisitor(data, session);
|
|
34187
|
+
}
|
|
34188
|
+
if (updateId && Object.keys(set).length > 0) {
|
|
34189
|
+
set = { ...set, updatedBy: value.updatedBy, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
34190
|
+
const res_updateById = await _updateById(updateId, set, session);
|
|
34191
|
+
}
|
|
34192
|
+
}
|
|
34193
|
+
return {
|
|
34194
|
+
add,
|
|
34195
|
+
updateById,
|
|
34196
|
+
reviewResidentPerson,
|
|
34197
|
+
suspendResidentById,
|
|
34198
|
+
addUpdateFromVisitor
|
|
34199
|
+
};
|
|
34200
|
+
}
|
|
34201
|
+
|
|
34202
|
+
// src/repositories/key.repo.ts
|
|
34203
|
+
import { ObjectId as ObjectId63 } from "mongodb";
|
|
34204
|
+
|
|
34205
|
+
// src/utils/mongo.ts
|
|
34206
|
+
import { useAtlas as useAtlas51 } from "@7365admin1/node-server-utils";
|
|
34207
|
+
var getDB = () => {
|
|
34208
|
+
const db = useAtlas51.getDb();
|
|
34209
|
+
if (!db) {
|
|
34210
|
+
throw new Error("Database not initialized! Check if useAtlas.initialize finished.");
|
|
34211
|
+
}
|
|
34212
|
+
return db;
|
|
34213
|
+
};
|
|
34214
|
+
|
|
34215
|
+
// src/models/key.model.ts
|
|
34216
|
+
import { ObjectId as ObjectId62 } from "mongodb";
|
|
34217
|
+
var MKeyLog = class {
|
|
34218
|
+
constructor({
|
|
34219
|
+
_id = new ObjectId62(),
|
|
34220
|
+
name = "",
|
|
34221
|
+
codes = [],
|
|
34222
|
+
passType = "",
|
|
34223
|
+
passId = "",
|
|
34224
|
+
code = "",
|
|
34225
|
+
// isTaken = false,
|
|
34226
|
+
site = "",
|
|
34227
|
+
organization = "",
|
|
34228
|
+
serviceProvider = "",
|
|
34229
|
+
createdAt = /* @__PURE__ */ new Date(),
|
|
34230
|
+
updatedAt = /* @__PURE__ */ new Date(),
|
|
34231
|
+
template,
|
|
34232
|
+
status = "Available",
|
|
34233
|
+
updatedBy = "",
|
|
34234
|
+
qty,
|
|
34235
|
+
description,
|
|
34236
|
+
location,
|
|
34237
|
+
prefix,
|
|
34238
|
+
keyNo,
|
|
34239
|
+
parentId
|
|
34240
|
+
} = {}) {
|
|
34241
|
+
this._id = _id;
|
|
34242
|
+
this.name = name;
|
|
34243
|
+
this.codes = codes;
|
|
34244
|
+
this.passType = passType;
|
|
34245
|
+
this.passId = passId;
|
|
34246
|
+
this.code = code;
|
|
34247
|
+
this.site = site;
|
|
34248
|
+
this.organization = organization;
|
|
34249
|
+
this.serviceProvider = serviceProvider;
|
|
34250
|
+
this.createdAt = createdAt;
|
|
34251
|
+
this.updatedAt = updatedAt;
|
|
34252
|
+
this.template = template;
|
|
34253
|
+
this.status = status;
|
|
34254
|
+
this.updatedBy = updatedBy;
|
|
34255
|
+
this.qty = qty;
|
|
34256
|
+
this.description = description;
|
|
34257
|
+
this.location = location;
|
|
34258
|
+
this.prefix = prefix;
|
|
34259
|
+
this.parentId = parentId;
|
|
34260
|
+
this.keyNo = keyNo;
|
|
34261
|
+
}
|
|
34262
|
+
};
|
|
34263
|
+
|
|
34264
|
+
// src/utils/pagination.util.ts
|
|
34265
|
+
function paginationUtil(items, page, limit, length) {
|
|
34266
|
+
const startIndex = length ? page * limit + 1 : 0;
|
|
34267
|
+
const endIndex = Math.min(startIndex + limit - 1, length);
|
|
34268
|
+
return {
|
|
34269
|
+
items,
|
|
34270
|
+
pages: Math.ceil(length / limit),
|
|
34271
|
+
pageRange: `${startIndex}-${endIndex} of ${length}`
|
|
34272
|
+
};
|
|
34273
|
+
}
|
|
34274
|
+
|
|
34275
|
+
// src/repositories/key.repo.ts
|
|
34276
|
+
var KeyRepo = class {
|
|
34277
|
+
static collection() {
|
|
34278
|
+
return getDB().collection("keys");
|
|
34279
|
+
}
|
|
34280
|
+
static collectionDynamic(collection) {
|
|
34281
|
+
return getDB().collection(collection);
|
|
34282
|
+
}
|
|
34283
|
+
static async getNextKeyNo(site) {
|
|
34284
|
+
try {
|
|
34285
|
+
site = await convertObjectIdUtil(site);
|
|
34286
|
+
return await this.collection().countDocuments({
|
|
34287
|
+
site,
|
|
34288
|
+
passType: "pass-key",
|
|
34289
|
+
parentId: { $not: { $type: "objectId" } }
|
|
34290
|
+
});
|
|
34291
|
+
} catch (error) {
|
|
34292
|
+
return Promise.reject("Server internal error. " + error);
|
|
34293
|
+
}
|
|
34294
|
+
}
|
|
34295
|
+
static async getKeyWithSequenceNumber(site, _id) {
|
|
34296
|
+
try {
|
|
34297
|
+
site = await convertObjectIdUtil(site);
|
|
34298
|
+
const results = await this.collection().find({
|
|
34299
|
+
site,
|
|
34300
|
+
passType: "pass-key",
|
|
34301
|
+
parentId: { $not: { $type: "objectId" } }
|
|
34302
|
+
}).sort({ createdAt: 1 }).toArray();
|
|
34303
|
+
for (let i = 0; i < results.length; i++) {
|
|
34304
|
+
if (results[i]._id.toString() == _id) {
|
|
34305
|
+
results[i].keyNo = (i + 1).toString().padStart(2, "0");
|
|
34306
|
+
return results[i];
|
|
34307
|
+
}
|
|
34308
|
+
}
|
|
34309
|
+
return Promise.reject("Key Not Found");
|
|
34310
|
+
} catch (error) {
|
|
34311
|
+
return Promise.reject("Server internal error. " + error);
|
|
34312
|
+
}
|
|
34313
|
+
}
|
|
34314
|
+
static async countByTemplateId(template, passType, site) {
|
|
34315
|
+
try {
|
|
34316
|
+
template = await convertObjectIdUtil(template);
|
|
34317
|
+
site = await convertObjectIdUtil(site);
|
|
34318
|
+
let passTypeQuery = { passType };
|
|
34319
|
+
if (passType != "pass-key")
|
|
34320
|
+
passTypeQuery = {
|
|
34321
|
+
passType: { $in: ["visitor-pass", "contractor-pass", "agent-pass"] }
|
|
34322
|
+
};
|
|
34323
|
+
const query = [
|
|
34324
|
+
{ $match: { template, ...passTypeQuery, site } },
|
|
34325
|
+
{ $addFields: { nameAsNumber: { $toDouble: "$name" } } },
|
|
34326
|
+
{ $sort: { nameAsNumber: -1 } },
|
|
34327
|
+
{ $limit: 1 },
|
|
34328
|
+
{ $project: { nameAsNumber: 1 } }
|
|
34329
|
+
];
|
|
34330
|
+
const result = await this.collection().aggregate(query).toArray();
|
|
34331
|
+
const highestName = result.length > 0 && result[0].nameAsNumber > 0 ? result[0].nameAsNumber : 0;
|
|
34332
|
+
return highestName;
|
|
34333
|
+
} catch (error) {
|
|
34334
|
+
return Promise.reject("Server internal error. " + error);
|
|
34335
|
+
}
|
|
34336
|
+
}
|
|
34337
|
+
static async createIndexes() {
|
|
34338
|
+
try {
|
|
34339
|
+
await this.collection().createIndex(
|
|
34340
|
+
{ passType: "text", name: "text" },
|
|
34341
|
+
{ name: "key_search" }
|
|
34342
|
+
);
|
|
34343
|
+
return Promise.resolve("User indexes created.");
|
|
34344
|
+
} catch (error) {
|
|
34345
|
+
return Promise.reject("Failed to create user indexes.");
|
|
34346
|
+
}
|
|
34347
|
+
}
|
|
34348
|
+
static async createKeyLog(keyLog, session) {
|
|
34349
|
+
try {
|
|
34350
|
+
if (keyLog.site)
|
|
34351
|
+
keyLog.site = await convertObjectIdUtil(keyLog.site);
|
|
34352
|
+
if (keyLog.parentId)
|
|
34353
|
+
keyLog.parentId = await convertObjectIdUtil(keyLog.parentId);
|
|
34354
|
+
if (keyLog.organization)
|
|
34355
|
+
keyLog.organization = await convertObjectIdUtil(keyLog.organization);
|
|
34356
|
+
if (keyLog.serviceProvider)
|
|
34357
|
+
keyLog.serviceProvider = await convertObjectIdUtil(
|
|
34358
|
+
keyLog.serviceProvider
|
|
34359
|
+
);
|
|
34360
|
+
if (keyLog.template)
|
|
34361
|
+
keyLog.template = await convertObjectIdUtil(keyLog.template);
|
|
34362
|
+
if (keyLog.updatedBy)
|
|
34363
|
+
keyLog.updatedBy = await convertObjectIdUtil(keyLog.updatedBy);
|
|
34364
|
+
return await this.collection().insertOne(new MKeyLog(keyLog), {
|
|
34365
|
+
session
|
|
34366
|
+
});
|
|
34367
|
+
} catch (error) {
|
|
34368
|
+
return Promise.reject(error || error.message || "Server internal error.");
|
|
34369
|
+
}
|
|
34370
|
+
}
|
|
34371
|
+
static async getByPageSearch2(query, pagination, search, prefixAndName, sortBy) {
|
|
34372
|
+
try {
|
|
34373
|
+
let { page, limit } = pagination;
|
|
34374
|
+
page = page ? page - 1 : 0;
|
|
34375
|
+
limit = limit ? limit : 10;
|
|
34376
|
+
const pipeline = [
|
|
34377
|
+
{
|
|
34378
|
+
$match: query
|
|
34379
|
+
},
|
|
34380
|
+
{
|
|
34381
|
+
$lookup: {
|
|
34382
|
+
from: "qr-code-templates",
|
|
34383
|
+
localField: "template",
|
|
34384
|
+
foreignField: "_id",
|
|
34385
|
+
as: "QRTemplateInfo"
|
|
34386
|
+
}
|
|
34387
|
+
},
|
|
34388
|
+
{
|
|
34389
|
+
$unwind: {
|
|
34390
|
+
path: "$QRTemplateInfo",
|
|
34391
|
+
preserveNullAndEmptyArrays: true
|
|
34392
|
+
}
|
|
34393
|
+
},
|
|
34394
|
+
{
|
|
34395
|
+
$addFields: {
|
|
34396
|
+
prefixPass: { $toUpper: "$QRTemplateInfo.prefixPass" },
|
|
34397
|
+
prefixKey: { $toUpper: "$QRTemplateInfo.prefixKey" },
|
|
34398
|
+
templateName: "$QRTemplateInfo.name"
|
|
34399
|
+
}
|
|
34400
|
+
},
|
|
34401
|
+
{
|
|
34402
|
+
$addFields: {
|
|
34403
|
+
prefixAndName: {
|
|
34404
|
+
$cond: {
|
|
34405
|
+
if: { $or: [{ $not: "$prefixPass" }, { $not: "$prefixKey" }] },
|
|
34406
|
+
then: "$name",
|
|
34407
|
+
else: {
|
|
34408
|
+
$cond: {
|
|
34409
|
+
if: { $eq: ["$passType", "pass-key"] },
|
|
34410
|
+
then: { $concat: ["$prefixKey", "$name"] },
|
|
34411
|
+
else: { $concat: ["$prefixPass", "$name"] }
|
|
34412
|
+
}
|
|
34413
|
+
}
|
|
34414
|
+
}
|
|
34415
|
+
}
|
|
34416
|
+
}
|
|
34417
|
+
},
|
|
34418
|
+
...search ? [
|
|
34419
|
+
{
|
|
34420
|
+
$match: {
|
|
34421
|
+
$or: [
|
|
34422
|
+
{ prefixAndName: { $regex: search, $options: "i" } },
|
|
34423
|
+
{ location: { $regex: search, $options: "i" } },
|
|
34424
|
+
{ description: { $regex: search, $options: "i" } }
|
|
34425
|
+
]
|
|
34426
|
+
}
|
|
34427
|
+
}
|
|
34428
|
+
] : [],
|
|
34429
|
+
...prefixAndName ? [
|
|
34430
|
+
{
|
|
34431
|
+
$match: {
|
|
34432
|
+
prefixAndName: {
|
|
34433
|
+
$regex: `^${prefixAndName}$`,
|
|
34434
|
+
$options: "i"
|
|
34435
|
+
}
|
|
34436
|
+
}
|
|
34437
|
+
}
|
|
34438
|
+
] : [],
|
|
34439
|
+
{
|
|
34440
|
+
$project: {
|
|
34441
|
+
QRTemplateInfo: 0
|
|
34442
|
+
}
|
|
34443
|
+
},
|
|
34444
|
+
{
|
|
34445
|
+
$addFields: {
|
|
34446
|
+
prefixOnly: {
|
|
34447
|
+
$toUpper: {
|
|
34448
|
+
$ifNull: [
|
|
34449
|
+
{
|
|
34450
|
+
$getField: {
|
|
34451
|
+
field: "match",
|
|
34452
|
+
input: {
|
|
34453
|
+
$regexFind: {
|
|
34454
|
+
input: "$prefixAndName",
|
|
34455
|
+
regex: "^[A-Za-z]+"
|
|
34456
|
+
}
|
|
34457
|
+
}
|
|
34458
|
+
}
|
|
34459
|
+
},
|
|
34460
|
+
""
|
|
34461
|
+
]
|
|
34462
|
+
}
|
|
34463
|
+
},
|
|
34464
|
+
numericSortValue: {
|
|
34465
|
+
$toInt: {
|
|
34466
|
+
$ifNull: [
|
|
34467
|
+
{
|
|
34468
|
+
$getField: {
|
|
34469
|
+
field: "match",
|
|
34470
|
+
input: {
|
|
34471
|
+
$regexFind: {
|
|
34472
|
+
input: "$prefixAndName",
|
|
34473
|
+
regex: "\\d+$"
|
|
34474
|
+
}
|
|
34475
|
+
}
|
|
34476
|
+
}
|
|
34477
|
+
},
|
|
34478
|
+
"0"
|
|
34479
|
+
]
|
|
34480
|
+
}
|
|
34481
|
+
}
|
|
33884
34482
|
}
|
|
33885
34483
|
},
|
|
33886
34484
|
{
|
|
@@ -34215,7 +34813,7 @@ var KeyRepo = class {
|
|
|
34215
34813
|
}
|
|
34216
34814
|
static async getById(_id) {
|
|
34217
34815
|
try {
|
|
34218
|
-
_id = new
|
|
34816
|
+
_id = new ObjectId63(_id);
|
|
34219
34817
|
} catch (error) {
|
|
34220
34818
|
return Promise.reject("Invalid key id");
|
|
34221
34819
|
}
|
|
@@ -34456,7 +35054,7 @@ var KeyRepo = class {
|
|
|
34456
35054
|
updatedDocs.forEach((item) => {
|
|
34457
35055
|
let keyItem = key;
|
|
34458
35056
|
keyItem["passOrKeyId"] = item?._id;
|
|
34459
|
-
keyItem["_id"] = new
|
|
35057
|
+
keyItem["_id"] = new ObjectId63();
|
|
34460
35058
|
keyItem["previousStatus"] = item?.status;
|
|
34461
35059
|
if (visitorId) {
|
|
34462
35060
|
console.log("visitorId true key.repo");
|
|
@@ -34481,7 +35079,7 @@ var KeyRepo = class {
|
|
|
34481
35079
|
}
|
|
34482
35080
|
}
|
|
34483
35081
|
static async deleteKeyById(_id, session) {
|
|
34484
|
-
_id = new
|
|
35082
|
+
_id = new ObjectId63(_id);
|
|
34485
35083
|
try {
|
|
34486
35084
|
await this.collection().findOne({ _id }, { session });
|
|
34487
35085
|
} catch (error) {
|
|
@@ -34496,7 +35094,7 @@ var KeyRepo = class {
|
|
|
34496
35094
|
static async getById2(id, session) {
|
|
34497
35095
|
return this.collection().aggregate(
|
|
34498
35096
|
[
|
|
34499
|
-
{ $match: { _id: new
|
|
35097
|
+
{ $match: { _id: new ObjectId63(id) } },
|
|
34500
35098
|
{
|
|
34501
35099
|
$lookup: {
|
|
34502
35100
|
from: "qr-code-templates",
|
|
@@ -34790,7 +35388,7 @@ function useVisitorTransactionService() {
|
|
|
34790
35388
|
from: MAILER_FROM_EMAIL,
|
|
34791
35389
|
password: MAILER_PASSWORD
|
|
34792
35390
|
};
|
|
34793
|
-
const mailer = new
|
|
35391
|
+
const mailer = new useMailer3(MailerConfig);
|
|
34794
35392
|
const {
|
|
34795
35393
|
add: _add,
|
|
34796
35394
|
updateById: _updateVisitorTansactionById,
|
|
@@ -34804,6 +35402,7 @@ function useVisitorTransactionService() {
|
|
|
34804
35402
|
getByNRIC,
|
|
34805
35403
|
updateById: _updateById
|
|
34806
35404
|
} = usePersonRepo();
|
|
35405
|
+
const { addUpdateFromVisitor } = usePersonService();
|
|
34807
35406
|
const { getBySite: _getBySite, getVisitorsInBySite: _getVisitorsInBySite } = useSiteCameraRepo();
|
|
34808
35407
|
const { getSiteById: _getSiteById } = useSiteRepo();
|
|
34809
35408
|
const {
|
|
@@ -34824,15 +35423,15 @@ function useVisitorTransactionService() {
|
|
|
34824
35423
|
if (!item)
|
|
34825
35424
|
return null;
|
|
34826
35425
|
if (typeof item === "string") {
|
|
34827
|
-
return
|
|
35426
|
+
return ObjectId64.isValid(item) ? item : null;
|
|
34828
35427
|
}
|
|
34829
35428
|
if (typeof item === "object" && item !== null) {
|
|
34830
35429
|
if ("keyId" in item) {
|
|
34831
35430
|
const keyId = String(item.keyId);
|
|
34832
|
-
return
|
|
35431
|
+
return ObjectId64.isValid(keyId) ? keyId : null;
|
|
34833
35432
|
}
|
|
34834
35433
|
const value = String(item);
|
|
34835
|
-
return
|
|
35434
|
+
return ObjectId64.isValid(value) ? value : null;
|
|
34836
35435
|
}
|
|
34837
35436
|
return null;
|
|
34838
35437
|
}
|
|
@@ -34846,12 +35445,12 @@ function useVisitorTransactionService() {
|
|
|
34846
35445
|
if (!keyId || seen.has(keyId))
|
|
34847
35446
|
continue;
|
|
34848
35447
|
seen.add(keyId);
|
|
34849
|
-
normalized.push({ keyId: new
|
|
35448
|
+
normalized.push({ keyId: new ObjectId64(keyId) });
|
|
34850
35449
|
}
|
|
34851
35450
|
return normalized;
|
|
34852
35451
|
}
|
|
34853
35452
|
async function add(value) {
|
|
34854
|
-
const session =
|
|
35453
|
+
const session = useAtlas52.getClient()?.startSession();
|
|
34855
35454
|
const allowedPersonTypes = [
|
|
34856
35455
|
"contractor" /* CONTRACTOR */,
|
|
34857
35456
|
"walk-in" /* WALK_IN */,
|
|
@@ -34867,272 +35466,50 @@ function useVisitorTransactionService() {
|
|
|
34867
35466
|
value.plateNumber
|
|
34868
35467
|
);
|
|
34869
35468
|
if (result2)
|
|
34870
|
-
throw new
|
|
35469
|
+
throw new BadRequestError101("Plate number is blocklisted");
|
|
34871
35470
|
}
|
|
34872
35471
|
let camera;
|
|
34873
35472
|
if (value.site && value.plateNumber) {
|
|
34874
35473
|
try {
|
|
34875
35474
|
camera = await _getVisitorsInBySite(value.site);
|
|
34876
35475
|
if (!camera) {
|
|
34877
|
-
|
|
35476
|
+
logger81.warn(`No camera document found for site: ${value.site}`);
|
|
34878
35477
|
}
|
|
34879
35478
|
} catch (error) {
|
|
34880
|
-
|
|
35479
|
+
logger81.error(
|
|
34881
35480
|
`Error _getVisitorsInBySite camera for site ${value.site}:`,
|
|
34882
35481
|
error
|
|
34883
|
-
);
|
|
34884
|
-
}
|
|
34885
|
-
}
|
|
34886
|
-
if (!value.unitName && value.unit) {
|
|
34887
|
-
const unit = await _getUnitById(value.unit);
|
|
34888
|
-
value.unitName = unit?.name;
|
|
34889
|
-
}
|
|
34890
|
-
if (allowedPersonTypes.includes(value?.type) && value?.nric) {
|
|
34891
|
-
const nric = value?.nric || "";
|
|
34892
|
-
const person = await getByNRIC(nric);
|
|
34893
|
-
const existingCompanyName = person?.companyName?.includes(
|
|
34894
|
-
value.company || ""
|
|
34895
|
-
);
|
|
34896
|
-
if (person && !existingCompanyName) {
|
|
34897
|
-
const updatedCompanyNames = value.company && !existingCompanyName ? [...person.companyName || [], value.company] : person.companyName;
|
|
34898
|
-
const updatePayload = {
|
|
34899
|
-
companyName: updatedCompanyNames
|
|
34900
|
-
};
|
|
34901
|
-
await _updateById(person._id, updatePayload, session);
|
|
34902
|
-
} else {
|
|
34903
|
-
const payload = {
|
|
34904
|
-
name: value.name || "",
|
|
34905
|
-
contact: value.contact ?? "",
|
|
34906
|
-
nric,
|
|
34907
|
-
type: value.type,
|
|
34908
|
-
companyName: value.company ? [value.company] : [],
|
|
34909
|
-
block: value.block ?? "",
|
|
34910
|
-
level: value?.level ?? "",
|
|
34911
|
-
unit: value?.unit?.toString() || "",
|
|
34912
|
-
org: value?.org ?? "",
|
|
34913
|
-
site: value?.site ?? "",
|
|
34914
|
-
plates: value.plateNumber ? [{ plateNumber: value.plateNumber, recNo: "" }] : [],
|
|
34915
|
-
isOwner: false
|
|
34916
|
-
};
|
|
34917
|
-
await _addPerson(payload, session);
|
|
34918
|
-
}
|
|
34919
|
-
}
|
|
34920
|
-
if (Array.isArray(value.members) && value.members.length > 0) {
|
|
34921
|
-
const chunkSize = 10;
|
|
34922
|
-
const {
|
|
34923
|
-
block,
|
|
34924
|
-
level,
|
|
34925
|
-
unit,
|
|
34926
|
-
site,
|
|
34927
|
-
org,
|
|
34928
|
-
type,
|
|
34929
|
-
company,
|
|
34930
|
-
remarks,
|
|
34931
|
-
contractorType,
|
|
34932
|
-
unitName
|
|
34933
|
-
} = value;
|
|
34934
|
-
for (let i = 0; i < value.members.length; i += chunkSize) {
|
|
34935
|
-
const chunk = value.members.slice(i, i + chunkSize);
|
|
34936
|
-
await Promise.all(
|
|
34937
|
-
chunk.map(async (member) => {
|
|
34938
|
-
const clonedMember = structuredClone(member);
|
|
34939
|
-
const { visitorPass, passKeys } = clonedMember;
|
|
34940
|
-
const preparedVisitorPass = Array.isArray(visitorPass) ? visitorPass.map((item) => ({
|
|
34941
|
-
...item,
|
|
34942
|
-
receivedDate: /* @__PURE__ */ new Date(),
|
|
34943
|
-
status: "Not Returned" /* NOT_RETURNED */,
|
|
34944
|
-
lastUpdate: null,
|
|
34945
|
-
remarks: ""
|
|
34946
|
-
})) : [];
|
|
34947
|
-
const preparedPassKeys = Array.isArray(passKeys) ? passKeys.map((item) => ({
|
|
34948
|
-
...item,
|
|
34949
|
-
receivedDate: /* @__PURE__ */ new Date(),
|
|
34950
|
-
status: "Not Returned" /* NOT_RETURNED */,
|
|
34951
|
-
lastUpdate: null,
|
|
34952
|
-
remarks: ""
|
|
34953
|
-
})) : [];
|
|
34954
|
-
const visitorId = await _add(
|
|
34955
|
-
{
|
|
34956
|
-
...clonedMember,
|
|
34957
|
-
block,
|
|
34958
|
-
level,
|
|
34959
|
-
unit,
|
|
34960
|
-
unitName,
|
|
34961
|
-
site,
|
|
34962
|
-
org,
|
|
34963
|
-
type,
|
|
34964
|
-
company,
|
|
34965
|
-
remarks,
|
|
34966
|
-
contractorType,
|
|
34967
|
-
checkIn: start,
|
|
34968
|
-
visitorPass: preparedVisitorPass,
|
|
34969
|
-
passKeys: preparedPassKeys,
|
|
34970
|
-
status: "registered" /* REGISTERED */
|
|
34971
|
-
},
|
|
34972
|
-
session
|
|
34973
|
-
);
|
|
34974
|
-
console.log("visitorId service", visitorId);
|
|
34975
|
-
for (const item of preparedVisitorPass) {
|
|
34976
|
-
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
|
|
34977
|
-
await KeyRepo.updateKeyById(
|
|
34978
|
-
item.keyId,
|
|
34979
|
-
{
|
|
34980
|
-
status: "In Use" /* IN_USE */,
|
|
34981
|
-
updatedBy: value.createdBy
|
|
34982
|
-
},
|
|
34983
|
-
value.site,
|
|
34984
|
-
session,
|
|
34985
|
-
void 0,
|
|
34986
|
-
visitorId,
|
|
34987
|
-
void 0,
|
|
34988
|
-
true
|
|
34989
|
-
);
|
|
34990
|
-
}
|
|
34991
|
-
for (const item of preparedPassKeys) {
|
|
34992
|
-
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
|
|
34993
|
-
await KeyRepo.updateKeyById(
|
|
34994
|
-
item.keyId,
|
|
34995
|
-
{
|
|
34996
|
-
status: "In Use" /* IN_USE */,
|
|
34997
|
-
updatedBy: value.createdBy,
|
|
34998
|
-
visitorId
|
|
34999
|
-
},
|
|
35000
|
-
value.site,
|
|
35001
|
-
session,
|
|
35002
|
-
void 0,
|
|
35003
|
-
visitorId,
|
|
35004
|
-
void 0,
|
|
35005
|
-
true
|
|
35006
|
-
);
|
|
35007
|
-
}
|
|
35008
|
-
})
|
|
35009
|
-
);
|
|
35010
|
-
}
|
|
35011
|
-
}
|
|
35012
|
-
let host;
|
|
35013
|
-
let username;
|
|
35014
|
-
let password;
|
|
35015
|
-
if (camera && value.plateNumber && camera.host && camera.username && camera.password) {
|
|
35016
|
-
host = camera.host;
|
|
35017
|
-
username = camera.username;
|
|
35018
|
-
password = camera.password;
|
|
35019
|
-
const blacklist = "TrafficBlackList" /* TRAFFIC_BLACKLIST */;
|
|
35020
|
-
const _plateNumber = value.plateNumber;
|
|
35021
|
-
const dahuaBlocklistQuery = {
|
|
35022
|
-
host,
|
|
35023
|
-
username,
|
|
35024
|
-
password,
|
|
35025
|
-
plateNumber: _plateNumber,
|
|
35026
|
-
mode: blacklist
|
|
35027
|
-
};
|
|
35028
|
-
const isBlocklistedRaw = await _getPlateNumber(dahuaBlocklistQuery);
|
|
35029
|
-
const rawString = isBlocklistedRaw?.toString?.("utf-8") ?? String(isBlocklistedRaw);
|
|
35030
|
-
const foundMatch = rawString.match(/found=(\d+)/);
|
|
35031
|
-
const found = foundMatch ? Number(foundMatch[1]) : 0;
|
|
35032
|
-
if (found === 1)
|
|
35033
|
-
throw new BadRequestError100("This plate number is blocklisted");
|
|
35034
|
-
}
|
|
35035
|
-
if (Array.isArray(value.visitorPass)) {
|
|
35036
|
-
for (const item of value.visitorPass) {
|
|
35037
|
-
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35038
|
-
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35039
|
-
item.lastUpdate = null;
|
|
35040
|
-
item.remarks = "";
|
|
35041
|
-
}
|
|
35042
|
-
}
|
|
35043
|
-
if (Array.isArray(value.passKeys)) {
|
|
35044
|
-
for (const item of value.passKeys) {
|
|
35045
|
-
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35046
|
-
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35047
|
-
item.lastUpdate = null;
|
|
35048
|
-
item.remarks = "";
|
|
35049
|
-
}
|
|
35050
|
-
}
|
|
35051
|
-
const result = await _add(value, session);
|
|
35052
|
-
if (Array.isArray(value.visitorPass)) {
|
|
35053
|
-
for (const item of value.visitorPass) {
|
|
35054
|
-
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
|
|
35055
|
-
await KeyRepo.updateKeyById(
|
|
35056
|
-
item.keyId,
|
|
35057
|
-
{ status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
|
|
35058
|
-
value.site,
|
|
35059
|
-
session,
|
|
35060
|
-
void 0,
|
|
35061
|
-
result,
|
|
35062
|
-
void 0,
|
|
35063
|
-
true
|
|
35064
|
-
);
|
|
35065
|
-
}
|
|
35066
|
-
}
|
|
35067
|
-
if (Array.isArray(value.passKeys)) {
|
|
35068
|
-
for (const item of value.passKeys) {
|
|
35069
|
-
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
|
|
35070
|
-
await KeyRepo.updateKeyById(
|
|
35071
|
-
item.keyId,
|
|
35072
|
-
{ status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
|
|
35073
|
-
value.site,
|
|
35074
|
-
session,
|
|
35075
|
-
void 0,
|
|
35076
|
-
result,
|
|
35077
|
-
void 0,
|
|
35078
|
-
true
|
|
35079
|
-
);
|
|
35080
|
-
}
|
|
35081
|
-
}
|
|
35082
|
-
await session?.commitTransaction();
|
|
35083
|
-
let openBarrier = null;
|
|
35084
|
-
const isOpenBarrier = allowedPersonTypes.includes(value?.type) || camera?.ANPRSwitches?.openBarrierPickUpDropOff == true;
|
|
35085
|
-
if (isOpenBarrier && value.plateNumber && host && username && password) {
|
|
35086
|
-
try {
|
|
35087
|
-
logger80.info(`Attempting to open barrier at host: ${host}`);
|
|
35088
|
-
openBarrier = await _openBarrier({
|
|
35089
|
-
host,
|
|
35090
|
-
username,
|
|
35091
|
-
password,
|
|
35092
|
-
plateNumber: value?.plateNumber
|
|
35093
|
-
});
|
|
35094
|
-
logger80.info(`Barrier command sent successfully to ${host}`, {
|
|
35095
|
-
response: openBarrier
|
|
35096
|
-
});
|
|
35097
|
-
} catch (error) {
|
|
35098
|
-
logger80.error(
|
|
35099
|
-
`Failed to open barrier at host ${host}. Critical system error:`,
|
|
35100
|
-
error
|
|
35101
|
-
);
|
|
35102
|
-
openBarrier = { success: false, error: error?.message };
|
|
35103
|
-
}
|
|
35104
|
-
}
|
|
35105
|
-
console.log("Open barrier response:", openBarrier);
|
|
35106
|
-
return result;
|
|
35107
|
-
} catch (error) {
|
|
35108
|
-
await session?.abortTransaction();
|
|
35109
|
-
throw error;
|
|
35110
|
-
} finally {
|
|
35111
|
-
session?.endSession();
|
|
35112
|
-
}
|
|
35113
|
-
}
|
|
35114
|
-
async function updateVisitorTransactionById(id, value) {
|
|
35115
|
-
const session = useAtlas51.getClient()?.startSession();
|
|
35116
|
-
session?.startTransaction();
|
|
35117
|
-
try {
|
|
35118
|
-
let camera;
|
|
35119
|
-
let host;
|
|
35120
|
-
let username;
|
|
35121
|
-
let password;
|
|
35122
|
-
if (value.checkIn) {
|
|
35123
|
-
const parsed = new Date(value.checkIn);
|
|
35124
|
-
value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
|
|
35482
|
+
);
|
|
35483
|
+
}
|
|
35125
35484
|
}
|
|
35126
|
-
if (value.
|
|
35485
|
+
if (!value.unitName && value.unit) {
|
|
35486
|
+
const unit = await _getUnitById(value.unit);
|
|
35487
|
+
value.unitName = unit?.name;
|
|
35488
|
+
}
|
|
35489
|
+
const { nric = "", name = "", contact = "", plateNumber = "", email = "", site = "", type = "guest", company, createdBy } = value;
|
|
35490
|
+
if (/^(guest|walk-in|contractor|delivery)$/.test(type)) {
|
|
35491
|
+
await addUpdateFromVisitor({
|
|
35492
|
+
nric,
|
|
35493
|
+
name,
|
|
35494
|
+
contact,
|
|
35495
|
+
plateNumber,
|
|
35496
|
+
email,
|
|
35497
|
+
type,
|
|
35498
|
+
site,
|
|
35499
|
+
company,
|
|
35500
|
+
updatedBy: createdBy
|
|
35501
|
+
}, session);
|
|
35502
|
+
}
|
|
35503
|
+
if (Array.isArray(value.members) && value.members.length > 0) {
|
|
35127
35504
|
const chunkSize = 10;
|
|
35128
35505
|
const {
|
|
35129
35506
|
block,
|
|
35130
35507
|
level,
|
|
35131
35508
|
unit,
|
|
35132
|
-
site,
|
|
35509
|
+
site: site2,
|
|
35133
35510
|
org,
|
|
35134
|
-
type,
|
|
35135
|
-
company,
|
|
35511
|
+
type: type2,
|
|
35512
|
+
company: company2,
|
|
35136
35513
|
remarks,
|
|
35137
35514
|
contractorType,
|
|
35138
35515
|
unitName
|
|
@@ -35142,7 +35519,18 @@ function useVisitorTransactionService() {
|
|
|
35142
35519
|
await Promise.all(
|
|
35143
35520
|
chunk.map(async (member) => {
|
|
35144
35521
|
const clonedMember = structuredClone(member);
|
|
35145
|
-
const { visitorPass, passKeys } = clonedMember;
|
|
35522
|
+
const { visitorPass, passKeys, nric: nric2, name: name2, contact: contact2 } = clonedMember;
|
|
35523
|
+
if (/^(guest|walk-in|contractor|delivery)$/.test(type2 ?? "invalid") && site2) {
|
|
35524
|
+
await addUpdateFromVisitor({
|
|
35525
|
+
nric: nric2,
|
|
35526
|
+
name: name2,
|
|
35527
|
+
contact: contact2,
|
|
35528
|
+
type: type2,
|
|
35529
|
+
site: site2,
|
|
35530
|
+
company: company2,
|
|
35531
|
+
updatedBy: createdBy
|
|
35532
|
+
}, session);
|
|
35533
|
+
}
|
|
35146
35534
|
const preparedVisitorPass = Array.isArray(visitorPass) ? visitorPass.map((item) => ({
|
|
35147
35535
|
...item,
|
|
35148
35536
|
receivedDate: /* @__PURE__ */ new Date(),
|
|
@@ -35164,13 +35552,13 @@ function useVisitorTransactionService() {
|
|
|
35164
35552
|
level,
|
|
35165
35553
|
unit,
|
|
35166
35554
|
unitName,
|
|
35167
|
-
site,
|
|
35555
|
+
site: site2,
|
|
35168
35556
|
org,
|
|
35169
|
-
type,
|
|
35170
|
-
company,
|
|
35557
|
+
type: type2,
|
|
35558
|
+
company: company2,
|
|
35171
35559
|
remarks,
|
|
35172
35560
|
contractorType,
|
|
35173
|
-
checkIn:
|
|
35561
|
+
checkIn: start,
|
|
35174
35562
|
visitorPass: preparedVisitorPass,
|
|
35175
35563
|
passKeys: preparedPassKeys,
|
|
35176
35564
|
status: "registered" /* REGISTERED */
|
|
@@ -35214,21 +35602,10 @@ function useVisitorTransactionService() {
|
|
|
35214
35602
|
})
|
|
35215
35603
|
);
|
|
35216
35604
|
}
|
|
35217
|
-
value.isMembersAdded = true;
|
|
35218
|
-
}
|
|
35219
|
-
if (value.site && value.plateNumber) {
|
|
35220
|
-
try {
|
|
35221
|
-
camera = await _getVisitorsInBySite(value.site);
|
|
35222
|
-
if (!camera) {
|
|
35223
|
-
logger80.warn(`No camera document found for site: ${value.site}`);
|
|
35224
|
-
}
|
|
35225
|
-
} catch (error) {
|
|
35226
|
-
logger80.error(
|
|
35227
|
-
`Error _getVisitorsInBySite camera for site ${value.site}:`,
|
|
35228
|
-
error
|
|
35229
|
-
);
|
|
35230
|
-
}
|
|
35231
35605
|
}
|
|
35606
|
+
let host;
|
|
35607
|
+
let username;
|
|
35608
|
+
let password;
|
|
35232
35609
|
if (camera && value.plateNumber && camera.host && camera.username && camera.password) {
|
|
35233
35610
|
host = camera.host;
|
|
35234
35611
|
username = camera.username;
|
|
@@ -35247,1427 +35624,1109 @@ function useVisitorTransactionService() {
|
|
|
35247
35624
|
const foundMatch = rawString.match(/found=(\d+)/);
|
|
35248
35625
|
const found = foundMatch ? Number(foundMatch[1]) : 0;
|
|
35249
35626
|
if (found === 1)
|
|
35250
|
-
throw new
|
|
35251
|
-
}
|
|
35252
|
-
if (value.checkOut) {
|
|
35253
|
-
const parsed = new Date(value.checkOut);
|
|
35254
|
-
value.checkOut = isNaN(parsed.getTime()) ? null : parsed;
|
|
35255
|
-
}
|
|
35256
|
-
if (value.site) {
|
|
35257
|
-
value.site = typeof value.site === "string" ? new ObjectId63(value.site) : value.site;
|
|
35258
|
-
}
|
|
35259
|
-
if (value?.block) {
|
|
35260
|
-
value.block = await convertObjectIdUtil2(value.block, "block Id");
|
|
35261
|
-
}
|
|
35262
|
-
if (value?.level) {
|
|
35263
|
-
value.level = await convertObjectIdUtil2(value.level, "level Id");
|
|
35264
|
-
}
|
|
35265
|
-
if (value.unit) {
|
|
35266
|
-
value.unit = typeof value.unit === "string" ? new ObjectId63(value.unit) : value.unit;
|
|
35267
|
-
const unit = await _getUnitById(value.unit);
|
|
35268
|
-
value.unitName = unit?.name;
|
|
35269
|
-
}
|
|
35270
|
-
if (value.updatedBy) {
|
|
35271
|
-
value.updatedBy = await convertObjectIdUtil2(value.updatedBy, "updatedBy Id");
|
|
35627
|
+
throw new BadRequestError101("This plate number is blocklisted");
|
|
35272
35628
|
}
|
|
35273
35629
|
if (Array.isArray(value.visitorPass)) {
|
|
35274
35630
|
for (const item of value.visitorPass) {
|
|
35275
|
-
|
|
35276
|
-
|
|
35277
|
-
|
|
35278
|
-
|
|
35279
|
-
{
|
|
35280
|
-
status: "In Use" /* IN_USE */,
|
|
35281
|
-
updatedBy: value.updatedBy
|
|
35282
|
-
},
|
|
35283
|
-
value.site,
|
|
35284
|
-
session,
|
|
35285
|
-
void 0,
|
|
35286
|
-
id
|
|
35287
|
-
);
|
|
35288
|
-
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35289
|
-
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35290
|
-
item.lastUpdate = null;
|
|
35291
|
-
item.remarks = "";
|
|
35292
|
-
} else {
|
|
35293
|
-
let status = "Invalid";
|
|
35294
|
-
if (item?.status == "Returned" /* RETURNED */) {
|
|
35295
|
-
status = "Available" /* AVAILABLE */;
|
|
35296
|
-
} else if (item?.status == "Removed" /* REMOVED */) {
|
|
35297
|
-
status = "Available" /* AVAILABLE */;
|
|
35298
|
-
} else if (item?.status == "Not Returned" /* NOT_RETURNED */) {
|
|
35299
|
-
status = "In Use" /* IN_USE */;
|
|
35300
|
-
} else if (item?.status && VALID_STATUSES.has(item.status)) {
|
|
35301
|
-
status = item.status;
|
|
35302
|
-
} else {
|
|
35303
|
-
throw new Error("Invalid Visitor Pass Status");
|
|
35304
|
-
}
|
|
35305
|
-
await KeyRepo.updateKeyById(
|
|
35306
|
-
item.keyId,
|
|
35307
|
-
{
|
|
35308
|
-
status,
|
|
35309
|
-
updatedBy: value.updatedBy
|
|
35310
|
-
},
|
|
35311
|
-
value.site,
|
|
35312
|
-
session,
|
|
35313
|
-
void 0,
|
|
35314
|
-
id,
|
|
35315
|
-
true
|
|
35316
|
-
);
|
|
35317
|
-
delete item.receivedDate;
|
|
35318
|
-
item.lastUpdate = /* @__PURE__ */ new Date();
|
|
35319
|
-
}
|
|
35631
|
+
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35632
|
+
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35633
|
+
item.lastUpdate = null;
|
|
35634
|
+
item.remarks = "";
|
|
35320
35635
|
}
|
|
35321
35636
|
}
|
|
35322
35637
|
if (Array.isArray(value.passKeys)) {
|
|
35323
35638
|
for (const item of value.passKeys) {
|
|
35324
|
-
|
|
35325
|
-
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
{
|
|
35329
|
-
status: "In Use" /* IN_USE */,
|
|
35330
|
-
updatedBy: value.updatedBy
|
|
35331
|
-
},
|
|
35332
|
-
value.site,
|
|
35333
|
-
session,
|
|
35334
|
-
void 0,
|
|
35335
|
-
id
|
|
35336
|
-
);
|
|
35337
|
-
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35338
|
-
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35339
|
-
item.lastUpdate = null;
|
|
35340
|
-
item.remarks = "";
|
|
35341
|
-
} else {
|
|
35342
|
-
let status = "Invalid";
|
|
35343
|
-
if (item?.status == "Returned" /* RETURNED */) {
|
|
35344
|
-
status = "Available" /* AVAILABLE */;
|
|
35345
|
-
} else if (item?.status == "Removed" /* REMOVED */) {
|
|
35346
|
-
status = "Available" /* AVAILABLE */;
|
|
35347
|
-
} else if (item?.status == "Not Returned" /* NOT_RETURNED */) {
|
|
35348
|
-
status = "In Use" /* IN_USE */;
|
|
35349
|
-
} else if (item?.status && VALID_STATUSES.has(item.status)) {
|
|
35350
|
-
status = item.status;
|
|
35351
|
-
} else {
|
|
35352
|
-
throw new Error("Invalid Key Status");
|
|
35353
|
-
}
|
|
35354
|
-
await KeyRepo.updateKeyById(
|
|
35355
|
-
item.keyId,
|
|
35356
|
-
{
|
|
35357
|
-
status,
|
|
35358
|
-
updatedBy: value.updatedBy
|
|
35359
|
-
},
|
|
35360
|
-
value.site,
|
|
35361
|
-
session,
|
|
35362
|
-
void 0,
|
|
35363
|
-
id,
|
|
35364
|
-
true
|
|
35365
|
-
);
|
|
35366
|
-
delete item.receivedDate;
|
|
35367
|
-
item.lastUpdate = /* @__PURE__ */ new Date();
|
|
35368
|
-
}
|
|
35369
|
-
}
|
|
35370
|
-
}
|
|
35371
|
-
await _updateVisitorTansactionById(id, value, session);
|
|
35372
|
-
const allowedPersonTypes = [
|
|
35373
|
-
"contractor" /* CONTRACTOR */,
|
|
35374
|
-
"walk-in" /* WALK_IN */,
|
|
35375
|
-
"guest" /* GUEST */,
|
|
35376
|
-
"delivery" /* DELIVERY */
|
|
35377
|
-
// PersonTypes.PICK_UP,
|
|
35378
|
-
];
|
|
35379
|
-
let openBarrier = null;
|
|
35380
|
-
const isOpenBarrier = allowedPersonTypes.includes(value?.type) || camera?.ANPRSwitches?.openBarrierPickUpDropOff == true;
|
|
35381
|
-
if (isOpenBarrier && value.plateNumber && host && username && password) {
|
|
35382
|
-
try {
|
|
35383
|
-
logger80.info(`Attempting to open barrier at host: ${host}`);
|
|
35384
|
-
openBarrier = await _openBarrier({
|
|
35385
|
-
host,
|
|
35386
|
-
username,
|
|
35387
|
-
password,
|
|
35388
|
-
plateNumber: value?.plateNumber
|
|
35389
|
-
});
|
|
35390
|
-
logger80.info(`Barrier command sent successfully to ${host}`, {
|
|
35391
|
-
response: openBarrier
|
|
35392
|
-
});
|
|
35393
|
-
} catch (error) {
|
|
35394
|
-
logger80.error(
|
|
35395
|
-
`Failed to open barrier at host ${host}. Critical system error:`,
|
|
35396
|
-
error
|
|
35397
|
-
);
|
|
35398
|
-
openBarrier = { success: false, error: error?.message };
|
|
35639
|
+
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35640
|
+
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35641
|
+
item.lastUpdate = null;
|
|
35642
|
+
item.remarks = "";
|
|
35399
35643
|
}
|
|
35400
35644
|
}
|
|
35401
|
-
|
|
35402
|
-
|
|
35403
|
-
|
|
35404
|
-
|
|
35405
|
-
|
|
35406
|
-
|
|
35407
|
-
|
|
35408
|
-
|
|
35409
|
-
|
|
35410
|
-
|
|
35411
|
-
|
|
35412
|
-
|
|
35413
|
-
|
|
35414
|
-
const userIds = Array.from(
|
|
35415
|
-
new Set(users.map((item) => item.user.toString()))
|
|
35645
|
+
const result = await _add(value, session);
|
|
35646
|
+
if (Array.isArray(value.visitorPass)) {
|
|
35647
|
+
for (const item of value.visitorPass) {
|
|
35648
|
+
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
|
|
35649
|
+
await KeyRepo.updateKeyById(
|
|
35650
|
+
item.keyId,
|
|
35651
|
+
{ status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
|
|
35652
|
+
value.site,
|
|
35653
|
+
session,
|
|
35654
|
+
void 0,
|
|
35655
|
+
result,
|
|
35656
|
+
void 0,
|
|
35657
|
+
true
|
|
35416
35658
|
);
|
|
35417
|
-
NotificationService.invitedVisitorCheckOutForMa({
|
|
35418
|
-
to: userIds,
|
|
35419
|
-
name: visitorTransaction.name,
|
|
35420
|
-
_id: id.toString()
|
|
35421
|
-
});
|
|
35422
35659
|
}
|
|
35423
35660
|
}
|
|
35424
|
-
|
|
35425
|
-
|
|
35426
|
-
|
|
35427
|
-
|
|
35428
|
-
|
|
35429
|
-
|
|
35430
|
-
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
|
|
35435
|
-
|
|
35436
|
-
let page = 1;
|
|
35437
|
-
let hasMore = true;
|
|
35438
|
-
while (hasMore) {
|
|
35439
|
-
const { items: sites } = await _getAllSites({
|
|
35440
|
-
page,
|
|
35441
|
-
limit
|
|
35442
|
-
});
|
|
35443
|
-
if (!sites.length) {
|
|
35444
|
-
hasMore = false;
|
|
35445
|
-
break;
|
|
35446
|
-
}
|
|
35447
|
-
for (const site of sites) {
|
|
35448
|
-
const siteId = site._id;
|
|
35449
|
-
const camera = await _getBySite(siteId, {
|
|
35450
|
-
type: "anpr" /* ANPR */
|
|
35451
|
-
});
|
|
35452
|
-
if (!camera)
|
|
35453
|
-
continue;
|
|
35454
|
-
const transactions = await _getExpiredCheckedOutTransactionsBySite(
|
|
35455
|
-
siteId
|
|
35661
|
+
if (Array.isArray(value.passKeys)) {
|
|
35662
|
+
for (const item of value.passKeys) {
|
|
35663
|
+
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
|
|
35664
|
+
await KeyRepo.updateKeyById(
|
|
35665
|
+
item.keyId,
|
|
35666
|
+
{ status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
|
|
35667
|
+
value.site,
|
|
35668
|
+
session,
|
|
35669
|
+
void 0,
|
|
35670
|
+
result,
|
|
35671
|
+
void 0,
|
|
35672
|
+
true
|
|
35456
35673
|
);
|
|
35457
|
-
if (!transactions.length)
|
|
35458
|
-
continue;
|
|
35459
|
-
const successfulIds = [];
|
|
35460
|
-
const batchSize = 5;
|
|
35461
|
-
for (let i = 0; i < transactions.length; i += batchSize) {
|
|
35462
|
-
const batch = transactions.slice(i, i + batchSize);
|
|
35463
|
-
await Promise.all(
|
|
35464
|
-
batch.map(async (transaction) => {
|
|
35465
|
-
try {
|
|
35466
|
-
const dahuaPayload = {
|
|
35467
|
-
host: camera.host,
|
|
35468
|
-
username: camera.username,
|
|
35469
|
-
password: camera.password,
|
|
35470
|
-
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
35471
|
-
recno: transaction.recNo
|
|
35472
|
-
};
|
|
35473
|
-
await _removePlateNumber(dahuaPayload);
|
|
35474
|
-
successfulIds.push(transaction._id);
|
|
35475
|
-
} catch (error) {
|
|
35476
|
-
const message = String(error?.message || "").toLowerCase();
|
|
35477
|
-
const isNotFound = message.includes("not found") || message.includes("does not exist") || message.includes("no record");
|
|
35478
|
-
if (isNotFound) {
|
|
35479
|
-
logger80.warn(
|
|
35480
|
-
`Dahua record already missing for transaction ${transaction._id}, marking as removed.`
|
|
35481
|
-
);
|
|
35482
|
-
successfulIds.push(transaction._id);
|
|
35483
|
-
return;
|
|
35484
|
-
}
|
|
35485
|
-
logger80.error(
|
|
35486
|
-
`Failed to remove plate for transaction ${transaction._id}`,
|
|
35487
|
-
error
|
|
35488
|
-
);
|
|
35489
|
-
}
|
|
35490
|
-
})
|
|
35491
|
-
);
|
|
35492
|
-
}
|
|
35493
|
-
if (successfulIds.length > 0) {
|
|
35494
|
-
await _updateManyDahuaSyncStatus(successfulIds, "removed");
|
|
35495
|
-
}
|
|
35496
35674
|
}
|
|
35497
|
-
page++;
|
|
35498
35675
|
}
|
|
35499
|
-
} catch (error) {
|
|
35500
|
-
throw error;
|
|
35501
|
-
}
|
|
35502
|
-
}
|
|
35503
|
-
async function inviteVisitor(value, userId) {
|
|
35504
|
-
const session = useAtlas51.getClient()?.startSession();
|
|
35505
|
-
if (!session) {
|
|
35506
|
-
throw new Error("Unable to start session for people service.");
|
|
35507
|
-
}
|
|
35508
|
-
try {
|
|
35509
|
-
session.startTransaction();
|
|
35510
|
-
const inviter = await _getByUserId(userId);
|
|
35511
|
-
const unit = await getById(inviter?.unit);
|
|
35512
|
-
value.checkIn = null;
|
|
35513
|
-
const payload = {
|
|
35514
|
-
...value,
|
|
35515
|
-
block: inviter?.block?.toString(),
|
|
35516
|
-
level: inviter?.level?.toString(),
|
|
35517
|
-
unit: inviter?.unit?.toString(),
|
|
35518
|
-
unitName: unit?.name,
|
|
35519
|
-
org: inviter?.org.toString(),
|
|
35520
|
-
site: inviter?.site.toString(),
|
|
35521
|
-
status: "approved" /* APPROVED */,
|
|
35522
|
-
inviterId: userId
|
|
35523
|
-
};
|
|
35524
|
-
const result = await _add(payload);
|
|
35525
35676
|
await session?.commitTransaction();
|
|
35677
|
+
let openBarrier = null;
|
|
35678
|
+
const isOpenBarrier = allowedPersonTypes.includes(value?.type) || camera?.ANPRSwitches?.openBarrierPickUpDropOff == true;
|
|
35679
|
+
if (isOpenBarrier && value.plateNumber && host && username && password) {
|
|
35680
|
+
try {
|
|
35681
|
+
logger81.info(`Attempting to open barrier at host: ${host}`);
|
|
35682
|
+
openBarrier = await _openBarrier({
|
|
35683
|
+
host,
|
|
35684
|
+
username,
|
|
35685
|
+
password,
|
|
35686
|
+
plateNumber: value?.plateNumber
|
|
35687
|
+
});
|
|
35688
|
+
logger81.info(`Barrier command sent successfully to ${host}`, {
|
|
35689
|
+
response: openBarrier
|
|
35690
|
+
});
|
|
35691
|
+
} catch (error) {
|
|
35692
|
+
logger81.error(
|
|
35693
|
+
`Failed to open barrier at host ${host}. Critical system error:`,
|
|
35694
|
+
error
|
|
35695
|
+
);
|
|
35696
|
+
openBarrier = { success: false, error: error?.message };
|
|
35697
|
+
}
|
|
35698
|
+
}
|
|
35699
|
+
console.log("Open barrier response:", openBarrier);
|
|
35526
35700
|
return result;
|
|
35527
35701
|
} catch (error) {
|
|
35528
|
-
|
|
35529
|
-
await session.abortTransaction();
|
|
35702
|
+
await session?.abortTransaction();
|
|
35530
35703
|
throw error;
|
|
35531
35704
|
} finally {
|
|
35532
35705
|
session?.endSession();
|
|
35533
35706
|
}
|
|
35534
35707
|
}
|
|
35535
|
-
|
|
35536
|
-
|
|
35537
|
-
|
|
35538
|
-
processTransactionDahuaStatus,
|
|
35539
|
-
inviteVisitor
|
|
35540
|
-
// changeVisitorTransactionKeysById,
|
|
35541
|
-
};
|
|
35542
|
-
}
|
|
35543
|
-
|
|
35544
|
-
// src/controllers/visitor-transaction.controller.ts
|
|
35545
|
-
import Joi57 from "joi";
|
|
35546
|
-
import { BadRequestError as BadRequestError101, logger as logger81 } from "@7365admin1/node-server-utils";
|
|
35547
|
-
function useVisitorTransactionController() {
|
|
35548
|
-
const {
|
|
35549
|
-
add: _add,
|
|
35550
|
-
updateVisitorTransactionById: _updateVisitorTransactionById,
|
|
35551
|
-
inviteVisitor: _inviteVisitor
|
|
35552
|
-
// changeVisitorTransactionKeysById: _changeVisitorTransactionKeysById,
|
|
35553
|
-
} = useVisitorTransactionService();
|
|
35554
|
-
const {
|
|
35555
|
-
getAll: _getAll,
|
|
35556
|
-
deleteVisitorTransaction: _deleteVisitorTransaction,
|
|
35557
|
-
getVisitorTransactionById: _getVisitorTransactionById,
|
|
35558
|
-
resendEmailInvitedResident
|
|
35559
|
-
} = useVisitorTransactionRepo();
|
|
35560
|
-
async function add(req, res, next) {
|
|
35708
|
+
async function updateVisitorTransactionById(id, value) {
|
|
35709
|
+
const session = useAtlas52.getClient()?.startSession();
|
|
35710
|
+
session?.startTransaction();
|
|
35561
35711
|
try {
|
|
35562
|
-
|
|
35563
|
-
|
|
35564
|
-
|
|
35565
|
-
|
|
35566
|
-
if (
|
|
35567
|
-
const
|
|
35568
|
-
|
|
35569
|
-
|
|
35570
|
-
|
|
35712
|
+
let camera;
|
|
35713
|
+
let host;
|
|
35714
|
+
let username;
|
|
35715
|
+
let password;
|
|
35716
|
+
if (value.checkIn) {
|
|
35717
|
+
const parsed = new Date(value.checkIn);
|
|
35718
|
+
value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
|
|
35719
|
+
}
|
|
35720
|
+
const { nric = "", name = "", contact = "", plateNumber = "", email = "", site = "", type = "guest", company, createdBy } = value;
|
|
35721
|
+
if (/^(guest|walk-in|contractor|delivery)$/.test(type)) {
|
|
35722
|
+
await addUpdateFromVisitor({
|
|
35723
|
+
nric,
|
|
35724
|
+
name,
|
|
35725
|
+
contact,
|
|
35726
|
+
plateNumber,
|
|
35727
|
+
email,
|
|
35728
|
+
type,
|
|
35729
|
+
site,
|
|
35730
|
+
company,
|
|
35731
|
+
updatedBy: createdBy
|
|
35732
|
+
}, session);
|
|
35733
|
+
}
|
|
35734
|
+
if (value.isMembersAdded != true && Array.isArray(value.members) && value.members.length > 0) {
|
|
35735
|
+
const chunkSize = 10;
|
|
35736
|
+
const {
|
|
35737
|
+
block,
|
|
35738
|
+
level,
|
|
35739
|
+
unit,
|
|
35740
|
+
site: site2,
|
|
35741
|
+
org,
|
|
35742
|
+
type: type2,
|
|
35743
|
+
company: company2,
|
|
35744
|
+
remarks,
|
|
35745
|
+
contractorType,
|
|
35746
|
+
unitName
|
|
35747
|
+
} = value;
|
|
35748
|
+
for (let i = 0; i < value.members.length; i += chunkSize) {
|
|
35749
|
+
const chunk = value.members.slice(i, i + chunkSize);
|
|
35750
|
+
await Promise.all(
|
|
35751
|
+
chunk.map(async (member) => {
|
|
35752
|
+
const clonedMember = structuredClone(member);
|
|
35753
|
+
const { visitorPass, passKeys, nric: nric2, name: name2, contact: contact2 } = clonedMember;
|
|
35754
|
+
if (/^(guest|walk-in|contractor|delivery)$/.test(type2 ?? "invalid") && site2) {
|
|
35755
|
+
await addUpdateFromVisitor({
|
|
35756
|
+
nric: nric2,
|
|
35757
|
+
name: name2,
|
|
35758
|
+
contact: contact2,
|
|
35759
|
+
type: type2,
|
|
35760
|
+
site: site2,
|
|
35761
|
+
company: company2,
|
|
35762
|
+
updatedBy: createdBy
|
|
35763
|
+
}, session);
|
|
35764
|
+
}
|
|
35765
|
+
const preparedVisitorPass = Array.isArray(visitorPass) ? visitorPass.map((item) => ({
|
|
35766
|
+
...item,
|
|
35767
|
+
receivedDate: /* @__PURE__ */ new Date(),
|
|
35768
|
+
status: "Not Returned" /* NOT_RETURNED */,
|
|
35769
|
+
lastUpdate: null,
|
|
35770
|
+
remarks: ""
|
|
35771
|
+
})) : [];
|
|
35772
|
+
const preparedPassKeys = Array.isArray(passKeys) ? passKeys.map((item) => ({
|
|
35773
|
+
...item,
|
|
35774
|
+
receivedDate: /* @__PURE__ */ new Date(),
|
|
35775
|
+
status: "Not Returned" /* NOT_RETURNED */,
|
|
35776
|
+
lastUpdate: null,
|
|
35777
|
+
remarks: ""
|
|
35778
|
+
})) : [];
|
|
35779
|
+
const visitorId = await _add(
|
|
35780
|
+
{
|
|
35781
|
+
...clonedMember,
|
|
35782
|
+
block,
|
|
35783
|
+
level,
|
|
35784
|
+
unit,
|
|
35785
|
+
unitName,
|
|
35786
|
+
site: site2,
|
|
35787
|
+
org,
|
|
35788
|
+
type: type2,
|
|
35789
|
+
company: company2,
|
|
35790
|
+
remarks,
|
|
35791
|
+
contractorType,
|
|
35792
|
+
checkIn: value.checkIn,
|
|
35793
|
+
visitorPass: preparedVisitorPass,
|
|
35794
|
+
passKeys: preparedPassKeys,
|
|
35795
|
+
status: "registered" /* REGISTERED */
|
|
35796
|
+
},
|
|
35797
|
+
session
|
|
35798
|
+
);
|
|
35799
|
+
console.log("visitorId service", visitorId);
|
|
35800
|
+
for (const item of preparedVisitorPass) {
|
|
35801
|
+
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
|
|
35802
|
+
await KeyRepo.updateKeyById(
|
|
35803
|
+
item.keyId,
|
|
35804
|
+
{
|
|
35805
|
+
status: "In Use" /* IN_USE */,
|
|
35806
|
+
updatedBy: value.createdBy
|
|
35807
|
+
},
|
|
35808
|
+
value.site,
|
|
35809
|
+
session,
|
|
35810
|
+
void 0,
|
|
35811
|
+
visitorId,
|
|
35812
|
+
void 0,
|
|
35813
|
+
true
|
|
35814
|
+
);
|
|
35815
|
+
}
|
|
35816
|
+
for (const item of preparedPassKeys) {
|
|
35817
|
+
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
|
|
35818
|
+
await KeyRepo.updateKeyById(
|
|
35819
|
+
item.keyId,
|
|
35820
|
+
{
|
|
35821
|
+
status: "In Use" /* IN_USE */,
|
|
35822
|
+
updatedBy: value.createdBy,
|
|
35823
|
+
visitorId
|
|
35824
|
+
},
|
|
35825
|
+
value.site,
|
|
35826
|
+
session,
|
|
35827
|
+
void 0,
|
|
35828
|
+
visitorId,
|
|
35829
|
+
void 0,
|
|
35830
|
+
true
|
|
35831
|
+
);
|
|
35832
|
+
}
|
|
35833
|
+
})
|
|
35834
|
+
);
|
|
35835
|
+
}
|
|
35836
|
+
value.isMembersAdded = true;
|
|
35837
|
+
}
|
|
35838
|
+
if (value.site && value.plateNumber) {
|
|
35839
|
+
try {
|
|
35840
|
+
camera = await _getVisitorsInBySite(value.site);
|
|
35841
|
+
if (!camera) {
|
|
35842
|
+
logger81.warn(`No camera document found for site: ${value.site}`);
|
|
35843
|
+
}
|
|
35844
|
+
} catch (error) {
|
|
35845
|
+
logger81.error(
|
|
35846
|
+
`Error _getVisitorsInBySite camera for site ${value.site}:`,
|
|
35847
|
+
error
|
|
35848
|
+
);
|
|
35849
|
+
}
|
|
35850
|
+
}
|
|
35851
|
+
if (camera && value.plateNumber && camera.host && camera.username && camera.password) {
|
|
35852
|
+
host = camera.host;
|
|
35853
|
+
username = camera.username;
|
|
35854
|
+
password = camera.password;
|
|
35855
|
+
const blacklist = "TrafficBlackList" /* TRAFFIC_BLACKLIST */;
|
|
35856
|
+
const _plateNumber = value.plateNumber;
|
|
35857
|
+
const dahuaBlocklistQuery = {
|
|
35858
|
+
host,
|
|
35859
|
+
username,
|
|
35860
|
+
password,
|
|
35861
|
+
plateNumber: _plateNumber,
|
|
35862
|
+
mode: blacklist
|
|
35863
|
+
};
|
|
35864
|
+
const isBlocklistedRaw = await _getPlateNumber(dahuaBlocklistQuery);
|
|
35865
|
+
const rawString = isBlocklistedRaw?.toString?.("utf-8") ?? String(isBlocklistedRaw);
|
|
35866
|
+
const foundMatch = rawString.match(/found=(\d+)/);
|
|
35867
|
+
const found = foundMatch ? Number(foundMatch[1]) : 0;
|
|
35868
|
+
if (found === 1)
|
|
35869
|
+
throw new BadRequestError101("This plate number is blocklisted");
|
|
35870
|
+
}
|
|
35871
|
+
if (value.checkOut) {
|
|
35872
|
+
const parsed = new Date(value.checkOut);
|
|
35873
|
+
value.checkOut = isNaN(parsed.getTime()) ? null : parsed;
|
|
35874
|
+
}
|
|
35875
|
+
if (value.site) {
|
|
35876
|
+
value.site = typeof value.site === "string" ? new ObjectId64(value.site) : value.site;
|
|
35571
35877
|
}
|
|
35572
|
-
|
|
35573
|
-
|
|
35574
|
-
return;
|
|
35575
|
-
} catch (error) {
|
|
35576
|
-
logger81.log({ level: "error", message: error.message });
|
|
35577
|
-
console.log("error", error);
|
|
35578
|
-
if (error?.message) {
|
|
35579
|
-
next(new BadRequestError101(error?.message));
|
|
35580
|
-
} else {
|
|
35581
|
-
next(error);
|
|
35878
|
+
if (value?.block) {
|
|
35879
|
+
value.block = await convertObjectIdUtil2(value.block, "block Id");
|
|
35582
35880
|
}
|
|
35583
|
-
|
|
35584
|
-
|
|
35585
|
-
}
|
|
35586
|
-
async function getAll(req, res, next) {
|
|
35587
|
-
try {
|
|
35588
|
-
const validation = Joi57.object({
|
|
35589
|
-
search: Joi57.string().optional().allow(null, ""),
|
|
35590
|
-
page: Joi57.number().integer().min(1).allow("", null).default(1),
|
|
35591
|
-
limit: Joi57.number().integer().min(1).max(100).allow("", null).default(10),
|
|
35592
|
-
sort: Joi57.string().valid(...Object.values(VisitorSort)).optional().allow(null, ""),
|
|
35593
|
-
status: Joi57.string().valid(...Object.values(VisitorStatus)).optional().allow(null, ""),
|
|
35594
|
-
org: Joi57.string().hex().length(24).optional().allow(null, ""),
|
|
35595
|
-
site: Joi57.string().hex().length(24).optional().allow(null, ""),
|
|
35596
|
-
dateFrom: Joi57.date().iso().optional().allow(null, ""),
|
|
35597
|
-
dateTo: Joi57.date().iso().optional().allow(null, ""),
|
|
35598
|
-
type: Joi57.alternatives().try(
|
|
35599
|
-
Joi57.array().items(
|
|
35600
|
-
Joi57.string().valid(...Object.values(PersonTypes))
|
|
35601
|
-
),
|
|
35602
|
-
Joi57.string().custom((value2) => {
|
|
35603
|
-
return value2.split(",").map((t) => t.trim());
|
|
35604
|
-
})
|
|
35605
|
-
).custom((value2) => {
|
|
35606
|
-
return Array.isArray(value2) ? value2 : [value2];
|
|
35607
|
-
}).optional().allow(null, ""),
|
|
35608
|
-
checkedOut: Joi57.boolean().allow(null, ""),
|
|
35609
|
-
plateNumber: Joi57.string().optional().allow(null, ""),
|
|
35610
|
-
order: Joi57.string().valid(...Object.values(SortOrder)).default("asc" /* ASC */),
|
|
35611
|
-
tab: Joi57.string().optional().allow(null, "")
|
|
35612
|
-
});
|
|
35613
|
-
const { error, value } = validation.validate(req.query, {
|
|
35614
|
-
abortEarly: false
|
|
35615
|
-
});
|
|
35616
|
-
if (error) {
|
|
35617
|
-
const messages = error.details.map((d) => d.message).join(", ");
|
|
35618
|
-
logger81.log({ level: "error", message: messages });
|
|
35619
|
-
next(new BadRequestError101(messages));
|
|
35620
|
-
return;
|
|
35881
|
+
if (value?.level) {
|
|
35882
|
+
value.level = await convertObjectIdUtil2(value.level, "level Id");
|
|
35621
35883
|
}
|
|
35622
|
-
|
|
35623
|
-
|
|
35624
|
-
|
|
35625
|
-
|
|
35626
|
-
|
|
35627
|
-
|
|
35628
|
-
|
|
35629
|
-
|
|
35630
|
-
|
|
35631
|
-
|
|
35632
|
-
|
|
35633
|
-
|
|
35634
|
-
|
|
35635
|
-
|
|
35636
|
-
|
|
35637
|
-
|
|
35638
|
-
|
|
35639
|
-
|
|
35640
|
-
|
|
35641
|
-
|
|
35642
|
-
|
|
35643
|
-
|
|
35644
|
-
|
|
35645
|
-
|
|
35646
|
-
|
|
35647
|
-
|
|
35648
|
-
|
|
35649
|
-
|
|
35650
|
-
|
|
35651
|
-
|
|
35652
|
-
|
|
35653
|
-
|
|
35654
|
-
|
|
35655
|
-
|
|
35656
|
-
|
|
35657
|
-
|
|
35658
|
-
|
|
35659
|
-
|
|
35660
|
-
|
|
35661
|
-
|
|
35662
|
-
|
|
35663
|
-
|
|
35664
|
-
|
|
35665
|
-
|
|
35666
|
-
|
|
35667
|
-
|
|
35668
|
-
|
|
35669
|
-
|
|
35670
|
-
|
|
35671
|
-
|
|
35672
|
-
|
|
35884
|
+
if (value.unit) {
|
|
35885
|
+
value.unit = typeof value.unit === "string" ? new ObjectId64(value.unit) : value.unit;
|
|
35886
|
+
const unit = await _getUnitById(value.unit);
|
|
35887
|
+
value.unitName = unit?.name;
|
|
35888
|
+
}
|
|
35889
|
+
if (value.updatedBy) {
|
|
35890
|
+
value.updatedBy = await convertObjectIdUtil2(value.updatedBy, "updatedBy Id");
|
|
35891
|
+
}
|
|
35892
|
+
if (Array.isArray(value.visitorPass)) {
|
|
35893
|
+
for (const item of value.visitorPass) {
|
|
35894
|
+
if (item.add) {
|
|
35895
|
+
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
|
|
35896
|
+
await KeyRepo.updateKeyById(
|
|
35897
|
+
item.keyId,
|
|
35898
|
+
{
|
|
35899
|
+
status: "In Use" /* IN_USE */,
|
|
35900
|
+
updatedBy: value.updatedBy
|
|
35901
|
+
},
|
|
35902
|
+
value.site,
|
|
35903
|
+
session,
|
|
35904
|
+
void 0,
|
|
35905
|
+
id
|
|
35906
|
+
);
|
|
35907
|
+
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35908
|
+
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35909
|
+
item.lastUpdate = null;
|
|
35910
|
+
item.remarks = "";
|
|
35911
|
+
} else {
|
|
35912
|
+
let status = "Invalid";
|
|
35913
|
+
if (item?.status == "Returned" /* RETURNED */) {
|
|
35914
|
+
status = "Available" /* AVAILABLE */;
|
|
35915
|
+
} else if (item?.status == "Removed" /* REMOVED */) {
|
|
35916
|
+
status = "Available" /* AVAILABLE */;
|
|
35917
|
+
} else if (item?.status == "Not Returned" /* NOT_RETURNED */) {
|
|
35918
|
+
status = "In Use" /* IN_USE */;
|
|
35919
|
+
} else if (item?.status && VALID_STATUSES.has(item.status)) {
|
|
35920
|
+
status = item.status;
|
|
35921
|
+
} else {
|
|
35922
|
+
throw new Error("Invalid Visitor Pass Status");
|
|
35923
|
+
}
|
|
35924
|
+
await KeyRepo.updateKeyById(
|
|
35925
|
+
item.keyId,
|
|
35926
|
+
{
|
|
35927
|
+
status,
|
|
35928
|
+
updatedBy: value.updatedBy
|
|
35929
|
+
},
|
|
35930
|
+
value.site,
|
|
35931
|
+
session,
|
|
35932
|
+
void 0,
|
|
35933
|
+
id,
|
|
35934
|
+
true
|
|
35935
|
+
);
|
|
35936
|
+
delete item.receivedDate;
|
|
35937
|
+
item.lastUpdate = /* @__PURE__ */ new Date();
|
|
35938
|
+
}
|
|
35673
35939
|
}
|
|
35674
|
-
);
|
|
35675
|
-
if (error) {
|
|
35676
|
-
const messages = error.details.map((d) => d.message).join(", ");
|
|
35677
|
-
logger81.log({ level: "error", message: messages });
|
|
35678
|
-
next(new BadRequestError101(messages));
|
|
35679
|
-
return;
|
|
35680
35940
|
}
|
|
35681
|
-
|
|
35682
|
-
|
|
35683
|
-
|
|
35684
|
-
|
|
35685
|
-
|
|
35686
|
-
|
|
35687
|
-
|
|
35688
|
-
|
|
35689
|
-
|
|
35690
|
-
|
|
35691
|
-
|
|
35692
|
-
|
|
35693
|
-
|
|
35694
|
-
|
|
35695
|
-
|
|
35696
|
-
|
|
35697
|
-
|
|
35698
|
-
|
|
35699
|
-
|
|
35700
|
-
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
|
|
35704
|
-
|
|
35705
|
-
|
|
35706
|
-
|
|
35707
|
-
|
|
35708
|
-
|
|
35709
|
-
|
|
35710
|
-
|
|
35711
|
-
|
|
35712
|
-
|
|
35713
|
-
|
|
35941
|
+
if (Array.isArray(value.passKeys)) {
|
|
35942
|
+
for (const item of value.passKeys) {
|
|
35943
|
+
if (item.add) {
|
|
35944
|
+
await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
|
|
35945
|
+
await KeyRepo.updateKeyById(
|
|
35946
|
+
item.keyId,
|
|
35947
|
+
{
|
|
35948
|
+
status: "In Use" /* IN_USE */,
|
|
35949
|
+
updatedBy: value.updatedBy
|
|
35950
|
+
},
|
|
35951
|
+
value.site,
|
|
35952
|
+
session,
|
|
35953
|
+
void 0,
|
|
35954
|
+
id
|
|
35955
|
+
);
|
|
35956
|
+
item.receivedDate = /* @__PURE__ */ new Date();
|
|
35957
|
+
item.status = "Not Returned" /* NOT_RETURNED */;
|
|
35958
|
+
item.lastUpdate = null;
|
|
35959
|
+
item.remarks = "";
|
|
35960
|
+
} else {
|
|
35961
|
+
let status = "Invalid";
|
|
35962
|
+
if (item?.status == "Returned" /* RETURNED */) {
|
|
35963
|
+
status = "Available" /* AVAILABLE */;
|
|
35964
|
+
} else if (item?.status == "Removed" /* REMOVED */) {
|
|
35965
|
+
status = "Available" /* AVAILABLE */;
|
|
35966
|
+
} else if (item?.status == "Not Returned" /* NOT_RETURNED */) {
|
|
35967
|
+
status = "In Use" /* IN_USE */;
|
|
35968
|
+
} else if (item?.status && VALID_STATUSES.has(item.status)) {
|
|
35969
|
+
status = item.status;
|
|
35970
|
+
} else {
|
|
35971
|
+
throw new Error("Invalid Key Status");
|
|
35972
|
+
}
|
|
35973
|
+
await KeyRepo.updateKeyById(
|
|
35974
|
+
item.keyId,
|
|
35975
|
+
{
|
|
35976
|
+
status,
|
|
35977
|
+
updatedBy: value.updatedBy
|
|
35978
|
+
},
|
|
35979
|
+
value.site,
|
|
35980
|
+
session,
|
|
35981
|
+
void 0,
|
|
35982
|
+
id,
|
|
35983
|
+
true
|
|
35984
|
+
);
|
|
35985
|
+
delete item.receivedDate;
|
|
35986
|
+
item.lastUpdate = /* @__PURE__ */ new Date();
|
|
35987
|
+
}
|
|
35988
|
+
}
|
|
35989
|
+
}
|
|
35990
|
+
await _updateVisitorTansactionById(id, value, session);
|
|
35991
|
+
const allowedPersonTypes = [
|
|
35992
|
+
"contractor" /* CONTRACTOR */,
|
|
35993
|
+
"walk-in" /* WALK_IN */,
|
|
35994
|
+
"guest" /* GUEST */,
|
|
35995
|
+
"delivery" /* DELIVERY */
|
|
35996
|
+
// PersonTypes.PICK_UP,
|
|
35997
|
+
];
|
|
35998
|
+
let openBarrier = null;
|
|
35999
|
+
const isOpenBarrier = allowedPersonTypes.includes(value?.type) || camera?.ANPRSwitches?.openBarrierPickUpDropOff == true;
|
|
36000
|
+
if (isOpenBarrier && value.plateNumber && host && username && password) {
|
|
36001
|
+
try {
|
|
36002
|
+
logger81.info(`Attempting to open barrier at host: ${host}`);
|
|
36003
|
+
openBarrier = await _openBarrier({
|
|
36004
|
+
host,
|
|
36005
|
+
username,
|
|
36006
|
+
password,
|
|
36007
|
+
plateNumber: value?.plateNumber
|
|
36008
|
+
});
|
|
36009
|
+
logger81.info(`Barrier command sent successfully to ${host}`, {
|
|
36010
|
+
response: openBarrier
|
|
36011
|
+
});
|
|
36012
|
+
} catch (error) {
|
|
36013
|
+
logger81.error(
|
|
36014
|
+
`Failed to open barrier at host ${host}. Critical system error:`,
|
|
36015
|
+
error
|
|
36016
|
+
);
|
|
36017
|
+
openBarrier = { success: false, error: error?.message };
|
|
36018
|
+
}
|
|
35714
36019
|
}
|
|
35715
|
-
|
|
35716
|
-
|
|
35717
|
-
|
|
35718
|
-
|
|
35719
|
-
|
|
35720
|
-
|
|
35721
|
-
const { error } = validation.validate(_id);
|
|
35722
|
-
if (error) {
|
|
35723
|
-
logger81.log({ level: "error", message: error.message });
|
|
35724
|
-
next(new BadRequestError101(error.message));
|
|
35725
|
-
return;
|
|
35726
|
-
}
|
|
35727
|
-
try {
|
|
35728
|
-
await _deleteVisitorTransaction(_id);
|
|
35729
|
-
res.status(200).json({ message: "Successfully deleted visitor transaction." });
|
|
35730
|
-
return;
|
|
35731
|
-
} catch (error2) {
|
|
35732
|
-
logger81.log({ level: "error", message: error2.message });
|
|
35733
|
-
next(error2);
|
|
35734
|
-
return;
|
|
35735
|
-
}
|
|
35736
|
-
}
|
|
35737
|
-
async function inviteVisitor(req, res, next) {
|
|
35738
|
-
const schema2 = Joi57.object({
|
|
35739
|
-
expectedCheckIn: Joi57.string().isoDate().required(),
|
|
35740
|
-
arrivalTime: Joi57.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().allow(null, "").messages({
|
|
35741
|
-
"string.pattern.base": "arrivalTime must be in HH:mm format (e.g. 09:30, 18:45)"
|
|
35742
|
-
}),
|
|
35743
|
-
duration: Joi57.number().integer().optional().allow(null, ""),
|
|
35744
|
-
name: Joi57.string().required(),
|
|
35745
|
-
contact: Joi57.string().optional().allow(null, ""),
|
|
35746
|
-
email: Joi57.string().email().optional().allow(null, ""),
|
|
35747
|
-
plateNumber: Joi57.string().optional().allow(null, ""),
|
|
35748
|
-
isOvernightParking: Joi57.boolean().optional().default(false),
|
|
35749
|
-
numberOfPassengers: Joi57.number().integer().optional().allow(null, ""),
|
|
35750
|
-
purpose: Joi57.string().optional().allow(null, ""),
|
|
35751
|
-
inviterUserId: Joi57.string().hex().length(24).required()
|
|
35752
|
-
});
|
|
35753
|
-
const { error, value } = schema2.validate(
|
|
35754
|
-
{ inviterUserId: req.params.inviterId, ...req.body },
|
|
35755
|
-
{ abortEarly: false }
|
|
35756
|
-
);
|
|
35757
|
-
if (error) {
|
|
35758
|
-
const messages = error.details.map((d) => d.message).join(", ");
|
|
35759
|
-
logger81.log({ level: "error", message: messages });
|
|
35760
|
-
next(new BadRequestError101(messages));
|
|
35761
|
-
return;
|
|
35762
|
-
}
|
|
35763
|
-
const {
|
|
35764
|
-
expectedCheckIn,
|
|
35765
|
-
arrivalTime,
|
|
35766
|
-
duration,
|
|
35767
|
-
name,
|
|
35768
|
-
contact,
|
|
35769
|
-
email,
|
|
35770
|
-
plateNumber,
|
|
35771
|
-
isOvernightParking,
|
|
35772
|
-
numberOfPassengers,
|
|
35773
|
-
purpose,
|
|
35774
|
-
inviterUserId
|
|
35775
|
-
} = value;
|
|
35776
|
-
const rest = {
|
|
35777
|
-
expectedCheckIn,
|
|
35778
|
-
arrivalTime,
|
|
35779
|
-
duration,
|
|
35780
|
-
name,
|
|
35781
|
-
contact,
|
|
35782
|
-
email,
|
|
35783
|
-
plateNumber,
|
|
35784
|
-
isOvernightParking,
|
|
35785
|
-
numberOfPassengers,
|
|
35786
|
-
purpose
|
|
35787
|
-
};
|
|
35788
|
-
try {
|
|
35789
|
-
const result = await _inviteVisitor(rest, inviterUserId);
|
|
35790
|
-
if (result) {
|
|
35791
|
-
await resendEmailInvitedResident(result, inviterUserId).catch((error2) => {
|
|
35792
|
-
console.error(`Failed to send invitation email: ${error2.message}`);
|
|
35793
|
-
logger81.log({ level: "error", message: `Failed to send invitation email: ${error2.message}` });
|
|
35794
|
-
});
|
|
36020
|
+
console.log("Open barrier response:", openBarrier);
|
|
36021
|
+
const visitorTransaction = await _getVisitorTransactionById(
|
|
36022
|
+
id.toString()
|
|
36023
|
+
);
|
|
36024
|
+
if (!visitorTransaction) {
|
|
36025
|
+
throw new Error("Visitor transaction not found.");
|
|
35795
36026
|
}
|
|
35796
|
-
|
|
35797
|
-
|
|
35798
|
-
|
|
35799
|
-
|
|
35800
|
-
|
|
35801
|
-
|
|
35802
|
-
|
|
35803
|
-
|
|
36027
|
+
if (value.checkOut && visitorTransaction.type === "guest" /* GUEST */) {
|
|
36028
|
+
const users = await getUsersBySiteId({
|
|
36029
|
+
status: "active",
|
|
36030
|
+
siteId: visitorTransaction.site.toString()
|
|
36031
|
+
});
|
|
36032
|
+
if (users?.length > 0) {
|
|
36033
|
+
const userIds = Array.from(
|
|
36034
|
+
new Set(users.map((item) => item.user.toString()))
|
|
36035
|
+
);
|
|
36036
|
+
NotificationService.invitedVisitorCheckOutForMa({
|
|
36037
|
+
to: userIds,
|
|
36038
|
+
name: visitorTransaction.name,
|
|
36039
|
+
_id: id.toString()
|
|
36040
|
+
});
|
|
36041
|
+
}
|
|
35804
36042
|
}
|
|
35805
|
-
|
|
35806
|
-
|
|
35807
|
-
return {
|
|
35808
|
-
add,
|
|
35809
|
-
getAll,
|
|
35810
|
-
updateVisitorTansactionById,
|
|
35811
|
-
deleteVisitorTransaction,
|
|
35812
|
-
inviteVisitor,
|
|
35813
|
-
getVisitorTransactionById
|
|
35814
|
-
// changeVisitorTransactionKeysById,
|
|
35815
|
-
};
|
|
35816
|
-
}
|
|
35817
|
-
|
|
35818
|
-
// src/models/guest-management.model.ts
|
|
35819
|
-
import Joi58 from "joi";
|
|
35820
|
-
import { ObjectId as ObjectId64 } from "mongodb";
|
|
35821
|
-
var GuestStatus = /* @__PURE__ */ ((GuestStatus2) => {
|
|
35822
|
-
GuestStatus2["PENDING"] = "pending";
|
|
35823
|
-
GuestStatus2["ACTIVE"] = "active";
|
|
35824
|
-
return GuestStatus2;
|
|
35825
|
-
})(GuestStatus || {});
|
|
35826
|
-
var GuestSort = /* @__PURE__ */ ((GuestSort2) => {
|
|
35827
|
-
GuestSort2["CREATED_AT"] = "createdAt";
|
|
35828
|
-
GuestSort2["NAME"] = "name";
|
|
35829
|
-
GuestSort2["ID"] = "_id";
|
|
35830
|
-
return GuestSort2;
|
|
35831
|
-
})(GuestSort || {});
|
|
35832
|
-
var schemaGuestManagement = Joi58.object({
|
|
35833
|
-
name: Joi58.string().required(),
|
|
35834
|
-
contact: Joi58.string().optional().allow(null, ""),
|
|
35835
|
-
block: Joi58.number().required(),
|
|
35836
|
-
level: Joi58.string().required(),
|
|
35837
|
-
unit: Joi58.string().required(),
|
|
35838
|
-
start: Joi58.date().required(),
|
|
35839
|
-
end: Joi58.date().required(),
|
|
35840
|
-
status: Joi58.string().optional().allow(null, ""),
|
|
35841
|
-
nric: Joi58.string().optional().allow(null, ""),
|
|
35842
|
-
remarks: Joi58.string().optional().allow(null, ""),
|
|
35843
|
-
org: Joi58.string().hex().length(24).required(),
|
|
35844
|
-
site: Joi58.string().hex().length(24).required(),
|
|
35845
|
-
plateNumber: Joi58.string().optional().allow(null, ""),
|
|
35846
|
-
unitName: Joi58.string().optional().allow(null, "")
|
|
35847
|
-
});
|
|
35848
|
-
var schemaUpdateGuestManagement = Joi58.object({
|
|
35849
|
-
_id: Joi58.string().hex().length(24).required(),
|
|
35850
|
-
name: Joi58.string().optional().allow(null, ""),
|
|
35851
|
-
contact: Joi58.string().optional().allow(null, ""),
|
|
35852
|
-
block: Joi58.number().optional().allow(null, ""),
|
|
35853
|
-
level: Joi58.string().optional().allow(null, ""),
|
|
35854
|
-
unit: Joi58.string().optional().allow(null, ""),
|
|
35855
|
-
start: Joi58.date().optional().allow(null, ""),
|
|
35856
|
-
end: Joi58.date().optional().allow(null, ""),
|
|
35857
|
-
status: Joi58.string().optional().allow(null, ""),
|
|
35858
|
-
unitName: Joi58.string().optional().allow(null, "")
|
|
35859
|
-
});
|
|
35860
|
-
function MGuestManagement(value) {
|
|
35861
|
-
const { error } = schemaGuestManagement.validate(value);
|
|
35862
|
-
if (error) {
|
|
35863
|
-
throw new Error(error.details[0].message);
|
|
35864
|
-
}
|
|
35865
|
-
if (value._id && typeof value._id === "string") {
|
|
35866
|
-
try {
|
|
35867
|
-
value._id = new ObjectId64(value._id);
|
|
35868
|
-
} catch (error2) {
|
|
35869
|
-
throw new Error("Invalid ID.");
|
|
35870
|
-
}
|
|
35871
|
-
}
|
|
35872
|
-
if (value.org && typeof value.org === "string") {
|
|
35873
|
-
try {
|
|
35874
|
-
value.org = new ObjectId64(value.org);
|
|
35875
|
-
} catch (error2) {
|
|
35876
|
-
throw new Error("Invalid org ID.");
|
|
35877
|
-
}
|
|
35878
|
-
}
|
|
35879
|
-
if (value.site && typeof value.site === "string") {
|
|
35880
|
-
try {
|
|
35881
|
-
value.site = new ObjectId64(value.site);
|
|
35882
|
-
} catch (error2) {
|
|
35883
|
-
throw new Error("Invalid site ID.");
|
|
35884
|
-
}
|
|
35885
|
-
}
|
|
35886
|
-
return {
|
|
35887
|
-
_id: value._id,
|
|
35888
|
-
name: value.name,
|
|
35889
|
-
contact: value.contact,
|
|
35890
|
-
block: value.block,
|
|
35891
|
-
level: value.level,
|
|
35892
|
-
unit: value.unit,
|
|
35893
|
-
start: value.start,
|
|
35894
|
-
end: value.end,
|
|
35895
|
-
status: value.status ?? "active",
|
|
35896
|
-
nric: value.nric,
|
|
35897
|
-
remarks: value.remarks,
|
|
35898
|
-
org: value.org,
|
|
35899
|
-
site: value.site,
|
|
35900
|
-
plateNumber: value.plateNumber,
|
|
35901
|
-
unitName: value.unitName,
|
|
35902
|
-
createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
35903
|
-
updatedAt: value.updatedAt,
|
|
35904
|
-
deletedAt: value.deletedAt
|
|
35905
|
-
};
|
|
35906
|
-
}
|
|
35907
|
-
|
|
35908
|
-
// src/repositories/guest-management.repo.ts
|
|
35909
|
-
import {
|
|
35910
|
-
BadRequestError as BadRequestError102,
|
|
35911
|
-
InternalServerError as InternalServerError34,
|
|
35912
|
-
logger as logger82,
|
|
35913
|
-
makeCacheKey as makeCacheKey31,
|
|
35914
|
-
paginate as paginate26,
|
|
35915
|
-
useAtlas as useAtlas52,
|
|
35916
|
-
useCache as useCache33
|
|
35917
|
-
} from "@7365admin1/node-server-utils";
|
|
35918
|
-
import { ObjectId as ObjectId65 } from "mongodb";
|
|
35919
|
-
var guests_namespace_collection = "visitor.guests";
|
|
35920
|
-
function useGuestManagementRepo() {
|
|
35921
|
-
const db = useAtlas52.getDb();
|
|
35922
|
-
if (!db) {
|
|
35923
|
-
throw new InternalServerError34("Unable to connect to server.");
|
|
35924
|
-
}
|
|
35925
|
-
const collection = db.collection(guests_namespace_collection);
|
|
35926
|
-
const { delNamespace, getCache, setCache } = useCache33(
|
|
35927
|
-
guests_namespace_collection
|
|
35928
|
-
);
|
|
35929
|
-
async function createTextIndex() {
|
|
35930
|
-
try {
|
|
35931
|
-
await collection.createIndex({
|
|
35932
|
-
name: "text",
|
|
35933
|
-
plateNumber: "text",
|
|
35934
|
-
company: "text",
|
|
35935
|
-
level: "text",
|
|
35936
|
-
unitName: "text",
|
|
35937
|
-
contact: "text"
|
|
35938
|
-
});
|
|
36043
|
+
await session?.commitTransaction();
|
|
36044
|
+
return "Successfully updated visitor transaction.";
|
|
35939
36045
|
} catch (error) {
|
|
35940
|
-
|
|
36046
|
+
await session?.abortTransaction();
|
|
36047
|
+
throw error;
|
|
36048
|
+
} finally {
|
|
36049
|
+
session?.endSession();
|
|
35941
36050
|
}
|
|
35942
36051
|
}
|
|
35943
|
-
async function
|
|
36052
|
+
async function processTransactionDahuaStatus() {
|
|
35944
36053
|
try {
|
|
35945
|
-
|
|
35946
|
-
|
|
35947
|
-
|
|
35948
|
-
|
|
35949
|
-
|
|
35950
|
-
|
|
35951
|
-
|
|
35952
|
-
|
|
35953
|
-
|
|
35954
|
-
|
|
35955
|
-
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
|
|
35959
|
-
|
|
35960
|
-
|
|
35961
|
-
|
|
36054
|
+
const limit = 100;
|
|
36055
|
+
let page = 1;
|
|
36056
|
+
let hasMore = true;
|
|
36057
|
+
while (hasMore) {
|
|
36058
|
+
const { items: sites } = await _getAllSites({
|
|
36059
|
+
page,
|
|
36060
|
+
limit
|
|
36061
|
+
});
|
|
36062
|
+
if (!sites.length) {
|
|
36063
|
+
hasMore = false;
|
|
36064
|
+
break;
|
|
36065
|
+
}
|
|
36066
|
+
for (const site of sites) {
|
|
36067
|
+
const siteId = site._id;
|
|
36068
|
+
const camera = await _getBySite(siteId, {
|
|
36069
|
+
type: "anpr" /* ANPR */
|
|
36070
|
+
});
|
|
36071
|
+
if (!camera)
|
|
36072
|
+
continue;
|
|
36073
|
+
const transactions = await _getExpiredCheckedOutTransactionsBySite(
|
|
36074
|
+
siteId
|
|
36075
|
+
);
|
|
36076
|
+
if (!transactions.length)
|
|
36077
|
+
continue;
|
|
36078
|
+
const successfulIds = [];
|
|
36079
|
+
const batchSize = 5;
|
|
36080
|
+
for (let i = 0; i < transactions.length; i += batchSize) {
|
|
36081
|
+
const batch = transactions.slice(i, i + batchSize);
|
|
36082
|
+
await Promise.all(
|
|
36083
|
+
batch.map(async (transaction) => {
|
|
36084
|
+
try {
|
|
36085
|
+
const dahuaPayload = {
|
|
36086
|
+
host: camera.host,
|
|
36087
|
+
username: camera.username,
|
|
36088
|
+
password: camera.password,
|
|
36089
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
36090
|
+
recno: transaction.recNo
|
|
36091
|
+
};
|
|
36092
|
+
await _removePlateNumber(dahuaPayload);
|
|
36093
|
+
successfulIds.push(transaction._id);
|
|
36094
|
+
} catch (error) {
|
|
36095
|
+
const message = String(error?.message || "").toLowerCase();
|
|
36096
|
+
const isNotFound = message.includes("not found") || message.includes("does not exist") || message.includes("no record");
|
|
36097
|
+
if (isNotFound) {
|
|
36098
|
+
logger81.warn(
|
|
36099
|
+
`Dahua record already missing for transaction ${transaction._id}, marking as removed.`
|
|
36100
|
+
);
|
|
36101
|
+
successfulIds.push(transaction._id);
|
|
36102
|
+
return;
|
|
36103
|
+
}
|
|
36104
|
+
logger81.error(
|
|
36105
|
+
`Failed to remove plate for transaction ${transaction._id}`,
|
|
36106
|
+
error
|
|
36107
|
+
);
|
|
36108
|
+
}
|
|
36109
|
+
})
|
|
36110
|
+
);
|
|
36111
|
+
}
|
|
36112
|
+
if (successfulIds.length > 0) {
|
|
36113
|
+
await _updateManyDahuaSyncStatus(successfulIds, "removed");
|
|
36114
|
+
}
|
|
36115
|
+
}
|
|
36116
|
+
page++;
|
|
35962
36117
|
}
|
|
36118
|
+
} catch (error) {
|
|
35963
36119
|
throw error;
|
|
35964
36120
|
}
|
|
35965
36121
|
}
|
|
35966
|
-
async function
|
|
35967
|
-
|
|
35968
|
-
|
|
35969
|
-
|
|
35970
|
-
sort = {},
|
|
35971
|
-
status = "active",
|
|
35972
|
-
dateFrom,
|
|
35973
|
-
dateTo,
|
|
35974
|
-
org = "",
|
|
35975
|
-
site = ""
|
|
35976
|
-
}, session) {
|
|
35977
|
-
page = page > 0 ? page - 1 : 0;
|
|
35978
|
-
const start = dateFrom ? { $gte: new Date(dateFrom).toISOString() } : void 0;
|
|
35979
|
-
const end = dateTo ? {
|
|
35980
|
-
$or: [
|
|
35981
|
-
{ end: { $lte: new Date(dateTo).toISOString() } },
|
|
35982
|
-
{ end: null }
|
|
35983
|
-
]
|
|
35984
|
-
} : void 0;
|
|
35985
|
-
const query = {
|
|
35986
|
-
status,
|
|
35987
|
-
...start && { start },
|
|
35988
|
-
...end,
|
|
35989
|
-
...search && { $text: { $search: search } },
|
|
35990
|
-
...ObjectId65.isValid(org) && { org: new ObjectId65(org) },
|
|
35991
|
-
...ObjectId65.isValid(site) && { site: new ObjectId65(site) }
|
|
35992
|
-
};
|
|
35993
|
-
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
35994
|
-
const cacheOptions = {
|
|
35995
|
-
sort: JSON.stringify(sort),
|
|
35996
|
-
page,
|
|
35997
|
-
limit,
|
|
35998
|
-
status,
|
|
35999
|
-
...start && { start: JSON.stringify(start) },
|
|
36000
|
-
...dateTo && { end: JSON.stringify(end) },
|
|
36001
|
-
...search && { search },
|
|
36002
|
-
...query.org && { org: query.org.toString() },
|
|
36003
|
-
...query.site && { site: query.site.toString() }
|
|
36004
|
-
};
|
|
36005
|
-
const cacheKey = makeCacheKey31(guests_namespace_collection, cacheOptions);
|
|
36006
|
-
const cachedData = await getCache(cacheKey);
|
|
36007
|
-
if (cachedData) {
|
|
36008
|
-
logger82.info(`Cache hit for key: ${cacheKey}`);
|
|
36009
|
-
return cachedData;
|
|
36122
|
+
async function inviteVisitor(value, userId) {
|
|
36123
|
+
const session = useAtlas52.getClient()?.startSession();
|
|
36124
|
+
if (!session) {
|
|
36125
|
+
throw new Error("Unable to start session for people service.");
|
|
36010
36126
|
}
|
|
36011
36127
|
try {
|
|
36012
|
-
|
|
36013
|
-
|
|
36014
|
-
|
|
36015
|
-
|
|
36016
|
-
|
|
36017
|
-
|
|
36018
|
-
|
|
36019
|
-
|
|
36020
|
-
|
|
36021
|
-
|
|
36022
|
-
|
|
36023
|
-
|
|
36024
|
-
|
|
36025
|
-
|
|
36026
|
-
|
|
36027
|
-
|
|
36128
|
+
session.startTransaction();
|
|
36129
|
+
const inviter = await _getByUserId(userId);
|
|
36130
|
+
const unit = await getById(inviter?.unit);
|
|
36131
|
+
value.checkIn = null;
|
|
36132
|
+
const payload = {
|
|
36133
|
+
...value,
|
|
36134
|
+
block: inviter?.block?.toString(),
|
|
36135
|
+
level: inviter?.level?.toString(),
|
|
36136
|
+
unit: inviter?.unit?.toString(),
|
|
36137
|
+
unitName: unit?.name,
|
|
36138
|
+
org: inviter?.org?.toString(),
|
|
36139
|
+
site: inviter?.site.toString(),
|
|
36140
|
+
status: "approved" /* APPROVED */,
|
|
36141
|
+
inviterId: userId
|
|
36142
|
+
};
|
|
36143
|
+
const result = await _add(payload);
|
|
36144
|
+
await session?.commitTransaction();
|
|
36145
|
+
return result;
|
|
36028
36146
|
} catch (error) {
|
|
36147
|
+
logger81.error("Error in visitor transaction invite visitor:", error);
|
|
36148
|
+
await session.abortTransaction();
|
|
36029
36149
|
throw error;
|
|
36150
|
+
} finally {
|
|
36151
|
+
session?.endSession();
|
|
36030
36152
|
}
|
|
36031
36153
|
}
|
|
36032
|
-
|
|
36154
|
+
return {
|
|
36155
|
+
add,
|
|
36156
|
+
updateVisitorTransactionById,
|
|
36157
|
+
processTransactionDahuaStatus,
|
|
36158
|
+
inviteVisitor
|
|
36159
|
+
// changeVisitorTransactionKeysById,
|
|
36160
|
+
};
|
|
36161
|
+
}
|
|
36162
|
+
|
|
36163
|
+
// src/controllers/visitor-transaction.controller.ts
|
|
36164
|
+
import Joi57 from "joi";
|
|
36165
|
+
import { BadRequestError as BadRequestError102, logger as logger82 } from "@7365admin1/node-server-utils";
|
|
36166
|
+
function useVisitorTransactionController() {
|
|
36167
|
+
const {
|
|
36168
|
+
add: _add,
|
|
36169
|
+
updateVisitorTransactionById: _updateVisitorTransactionById,
|
|
36170
|
+
inviteVisitor: _inviteVisitor
|
|
36171
|
+
// changeVisitorTransactionKeysById: _changeVisitorTransactionKeysById,
|
|
36172
|
+
} = useVisitorTransactionService();
|
|
36173
|
+
const {
|
|
36174
|
+
getAll: _getAll,
|
|
36175
|
+
deleteVisitorTransaction: _deleteVisitorTransaction,
|
|
36176
|
+
getVisitorTransactionById: _getVisitorTransactionById,
|
|
36177
|
+
resendEmailInvitedResident
|
|
36178
|
+
} = useVisitorTransactionRepo();
|
|
36179
|
+
async function add(req, res, next) {
|
|
36033
36180
|
try {
|
|
36034
|
-
|
|
36181
|
+
const { companyName, firstName, lastName, ...restData } = req.body;
|
|
36182
|
+
const { error, value } = schemaVisitorTransaction.validate(restData, {
|
|
36183
|
+
abortEarly: false
|
|
36184
|
+
});
|
|
36185
|
+
if (error) {
|
|
36186
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36187
|
+
logger82.log({ level: "error", message: messages });
|
|
36188
|
+
next(new BadRequestError102(messages));
|
|
36189
|
+
return;
|
|
36190
|
+
}
|
|
36191
|
+
const data = await _add(value);
|
|
36192
|
+
res.status(201).json(data);
|
|
36193
|
+
return;
|
|
36035
36194
|
} catch (error) {
|
|
36036
|
-
|
|
36195
|
+
logger82.log({ level: "error", message: error.message });
|
|
36196
|
+
console.log("error", error);
|
|
36197
|
+
if (error?.message) {
|
|
36198
|
+
next(new BadRequestError102(error?.message));
|
|
36199
|
+
} else {
|
|
36200
|
+
next(error);
|
|
36201
|
+
}
|
|
36202
|
+
return;
|
|
36037
36203
|
}
|
|
36038
|
-
|
|
36204
|
+
}
|
|
36205
|
+
async function getAll(req, res, next) {
|
|
36039
36206
|
try {
|
|
36040
|
-
const
|
|
36041
|
-
|
|
36042
|
-
|
|
36043
|
-
|
|
36044
|
-
|
|
36045
|
-
|
|
36046
|
-
|
|
36047
|
-
|
|
36048
|
-
)
|
|
36049
|
-
|
|
36050
|
-
|
|
36051
|
-
|
|
36052
|
-
|
|
36053
|
-
|
|
36207
|
+
const validation = Joi57.object({
|
|
36208
|
+
search: Joi57.string().optional().allow(null, ""),
|
|
36209
|
+
page: Joi57.number().integer().min(1).allow("", null).default(1),
|
|
36210
|
+
limit: Joi57.number().integer().min(1).max(100).allow("", null).default(10),
|
|
36211
|
+
sort: Joi57.string().valid(...Object.values(VisitorSort)).optional().allow(null, ""),
|
|
36212
|
+
status: Joi57.string().valid(...Object.values(VisitorStatus)).optional().allow(null, ""),
|
|
36213
|
+
org: Joi57.string().hex().length(24).optional().allow(null, ""),
|
|
36214
|
+
site: Joi57.string().hex().length(24).optional().allow(null, ""),
|
|
36215
|
+
dateFrom: Joi57.date().iso().optional().allow(null, ""),
|
|
36216
|
+
dateTo: Joi57.date().iso().optional().allow(null, ""),
|
|
36217
|
+
type: Joi57.alternatives().try(
|
|
36218
|
+
Joi57.array().items(
|
|
36219
|
+
Joi57.string().valid(...Object.values(PersonTypes))
|
|
36220
|
+
),
|
|
36221
|
+
Joi57.string().custom((value2) => {
|
|
36222
|
+
return value2.split(",").map((t) => t.trim());
|
|
36223
|
+
})
|
|
36224
|
+
).custom((value2) => {
|
|
36225
|
+
return Array.isArray(value2) ? value2 : [value2];
|
|
36226
|
+
}).optional().allow(null, ""),
|
|
36227
|
+
checkedOut: Joi57.boolean().allow(null, ""),
|
|
36228
|
+
plateNumber: Joi57.string().optional().allow(null, ""),
|
|
36229
|
+
order: Joi57.string().valid(...Object.values(SortOrder)).default("asc" /* ASC */),
|
|
36230
|
+
tab: Joi57.string().optional().allow(null, "")
|
|
36231
|
+
});
|
|
36232
|
+
const { error, value } = validation.validate(req.query, {
|
|
36233
|
+
abortEarly: false
|
|
36234
|
+
});
|
|
36235
|
+
if (error) {
|
|
36236
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36237
|
+
logger82.log({ level: "error", message: messages });
|
|
36238
|
+
next(new BadRequestError102(messages));
|
|
36239
|
+
return;
|
|
36240
|
+
}
|
|
36241
|
+
const {
|
|
36242
|
+
org,
|
|
36243
|
+
site,
|
|
36244
|
+
search,
|
|
36245
|
+
page,
|
|
36246
|
+
limit,
|
|
36247
|
+
status,
|
|
36248
|
+
dateTo,
|
|
36249
|
+
dateFrom,
|
|
36250
|
+
type,
|
|
36251
|
+
checkedOut,
|
|
36252
|
+
sort,
|
|
36253
|
+
order,
|
|
36254
|
+
plateNumber,
|
|
36255
|
+
tab
|
|
36256
|
+
} = value;
|
|
36257
|
+
const sortObj = {
|
|
36258
|
+
[sort ? sort : "_id" /* ID */]: order === "asc" /* ASC */ ? 1 : -1
|
|
36259
|
+
};
|
|
36260
|
+
const data = await _getAll({
|
|
36261
|
+
org,
|
|
36262
|
+
site,
|
|
36263
|
+
search,
|
|
36264
|
+
page,
|
|
36265
|
+
limit,
|
|
36266
|
+
sort: sortObj,
|
|
36267
|
+
status,
|
|
36268
|
+
dateTo,
|
|
36269
|
+
dateFrom,
|
|
36270
|
+
type,
|
|
36271
|
+
checkedOut,
|
|
36272
|
+
plateNumber,
|
|
36273
|
+
tab
|
|
36054
36274
|
});
|
|
36055
|
-
|
|
36275
|
+
res.status(200).json(data);
|
|
36276
|
+
return;
|
|
36056
36277
|
} catch (error) {
|
|
36057
|
-
|
|
36278
|
+
logger82.log({ level: "error", message: error.message });
|
|
36279
|
+
next(error);
|
|
36280
|
+
return;
|
|
36058
36281
|
}
|
|
36059
36282
|
}
|
|
36060
|
-
async function
|
|
36061
|
-
try {
|
|
36062
|
-
_id = new ObjectId65(_id);
|
|
36063
|
-
} catch (error) {
|
|
36064
|
-
throw new BadRequestError102("Invalid customer ID format.");
|
|
36065
|
-
}
|
|
36283
|
+
async function getVisitorTransactionById(req, res, next) {
|
|
36066
36284
|
try {
|
|
36067
|
-
const
|
|
36068
|
-
|
|
36069
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36070
|
-
deletedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
36071
|
-
};
|
|
36072
|
-
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
36073
|
-
if (res.modifiedCount === 0) {
|
|
36074
|
-
throw new InternalServerError34("Unable to delete visitor transaction.");
|
|
36075
|
-
}
|
|
36076
|
-
delNamespace().then(() => {
|
|
36077
|
-
logger82.info(
|
|
36078
|
-
`Cache cleared for namespace: ${guests_namespace_collection}`
|
|
36079
|
-
);
|
|
36080
|
-
}).catch((err) => {
|
|
36081
|
-
logger82.error(
|
|
36082
|
-
`Failed to clear cache for namespace: ${guests_namespace_collection}`,
|
|
36083
|
-
err
|
|
36084
|
-
);
|
|
36285
|
+
const schema2 = Joi57.object({
|
|
36286
|
+
_id: Joi57.string().hex().length(24).required()
|
|
36085
36287
|
});
|
|
36086
|
-
|
|
36288
|
+
const { error, value } = schema2.validate(
|
|
36289
|
+
{ _id: req.params.id },
|
|
36290
|
+
{
|
|
36291
|
+
abortEarly: false
|
|
36292
|
+
}
|
|
36293
|
+
);
|
|
36294
|
+
if (error) {
|
|
36295
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36296
|
+
logger82.log({ level: "error", message: messages });
|
|
36297
|
+
next(new BadRequestError102(messages));
|
|
36298
|
+
return;
|
|
36299
|
+
}
|
|
36300
|
+
const { _id } = value;
|
|
36301
|
+
const data = await _getVisitorTransactionById(_id);
|
|
36302
|
+
res.status(200).json(data);
|
|
36303
|
+
return;
|
|
36087
36304
|
} catch (error) {
|
|
36088
|
-
|
|
36305
|
+
logger82.log({ level: "error", message: error.message });
|
|
36306
|
+
next(error);
|
|
36307
|
+
return;
|
|
36089
36308
|
}
|
|
36090
36309
|
}
|
|
36091
|
-
|
|
36092
|
-
|
|
36093
|
-
|
|
36094
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36100
|
-
|
|
36101
|
-
|
|
36102
|
-
function useGuestManagementService() {
|
|
36103
|
-
const { add: _add, updateVisitorGuestById: _updateVisitorGuestById } = useGuestManagementRepo();
|
|
36104
|
-
async function add(value) {
|
|
36105
|
-
const session = useAtlas53.getClient()?.startSession();
|
|
36106
|
-
session?.startTransaction();
|
|
36107
|
-
try {
|
|
36108
|
-
await _add(value, session);
|
|
36109
|
-
await session?.commitTransaction();
|
|
36110
|
-
return "Successfully added guest.";
|
|
36111
|
-
} catch (error) {
|
|
36112
|
-
await session?.abortTransaction();
|
|
36113
|
-
throw error;
|
|
36114
|
-
} finally {
|
|
36115
|
-
session?.endSession();
|
|
36310
|
+
async function updateVisitorTansactionById(req, res, next) {
|
|
36311
|
+
const _id = req.params.id;
|
|
36312
|
+
const payload = { _id, ...req.body };
|
|
36313
|
+
const { error } = schemaUpdateVisTrans.validate(payload, {
|
|
36314
|
+
abortEarly: false
|
|
36315
|
+
});
|
|
36316
|
+
if (error) {
|
|
36317
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36318
|
+
logger82.log({ level: "error", message: messages });
|
|
36319
|
+
next(new BadRequestError102(messages));
|
|
36320
|
+
return;
|
|
36116
36321
|
}
|
|
36117
|
-
}
|
|
36118
|
-
async function updateVisitorGuestById(id, value) {
|
|
36119
|
-
const session = useAtlas53.getClient()?.startSession();
|
|
36120
|
-
session?.startTransaction();
|
|
36121
36322
|
try {
|
|
36122
|
-
await
|
|
36123
|
-
|
|
36124
|
-
return
|
|
36125
|
-
} catch (
|
|
36126
|
-
|
|
36127
|
-
|
|
36128
|
-
|
|
36129
|
-
|
|
36323
|
+
await _updateVisitorTransactionById(_id, req.body);
|
|
36324
|
+
res.status(200).json({ message: "Successfully updated visitor transaction." });
|
|
36325
|
+
return;
|
|
36326
|
+
} catch (error2) {
|
|
36327
|
+
logger82.log({ level: "error", message: error2.message });
|
|
36328
|
+
console.log("error", error2);
|
|
36329
|
+
if (error2?.message) {
|
|
36330
|
+
next(new BadRequestError102(error2?.message));
|
|
36331
|
+
} else {
|
|
36332
|
+
next(error2);
|
|
36333
|
+
}
|
|
36334
|
+
return;
|
|
36130
36335
|
}
|
|
36131
36336
|
}
|
|
36132
|
-
|
|
36133
|
-
|
|
36134
|
-
|
|
36135
|
-
|
|
36136
|
-
}
|
|
36137
|
-
|
|
36138
|
-
// src/controllers/guest-management.controller.ts
|
|
36139
|
-
import Joi59 from "joi";
|
|
36140
|
-
import { BadRequestError as BadRequestError103, logger as logger83 } from "@7365admin1/node-server-utils";
|
|
36141
|
-
function useGuestManagementController() {
|
|
36142
|
-
const { add: _add, updateVisitorGuestById: _updateVisitorGuestById } = useGuestManagementService();
|
|
36143
|
-
const { getAll: _getAll, deleteVisitorGuest: _deleteVisitorGuest } = useGuestManagementRepo();
|
|
36144
|
-
async function add(req, res, next) {
|
|
36145
|
-
const payload = { ...req.body };
|
|
36146
|
-
const { error } = schemaGuestManagement.validate(payload, {
|
|
36147
|
-
abortEarly: false
|
|
36148
|
-
});
|
|
36337
|
+
async function deleteVisitorTransaction(req, res, next) {
|
|
36338
|
+
const validation = Joi57.string().hex().required();
|
|
36339
|
+
const _id = req.params.id;
|
|
36340
|
+
const { error } = validation.validate(_id);
|
|
36149
36341
|
if (error) {
|
|
36150
|
-
|
|
36151
|
-
|
|
36152
|
-
next(new BadRequestError103(messages));
|
|
36342
|
+
logger82.log({ level: "error", message: error.message });
|
|
36343
|
+
next(new BadRequestError102(error.message));
|
|
36153
36344
|
return;
|
|
36154
36345
|
}
|
|
36155
36346
|
try {
|
|
36156
|
-
|
|
36157
|
-
res.status(
|
|
36347
|
+
await _deleteVisitorTransaction(_id);
|
|
36348
|
+
res.status(200).json({ message: "Successfully deleted visitor transaction." });
|
|
36158
36349
|
return;
|
|
36159
36350
|
} catch (error2) {
|
|
36160
|
-
|
|
36351
|
+
logger82.log({ level: "error", message: error2.message });
|
|
36161
36352
|
next(error2);
|
|
36162
36353
|
return;
|
|
36163
36354
|
}
|
|
36164
36355
|
}
|
|
36165
|
-
async function
|
|
36166
|
-
const
|
|
36167
|
-
|
|
36168
|
-
|
|
36169
|
-
|
|
36170
|
-
|
|
36171
|
-
|
|
36172
|
-
|
|
36173
|
-
|
|
36174
|
-
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
|
|
36179
|
-
|
|
36180
|
-
abortEarly: false
|
|
36356
|
+
async function inviteVisitor(req, res, next) {
|
|
36357
|
+
const schema2 = Joi57.object({
|
|
36358
|
+
expectedCheckIn: Joi57.string().isoDate().required(),
|
|
36359
|
+
arrivalTime: Joi57.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().allow(null, "").messages({
|
|
36360
|
+
"string.pattern.base": "arrivalTime must be in HH:mm format (e.g. 09:30, 18:45)"
|
|
36361
|
+
}),
|
|
36362
|
+
duration: Joi57.number().integer().optional().allow(null, ""),
|
|
36363
|
+
name: Joi57.string().required(),
|
|
36364
|
+
contact: Joi57.string().optional().allow(null, ""),
|
|
36365
|
+
email: Joi57.string().email().optional().allow(null, ""),
|
|
36366
|
+
plateNumber: Joi57.string().optional().allow(null, ""),
|
|
36367
|
+
isOvernightParking: Joi57.boolean().optional().default(false),
|
|
36368
|
+
numberOfPassengers: Joi57.number().integer().optional().allow(null, ""),
|
|
36369
|
+
purpose: Joi57.string().optional().allow(null, ""),
|
|
36370
|
+
inviterUserId: Joi57.string().hex().length(24).required()
|
|
36181
36371
|
});
|
|
36372
|
+
const { error, value } = schema2.validate(
|
|
36373
|
+
{ inviterUserId: req.params.inviterId, ...req.body },
|
|
36374
|
+
{ abortEarly: false }
|
|
36375
|
+
);
|
|
36182
36376
|
if (error) {
|
|
36183
36377
|
const messages = error.details.map((d) => d.message).join(", ");
|
|
36184
|
-
|
|
36185
|
-
next(new
|
|
36378
|
+
logger82.log({ level: "error", message: messages });
|
|
36379
|
+
next(new BadRequestError102(messages));
|
|
36186
36380
|
return;
|
|
36187
36381
|
}
|
|
36188
|
-
const
|
|
36189
|
-
|
|
36190
|
-
|
|
36191
|
-
|
|
36192
|
-
|
|
36193
|
-
|
|
36194
|
-
|
|
36195
|
-
|
|
36196
|
-
|
|
36197
|
-
|
|
36198
|
-
|
|
36199
|
-
|
|
36200
|
-
|
|
36201
|
-
|
|
36202
|
-
|
|
36382
|
+
const {
|
|
36383
|
+
expectedCheckIn,
|
|
36384
|
+
arrivalTime,
|
|
36385
|
+
duration,
|
|
36386
|
+
name,
|
|
36387
|
+
contact,
|
|
36388
|
+
email,
|
|
36389
|
+
plateNumber,
|
|
36390
|
+
isOvernightParking,
|
|
36391
|
+
numberOfPassengers,
|
|
36392
|
+
purpose,
|
|
36393
|
+
inviterUserId
|
|
36394
|
+
} = value;
|
|
36395
|
+
const rest = {
|
|
36396
|
+
expectedCheckIn,
|
|
36397
|
+
arrivalTime,
|
|
36398
|
+
duration,
|
|
36399
|
+
name,
|
|
36400
|
+
contact,
|
|
36401
|
+
email,
|
|
36402
|
+
plateNumber,
|
|
36403
|
+
isOvernightParking,
|
|
36404
|
+
numberOfPassengers,
|
|
36405
|
+
purpose
|
|
36406
|
+
};
|
|
36407
|
+
try {
|
|
36408
|
+
const result = await _inviteVisitor(rest, inviterUserId);
|
|
36409
|
+
if (result) {
|
|
36410
|
+
await resendEmailInvitedResident(result, inviterUserId).catch((error2) => {
|
|
36411
|
+
console.error(`Failed to send invitation email: ${error2.message}`);
|
|
36412
|
+
logger82.log({ level: "error", message: `Failed to send invitation email: ${error2.message}` });
|
|
36413
|
+
});
|
|
36203
36414
|
}
|
|
36204
|
-
|
|
36415
|
+
res.status(200).json({ insertedId: result });
|
|
36416
|
+
return;
|
|
36417
|
+
} catch (error2) {
|
|
36418
|
+
logger82.log({ level: "error", message: error2.message });
|
|
36419
|
+
if (error2?.message) {
|
|
36420
|
+
next(new BadRequestError102(error2?.message));
|
|
36421
|
+
} else {
|
|
36422
|
+
next(error2);
|
|
36423
|
+
}
|
|
36424
|
+
}
|
|
36425
|
+
}
|
|
36426
|
+
return {
|
|
36427
|
+
add,
|
|
36428
|
+
getAll,
|
|
36429
|
+
updateVisitorTansactionById,
|
|
36430
|
+
deleteVisitorTransaction,
|
|
36431
|
+
inviteVisitor,
|
|
36432
|
+
getVisitorTransactionById
|
|
36433
|
+
// changeVisitorTransactionKeysById,
|
|
36434
|
+
};
|
|
36435
|
+
}
|
|
36436
|
+
|
|
36437
|
+
// src/models/guest-management.model.ts
|
|
36438
|
+
import Joi58 from "joi";
|
|
36439
|
+
import { ObjectId as ObjectId65 } from "mongodb";
|
|
36440
|
+
var GuestStatus = /* @__PURE__ */ ((GuestStatus2) => {
|
|
36441
|
+
GuestStatus2["PENDING"] = "pending";
|
|
36442
|
+
GuestStatus2["ACTIVE"] = "active";
|
|
36443
|
+
return GuestStatus2;
|
|
36444
|
+
})(GuestStatus || {});
|
|
36445
|
+
var GuestSort = /* @__PURE__ */ ((GuestSort2) => {
|
|
36446
|
+
GuestSort2["CREATED_AT"] = "createdAt";
|
|
36447
|
+
GuestSort2["NAME"] = "name";
|
|
36448
|
+
GuestSort2["ID"] = "_id";
|
|
36449
|
+
return GuestSort2;
|
|
36450
|
+
})(GuestSort || {});
|
|
36451
|
+
var schemaGuestManagement = Joi58.object({
|
|
36452
|
+
name: Joi58.string().required(),
|
|
36453
|
+
contact: Joi58.string().optional().allow(null, ""),
|
|
36454
|
+
block: Joi58.number().required(),
|
|
36455
|
+
level: Joi58.string().required(),
|
|
36456
|
+
unit: Joi58.string().required(),
|
|
36457
|
+
start: Joi58.date().required(),
|
|
36458
|
+
end: Joi58.date().required(),
|
|
36459
|
+
status: Joi58.string().optional().allow(null, ""),
|
|
36460
|
+
nric: Joi58.string().optional().allow(null, ""),
|
|
36461
|
+
remarks: Joi58.string().optional().allow(null, ""),
|
|
36462
|
+
org: Joi58.string().hex().length(24).required(),
|
|
36463
|
+
site: Joi58.string().hex().length(24).required(),
|
|
36464
|
+
plateNumber: Joi58.string().optional().allow(null, ""),
|
|
36465
|
+
unitName: Joi58.string().optional().allow(null, "")
|
|
36466
|
+
});
|
|
36467
|
+
var schemaUpdateGuestManagement = Joi58.object({
|
|
36468
|
+
_id: Joi58.string().hex().length(24).required(),
|
|
36469
|
+
name: Joi58.string().optional().allow(null, ""),
|
|
36470
|
+
contact: Joi58.string().optional().allow(null, ""),
|
|
36471
|
+
block: Joi58.number().optional().allow(null, ""),
|
|
36472
|
+
level: Joi58.string().optional().allow(null, ""),
|
|
36473
|
+
unit: Joi58.string().optional().allow(null, ""),
|
|
36474
|
+
start: Joi58.date().optional().allow(null, ""),
|
|
36475
|
+
end: Joi58.date().optional().allow(null, ""),
|
|
36476
|
+
status: Joi58.string().optional().allow(null, ""),
|
|
36477
|
+
unitName: Joi58.string().optional().allow(null, "")
|
|
36478
|
+
});
|
|
36479
|
+
function MGuestManagement(value) {
|
|
36480
|
+
const { error } = schemaGuestManagement.validate(value);
|
|
36481
|
+
if (error) {
|
|
36482
|
+
throw new Error(error.details[0].message);
|
|
36483
|
+
}
|
|
36484
|
+
if (value._id && typeof value._id === "string") {
|
|
36205
36485
|
try {
|
|
36206
|
-
|
|
36207
|
-
search,
|
|
36208
|
-
page,
|
|
36209
|
-
limit,
|
|
36210
|
-
sort: sortObj,
|
|
36211
|
-
status,
|
|
36212
|
-
dateFrom,
|
|
36213
|
-
dateTo,
|
|
36214
|
-
org,
|
|
36215
|
-
site
|
|
36216
|
-
});
|
|
36217
|
-
res.status(200).json(data);
|
|
36218
|
-
return;
|
|
36486
|
+
value._id = new ObjectId65(value._id);
|
|
36219
36487
|
} catch (error2) {
|
|
36220
|
-
|
|
36221
|
-
next(error2);
|
|
36222
|
-
return;
|
|
36488
|
+
throw new Error("Invalid ID.");
|
|
36223
36489
|
}
|
|
36224
36490
|
}
|
|
36225
|
-
|
|
36226
|
-
const _id = req.params.id;
|
|
36227
|
-
const payload = { _id, ...req.body };
|
|
36228
|
-
const { error } = schemaUpdateGuestManagement.validate(payload, {
|
|
36229
|
-
abortEarly: false
|
|
36230
|
-
});
|
|
36231
|
-
if (error) {
|
|
36232
|
-
const messages = error.details.map((d) => d.message).join(", ");
|
|
36233
|
-
logger83.log({ level: "error", message: messages });
|
|
36234
|
-
next(new BadRequestError103(messages));
|
|
36235
|
-
return;
|
|
36236
|
-
}
|
|
36491
|
+
if (value.org && typeof value.org === "string") {
|
|
36237
36492
|
try {
|
|
36238
|
-
|
|
36239
|
-
res.status(200).json({ message: result });
|
|
36240
|
-
return;
|
|
36493
|
+
value.org = new ObjectId65(value.org);
|
|
36241
36494
|
} catch (error2) {
|
|
36242
|
-
|
|
36243
|
-
next(error2);
|
|
36244
|
-
return;
|
|
36495
|
+
throw new Error("Invalid org ID.");
|
|
36245
36496
|
}
|
|
36246
36497
|
}
|
|
36247
|
-
|
|
36248
|
-
const validation = Joi59.string().hex().required();
|
|
36249
|
-
const _id = req.params.id;
|
|
36250
|
-
const { error } = validation.validate(_id);
|
|
36251
|
-
if (error) {
|
|
36252
|
-
logger83.log({ level: "error", message: error.message });
|
|
36253
|
-
next(new BadRequestError103(error.message));
|
|
36254
|
-
return;
|
|
36255
|
-
}
|
|
36498
|
+
if (value.site && typeof value.site === "string") {
|
|
36256
36499
|
try {
|
|
36257
|
-
|
|
36258
|
-
res.status(200).json({ message: "Successfully deleted visitor guest." });
|
|
36259
|
-
return;
|
|
36500
|
+
value.site = new ObjectId65(value.site);
|
|
36260
36501
|
} catch (error2) {
|
|
36261
|
-
|
|
36262
|
-
next(error2);
|
|
36263
|
-
return;
|
|
36502
|
+
throw new Error("Invalid site ID.");
|
|
36264
36503
|
}
|
|
36265
36504
|
}
|
|
36266
36505
|
return {
|
|
36267
|
-
|
|
36268
|
-
|
|
36269
|
-
|
|
36270
|
-
|
|
36506
|
+
_id: value._id,
|
|
36507
|
+
name: value.name,
|
|
36508
|
+
contact: value.contact,
|
|
36509
|
+
block: value.block,
|
|
36510
|
+
level: value.level,
|
|
36511
|
+
unit: value.unit,
|
|
36512
|
+
start: value.start,
|
|
36513
|
+
end: value.end,
|
|
36514
|
+
status: value.status ?? "active",
|
|
36515
|
+
nric: value.nric,
|
|
36516
|
+
remarks: value.remarks,
|
|
36517
|
+
org: value.org,
|
|
36518
|
+
site: value.site,
|
|
36519
|
+
plateNumber: value.plateNumber,
|
|
36520
|
+
unitName: value.unitName,
|
|
36521
|
+
createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
36522
|
+
updatedAt: value.updatedAt,
|
|
36523
|
+
deletedAt: value.deletedAt
|
|
36271
36524
|
};
|
|
36272
36525
|
}
|
|
36273
36526
|
|
|
36274
|
-
// src/
|
|
36275
|
-
import Joi60 from "joi";
|
|
36276
|
-
import { BadRequestError as BadRequestError105, logger as logger85 } from "@7365admin1/node-server-utils";
|
|
36277
|
-
|
|
36278
|
-
// src/services/person.service.ts
|
|
36527
|
+
// src/repositories/guest-management.repo.ts
|
|
36279
36528
|
import {
|
|
36280
|
-
BadRequestError as
|
|
36281
|
-
|
|
36282
|
-
|
|
36283
|
-
|
|
36284
|
-
|
|
36285
|
-
|
|
36286
|
-
|
|
36287
|
-
getDirectory as getDirectory3
|
|
36529
|
+
BadRequestError as BadRequestError103,
|
|
36530
|
+
InternalServerError as InternalServerError34,
|
|
36531
|
+
logger as logger83,
|
|
36532
|
+
makeCacheKey as makeCacheKey31,
|
|
36533
|
+
paginate as paginate26,
|
|
36534
|
+
useAtlas as useAtlas53,
|
|
36535
|
+
useCache as useCache33
|
|
36288
36536
|
} from "@7365admin1/node-server-utils";
|
|
36289
36537
|
import { ObjectId as ObjectId66 } from "mongodb";
|
|
36290
|
-
|
|
36291
|
-
|
|
36292
|
-
|
|
36293
|
-
|
|
36294
|
-
|
|
36295
|
-
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
|
|
36299
|
-
|
|
36300
|
-
|
|
36301
|
-
add: _add,
|
|
36302
|
-
getById: _getById,
|
|
36303
|
-
updateById: _updateById,
|
|
36304
|
-
getByUserId: _getByUserId,
|
|
36305
|
-
reviewResidentPerson: _reviewResidentPerson,
|
|
36306
|
-
getApprovedPersonByPlateNumber
|
|
36307
|
-
} = usePersonRepo();
|
|
36308
|
-
const { addPlateNumber: _addPlateNumber } = useDahuaService();
|
|
36309
|
-
const {
|
|
36310
|
-
createUser: addUser,
|
|
36311
|
-
getUserByEmail,
|
|
36312
|
-
updateUserFieldById: _updateUserFieldById,
|
|
36313
|
-
getUserById,
|
|
36314
|
-
updateUserOrgById: _updateUserOrgById,
|
|
36315
|
-
updateUserUnitById: _updateUserUnitById
|
|
36316
|
-
} = useUserRepo();
|
|
36317
|
-
const { add: addMember } = useMemberRepo();
|
|
36318
|
-
const { getById: _getUnitById, updateById: updateUnitById } = useBuildingUnitRepo();
|
|
36319
|
-
const { getAllCameraWithPassword: _getAllSiteCameras, getCamerasBySiteId } = useSiteCameraRepo();
|
|
36320
|
-
const { updateStatusById } = useFileRepo();
|
|
36321
|
-
const { getById: getOrgById } = useOrgRepo();
|
|
36322
|
-
const { getSiteById } = useSiteRepo();
|
|
36323
|
-
const { add: addVehicle } = useVehicleService();
|
|
36324
|
-
async function add(value) {
|
|
36325
|
-
const session = useAtlas54.getClient()?.startSession();
|
|
36326
|
-
if (!session) {
|
|
36327
|
-
throw new Error("Unable to start session for people service.");
|
|
36328
|
-
}
|
|
36538
|
+
var guests_namespace_collection = "visitor.guests";
|
|
36539
|
+
function useGuestManagementRepo() {
|
|
36540
|
+
const db = useAtlas53.getDb();
|
|
36541
|
+
if (!db) {
|
|
36542
|
+
throw new InternalServerError34("Unable to connect to server.");
|
|
36543
|
+
}
|
|
36544
|
+
const collection = db.collection(guests_namespace_collection);
|
|
36545
|
+
const { delNamespace, getCache, setCache } = useCache33(
|
|
36546
|
+
guests_namespace_collection
|
|
36547
|
+
);
|
|
36548
|
+
async function createTextIndex() {
|
|
36329
36549
|
try {
|
|
36330
|
-
|
|
36331
|
-
|
|
36332
|
-
|
|
36333
|
-
|
|
36334
|
-
|
|
36335
|
-
|
|
36336
|
-
|
|
36337
|
-
|
|
36338
|
-
|
|
36339
|
-
|
|
36340
|
-
throw new NotFoundError23("File not found.");
|
|
36341
|
-
}
|
|
36342
|
-
}
|
|
36343
|
-
}
|
|
36344
|
-
const isValidType = ["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
|
|
36345
|
-
value?.type
|
|
36346
|
-
);
|
|
36347
|
-
if (value.email && value.password && value.name && isValidType) {
|
|
36348
|
-
const _user = await getUserByEmail(value.email);
|
|
36349
|
-
if (_user) {
|
|
36350
|
-
throw new BadRequestError104(`User already exists: ${value.email}.`);
|
|
36351
|
-
}
|
|
36352
|
-
const hashedPassword = await hashPassword3(value.password);
|
|
36353
|
-
const user = {
|
|
36354
|
-
email: value.email,
|
|
36355
|
-
password: hashedPassword,
|
|
36356
|
-
name: value.name,
|
|
36357
|
-
status: value.platform == "mobile" ? "pending" : "active",
|
|
36358
|
-
defaultOrg: value.org?.toString() || "",
|
|
36359
|
-
block: value.block,
|
|
36360
|
-
level: value.level,
|
|
36361
|
-
type: value.type,
|
|
36362
|
-
unitName: value.unitName,
|
|
36363
|
-
contact: value.contact,
|
|
36364
|
-
site: value.site?.toString() || "",
|
|
36365
|
-
unitId: value.unit?.toString() || ""
|
|
36366
|
-
};
|
|
36367
|
-
const userId = await addUser(user, session);
|
|
36368
|
-
value.user = userId.toString();
|
|
36369
|
-
let org = null;
|
|
36370
|
-
if (userId) {
|
|
36371
|
-
if (value?.org) {
|
|
36372
|
-
org = await getOrgById(value.org);
|
|
36373
|
-
if (!org) {
|
|
36374
|
-
value.org = "";
|
|
36375
|
-
}
|
|
36376
|
-
}
|
|
36377
|
-
let site;
|
|
36378
|
-
if (value.site) {
|
|
36379
|
-
site = await getSiteById(value.site);
|
|
36380
|
-
}
|
|
36381
|
-
await addMember(
|
|
36382
|
-
{
|
|
36383
|
-
org: value.org?.toString() || "",
|
|
36384
|
-
orgName: org?.name || "",
|
|
36385
|
-
user: userId.toString(),
|
|
36386
|
-
name: value.name,
|
|
36387
|
-
role: "",
|
|
36388
|
-
type: "resident",
|
|
36389
|
-
siteId: value.site?.toString() || "",
|
|
36390
|
-
siteName: site?.name || ""
|
|
36391
|
-
},
|
|
36392
|
-
session
|
|
36393
|
-
);
|
|
36394
|
-
}
|
|
36395
|
-
}
|
|
36396
|
-
if (value.isOwner && value.unit) {
|
|
36397
|
-
const unit = await _getUnitById(value.unit.toString());
|
|
36398
|
-
await updateUnitById(
|
|
36399
|
-
unit?._id?.toString() || "",
|
|
36400
|
-
{ ownerName: value.name, owner: value._id },
|
|
36401
|
-
session
|
|
36402
|
-
);
|
|
36403
|
-
}
|
|
36404
|
-
await _add(value, session);
|
|
36405
|
-
await session.commitTransaction();
|
|
36406
|
-
if (value.email && isValidType && value.platform === "mobile") {
|
|
36407
|
-
const emailContent = compileHandlebar3({
|
|
36408
|
-
context: { name: value.name },
|
|
36409
|
-
filePath: getDirectory3(
|
|
36410
|
-
__dirname,
|
|
36411
|
-
"./public/handlebars/registration-pending"
|
|
36412
|
-
)
|
|
36413
|
-
});
|
|
36414
|
-
await mailer.sendMail({
|
|
36415
|
-
to: value.email,
|
|
36416
|
-
subject: "Registration Received \u2013 Pending Review",
|
|
36417
|
-
html: emailContent,
|
|
36418
|
-
sender: "iService365"
|
|
36419
|
-
}).catch((error) => {
|
|
36420
|
-
logger84.error(`Failed to send registration pending email: ${error}`);
|
|
36421
|
-
});
|
|
36422
|
-
}
|
|
36423
|
-
return "People added successfully.";
|
|
36424
|
-
} catch (error) {
|
|
36425
|
-
logger84.error("Error in people service add:", error);
|
|
36426
|
-
await session.abortTransaction();
|
|
36427
|
-
throw error;
|
|
36428
|
-
} finally {
|
|
36429
|
-
session.endSession();
|
|
36550
|
+
await collection.createIndex({
|
|
36551
|
+
name: "text",
|
|
36552
|
+
plateNumber: "text",
|
|
36553
|
+
company: "text",
|
|
36554
|
+
level: "text",
|
|
36555
|
+
unitName: "text",
|
|
36556
|
+
contact: "text"
|
|
36557
|
+
});
|
|
36558
|
+
} catch (error) {
|
|
36559
|
+
throw new InternalServerError34("Failed to create text index on guest.");
|
|
36430
36560
|
}
|
|
36431
36561
|
}
|
|
36432
|
-
async function
|
|
36433
|
-
const session = useAtlas54.getClient()?.startSession();
|
|
36434
|
-
if (!session) {
|
|
36435
|
-
throw new BadRequestError104("Database session not available.");
|
|
36436
|
-
}
|
|
36562
|
+
async function add(value, session) {
|
|
36437
36563
|
try {
|
|
36438
|
-
|
|
36439
|
-
const
|
|
36440
|
-
|
|
36441
|
-
|
|
36442
|
-
|
|
36443
|
-
const isNameUpdated = typeof value.name === "string" && value.name.trim() !== person.name;
|
|
36444
|
-
const isOwnerChanged = value.isOwner !== person.isOwner;
|
|
36445
|
-
if (value.org && typeof value.org === "string") {
|
|
36446
|
-
value.org = new ObjectId66(value.org);
|
|
36447
|
-
}
|
|
36448
|
-
if (value.site && typeof value.site === "string") {
|
|
36449
|
-
value.site = new ObjectId66(value.site);
|
|
36450
|
-
}
|
|
36451
|
-
if (value.block && typeof value.block === "string") {
|
|
36452
|
-
value.block = new ObjectId66(value.block);
|
|
36453
|
-
}
|
|
36454
|
-
if (value.level && typeof value.level === "string") {
|
|
36455
|
-
value.level = new ObjectId66(value.level);
|
|
36456
|
-
}
|
|
36457
|
-
if (value.unit && typeof value.unit === "string") {
|
|
36458
|
-
value.unit = new ObjectId66(value.unit);
|
|
36459
|
-
}
|
|
36460
|
-
const isOrgChanged = value.org && person.org?.toString() !== value.org.toString();
|
|
36461
|
-
const toKey = (v) => v === null || v === void 0 || v === "" ? "" : v.toString();
|
|
36462
|
-
const isBlockChanged = "block" in value && toKey(value.block) !== toKey(person.block);
|
|
36463
|
-
const isLevelChanged = "level" in value && toKey(value.level) !== toKey(person.level);
|
|
36464
|
-
const isUnitChanged = "unit" in value && toKey(value.unit) !== toKey(person.unit);
|
|
36465
|
-
await _updateById(_id, value, session);
|
|
36466
|
-
if (isOrgChanged && person.user) {
|
|
36467
|
-
await _updateUserOrgById(
|
|
36468
|
-
person.user.toString(),
|
|
36469
|
-
value.org,
|
|
36470
|
-
session
|
|
36564
|
+
value = MGuestManagement(value);
|
|
36565
|
+
const res = await collection.insertOne(value, { session });
|
|
36566
|
+
delNamespace().then(() => {
|
|
36567
|
+
logger83.info(
|
|
36568
|
+
`Cache cleared for namespace: ${guests_namespace_collection}`
|
|
36471
36569
|
);
|
|
36472
|
-
}
|
|
36473
|
-
|
|
36474
|
-
|
|
36475
|
-
|
|
36476
|
-
userUnitPayload.block = value.block ?? null;
|
|
36477
|
-
}
|
|
36478
|
-
if (isLevelChanged) {
|
|
36479
|
-
userUnitPayload.level = value.level ?? null;
|
|
36480
|
-
}
|
|
36481
|
-
if (isUnitChanged) {
|
|
36482
|
-
userUnitPayload.unitId = value.unit ?? null;
|
|
36483
|
-
if ("unitName" in value) {
|
|
36484
|
-
userUnitPayload.unitName = value.unitName ?? "";
|
|
36485
|
-
}
|
|
36486
|
-
}
|
|
36487
|
-
await _updateUserUnitById(
|
|
36488
|
-
person.user.toString(),
|
|
36489
|
-
userUnitPayload,
|
|
36490
|
-
session
|
|
36570
|
+
}).catch((err) => {
|
|
36571
|
+
logger83.error(
|
|
36572
|
+
`Failed to clear cache for namespace: ${guests_namespace_collection}`,
|
|
36573
|
+
err
|
|
36491
36574
|
);
|
|
36492
|
-
}
|
|
36493
|
-
if (value.unit && (isNameUpdated || isOwnerChanged || value.isOwner)) {
|
|
36494
|
-
const unit = await _getUnitById(value.unit.toString());
|
|
36495
|
-
if (unit) {
|
|
36496
|
-
let updatePayload = {};
|
|
36497
|
-
if (isOwnerChanged) {
|
|
36498
|
-
if (value.isOwner) {
|
|
36499
|
-
updatePayload.owner = _id;
|
|
36500
|
-
updatePayload.ownerName = value.name || "";
|
|
36501
|
-
} else {
|
|
36502
|
-
updatePayload.owner = "";
|
|
36503
|
-
updatePayload.ownerName = "";
|
|
36504
|
-
}
|
|
36505
|
-
}
|
|
36506
|
-
if (isNameUpdated && value.isOwner && unit.owner?.toString() === _id.toString()) {
|
|
36507
|
-
updatePayload.ownerName = value.name;
|
|
36508
|
-
}
|
|
36509
|
-
if (value.isOwner && (!unit.owner || !unit.ownerName)) {
|
|
36510
|
-
updatePayload.owner = _id;
|
|
36511
|
-
updatePayload.ownerName = value.name || "";
|
|
36512
|
-
}
|
|
36513
|
-
if (Object.keys(updatePayload).length > 0 && unit && unit._id) {
|
|
36514
|
-
await updateUnitById(
|
|
36515
|
-
unit._id.toString(),
|
|
36516
|
-
updatePayload,
|
|
36517
|
-
session
|
|
36518
|
-
);
|
|
36519
|
-
}
|
|
36520
|
-
}
|
|
36521
|
-
}
|
|
36522
|
-
if (isUnitChanged && person.isOwner && person.unit) {
|
|
36523
|
-
const previousUnit = await _getUnitById(person.unit.toString());
|
|
36524
|
-
if (previousUnit && previousUnit._id && previousUnit.owner?.toString() === person?._id?.toString()) {
|
|
36525
|
-
await updateUnitById(
|
|
36526
|
-
previousUnit._id.toString(),
|
|
36527
|
-
{ owner: "", ownerName: "" },
|
|
36528
|
-
session
|
|
36529
|
-
);
|
|
36530
|
-
}
|
|
36531
|
-
}
|
|
36532
|
-
await session.commitTransaction();
|
|
36533
|
-
return "Person updated successfully.";
|
|
36534
|
-
} catch (error) {
|
|
36535
|
-
logger84.log({
|
|
36536
|
-
level: "error",
|
|
36537
|
-
message: `Failed to add site billing item: ${error.message}`
|
|
36538
36575
|
});
|
|
36539
|
-
|
|
36576
|
+
return res.insertedId;
|
|
36577
|
+
} catch (error) {
|
|
36578
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
36579
|
+
if (isDuplicated) {
|
|
36580
|
+
throw new BadRequestError103("Visitor already exists.");
|
|
36581
|
+
}
|
|
36540
36582
|
throw error;
|
|
36541
|
-
} finally {
|
|
36542
|
-
session.endSession();
|
|
36543
36583
|
}
|
|
36544
36584
|
}
|
|
36545
|
-
async function
|
|
36546
|
-
|
|
36547
|
-
|
|
36548
|
-
|
|
36549
|
-
|
|
36550
|
-
|
|
36551
|
-
|
|
36552
|
-
|
|
36553
|
-
|
|
36554
|
-
|
|
36555
|
-
|
|
36585
|
+
async function getAll({
|
|
36586
|
+
search = "",
|
|
36587
|
+
page = 1,
|
|
36588
|
+
limit = 10,
|
|
36589
|
+
sort = {},
|
|
36590
|
+
status = "active",
|
|
36591
|
+
dateFrom,
|
|
36592
|
+
dateTo,
|
|
36593
|
+
org = "",
|
|
36594
|
+
site = ""
|
|
36595
|
+
}, session) {
|
|
36596
|
+
page = page > 0 ? page - 1 : 0;
|
|
36597
|
+
const start = dateFrom ? { $gte: new Date(dateFrom).toISOString() } : void 0;
|
|
36598
|
+
const end = dateTo ? {
|
|
36599
|
+
$or: [
|
|
36600
|
+
{ end: { $lte: new Date(dateTo).toISOString() } },
|
|
36601
|
+
{ end: null }
|
|
36602
|
+
]
|
|
36603
|
+
} : void 0;
|
|
36604
|
+
const query = {
|
|
36605
|
+
status,
|
|
36606
|
+
...start && { start },
|
|
36607
|
+
...end,
|
|
36608
|
+
...search && { $text: { $search: search } },
|
|
36609
|
+
...ObjectId66.isValid(org) && { org: new ObjectId66(org) },
|
|
36610
|
+
...ObjectId66.isValid(site) && { site: new ObjectId66(site) }
|
|
36611
|
+
};
|
|
36612
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
36613
|
+
const cacheOptions = {
|
|
36614
|
+
sort: JSON.stringify(sort),
|
|
36615
|
+
page,
|
|
36616
|
+
limit,
|
|
36617
|
+
status,
|
|
36618
|
+
...start && { start: JSON.stringify(start) },
|
|
36619
|
+
...dateTo && { end: JSON.stringify(end) },
|
|
36620
|
+
...search && { search },
|
|
36621
|
+
...query.org && { org: query.org.toString() },
|
|
36622
|
+
...query.site && { site: query.site.toString() }
|
|
36623
|
+
};
|
|
36624
|
+
const cacheKey = makeCacheKey31(guests_namespace_collection, cacheOptions);
|
|
36625
|
+
const cachedData = await getCache(cacheKey);
|
|
36626
|
+
if (cachedData) {
|
|
36627
|
+
logger83.info(`Cache hit for key: ${cacheKey}`);
|
|
36628
|
+
return cachedData;
|
|
36556
36629
|
}
|
|
36557
|
-
return { start: "", end: "" };
|
|
36558
|
-
}
|
|
36559
|
-
async function reviewResidentPerson(id, value) {
|
|
36560
|
-
const session = useAtlas54.getClient()?.startSession();
|
|
36561
|
-
session?.startTransaction();
|
|
36562
36630
|
try {
|
|
36563
|
-
const
|
|
36564
|
-
|
|
36565
|
-
|
|
36566
|
-
|
|
36567
|
-
|
|
36568
|
-
|
|
36569
|
-
|
|
36570
|
-
|
|
36571
|
-
|
|
36572
|
-
|
|
36573
|
-
const
|
|
36574
|
-
|
|
36575
|
-
|
|
36576
|
-
|
|
36577
|
-
|
|
36578
|
-
|
|
36579
|
-
|
|
36580
|
-
|
|
36581
|
-
|
|
36582
|
-
|
|
36583
|
-
|
|
36584
|
-
|
|
36585
|
-
|
|
36586
|
-
|
|
36587
|
-
|
|
36588
|
-
|
|
36589
|
-
|
|
36590
|
-
|
|
36591
|
-
|
|
36592
|
-
|
|
36593
|
-
|
|
36594
|
-
|
|
36595
|
-
|
|
36596
|
-
|
|
36597
|
-
|
|
36598
|
-
|
|
36599
|
-
let statusLogo = statusIconPath;
|
|
36600
|
-
if (person.user) {
|
|
36601
|
-
let userStatus = "";
|
|
36602
|
-
switch (reviewStatus) {
|
|
36603
|
-
case "approved":
|
|
36604
|
-
userStatus = "active";
|
|
36605
|
-
if (person.plateNumber && person.plateNumber.trim() !== "") {
|
|
36606
|
-
const anprCameraList = await getCamerasBySiteId(
|
|
36607
|
-
person.site.toString()
|
|
36608
|
-
);
|
|
36609
|
-
const existingPerson = await getApprovedPersonByPlateNumber(
|
|
36610
|
-
person.plateNumber,
|
|
36611
|
-
id
|
|
36612
|
-
);
|
|
36613
|
-
if (anprCameraList.length > 0 && !existingPerson) {
|
|
36614
|
-
await addVehicle(vehicle, session);
|
|
36615
|
-
}
|
|
36616
|
-
}
|
|
36617
|
-
title = "Your account has been approved";
|
|
36618
|
-
hideLogin = false;
|
|
36619
|
-
statusLogo = `${statusIconPath}/approved-icon.png`;
|
|
36620
|
-
message2 = `Welcome, ${person.name}! Thank you for signing up; we're excited to have you join us!`;
|
|
36621
|
-
break;
|
|
36622
|
-
case "resubmit":
|
|
36623
|
-
userStatus = "resubmit";
|
|
36624
|
-
title = "We request resubmission for your application";
|
|
36625
|
-
hideLogin = false;
|
|
36626
|
-
statusLogo = `${statusIconPath}/resubmission-icon.png`;
|
|
36627
|
-
message2 = `Hi ${person.name}, please resubmit your application with the required updates.${value.remarks ? ` Remarks: ${value.remarks}` : ""}`;
|
|
36628
|
-
break;
|
|
36629
|
-
case "rejected":
|
|
36630
|
-
userStatus = "rejected";
|
|
36631
|
-
title = "Your account request for application has been rejected.";
|
|
36632
|
-
statusLogo = `${statusIconPath}/deleted-icon.png`;
|
|
36633
|
-
message2 = `Hi ${person.name}, unfortunately your application has been rejected.${value.remarks ? ` Remarks: ${value.remarks}` : " Please contact support for more information."}`;
|
|
36634
|
-
break;
|
|
36635
|
-
default:
|
|
36636
|
-
throw new Error("Invalid review status");
|
|
36637
|
-
}
|
|
36638
|
-
const dir = __dirname;
|
|
36639
|
-
const filePath = getDirectory3(
|
|
36640
|
-
dir,
|
|
36641
|
-
"./public/handlebars/resident-remarks-email"
|
|
36631
|
+
const basePipeline = [
|
|
36632
|
+
{ $match: query },
|
|
36633
|
+
{ $sort: sort },
|
|
36634
|
+
{ $skip: page * limit },
|
|
36635
|
+
{ $limit: limit }
|
|
36636
|
+
];
|
|
36637
|
+
const [items, countResult] = await Promise.all([
|
|
36638
|
+
collection.aggregate(basePipeline, { session }).toArray(),
|
|
36639
|
+
collection.aggregate([{ $match: query }, { $count: "total" }], { session }).toArray()
|
|
36640
|
+
]);
|
|
36641
|
+
const totalCount = countResult[0]?.total || 0;
|
|
36642
|
+
const data = paginate26(items, page, limit, totalCount);
|
|
36643
|
+
setCache(cacheKey, data, 15 * 60).then(() => logger83.info(`Cache set for key: ${cacheKey}`)).catch(
|
|
36644
|
+
(err) => logger83.error(`Failed to set cache for key: ${cacheKey}`, err)
|
|
36645
|
+
);
|
|
36646
|
+
return data;
|
|
36647
|
+
} catch (error) {
|
|
36648
|
+
throw error;
|
|
36649
|
+
}
|
|
36650
|
+
}
|
|
36651
|
+
async function updateVisitorGuestById(_id, value, session) {
|
|
36652
|
+
try {
|
|
36653
|
+
_id = new ObjectId66(_id);
|
|
36654
|
+
} catch (error) {
|
|
36655
|
+
throw new BadRequestError103("Invalid visitor transaction ID format.");
|
|
36656
|
+
}
|
|
36657
|
+
value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
36658
|
+
try {
|
|
36659
|
+
const result = await collection.updateOne(
|
|
36660
|
+
{ _id },
|
|
36661
|
+
{ $set: value },
|
|
36662
|
+
{ session }
|
|
36663
|
+
);
|
|
36664
|
+
delNamespace().then(() => {
|
|
36665
|
+
logger83.info(
|
|
36666
|
+
`Cache cleared for namespace: ${guests_namespace_collection}`
|
|
36642
36667
|
);
|
|
36643
|
-
|
|
36644
|
-
|
|
36645
|
-
|
|
36646
|
-
|
|
36647
|
-
hideLogin,
|
|
36648
|
-
logo,
|
|
36649
|
-
statusLogo
|
|
36650
|
-
},
|
|
36651
|
-
filePath
|
|
36652
|
-
});
|
|
36653
|
-
await mailer.sendMail({
|
|
36654
|
-
to: person.email,
|
|
36655
|
-
subject: title,
|
|
36656
|
-
html: emailContent,
|
|
36657
|
-
sender: "iService365"
|
|
36658
|
-
}).catch((error) => {
|
|
36659
|
-
logger84.log({
|
|
36660
|
-
level: "error",
|
|
36661
|
-
message: `Error sending user ${reviewStatus} email: ${error}`
|
|
36662
|
-
});
|
|
36663
|
-
});
|
|
36664
|
-
await _updateUserFieldById(
|
|
36665
|
-
{ _id: person.user.toString(), field: "status", value: userStatus },
|
|
36666
|
-
session
|
|
36668
|
+
}).catch((err) => {
|
|
36669
|
+
logger83.error(
|
|
36670
|
+
`Failed to clear cache for namespace: ${guests_namespace_collection}`,
|
|
36671
|
+
err
|
|
36667
36672
|
);
|
|
36673
|
+
});
|
|
36674
|
+
return result;
|
|
36675
|
+
} catch (error) {
|
|
36676
|
+
throw error;
|
|
36677
|
+
}
|
|
36678
|
+
}
|
|
36679
|
+
async function deleteVisitorGuest(_id) {
|
|
36680
|
+
try {
|
|
36681
|
+
_id = new ObjectId66(_id);
|
|
36682
|
+
} catch (error) {
|
|
36683
|
+
throw new BadRequestError103("Invalid customer ID format.");
|
|
36684
|
+
}
|
|
36685
|
+
try {
|
|
36686
|
+
const updateValue = {
|
|
36687
|
+
status: "deleted",
|
|
36688
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36689
|
+
deletedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
36690
|
+
};
|
|
36691
|
+
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
36692
|
+
if (res.modifiedCount === 0) {
|
|
36693
|
+
throw new InternalServerError34("Unable to delete visitor transaction.");
|
|
36668
36694
|
}
|
|
36695
|
+
delNamespace().then(() => {
|
|
36696
|
+
logger83.info(
|
|
36697
|
+
`Cache cleared for namespace: ${guests_namespace_collection}`
|
|
36698
|
+
);
|
|
36699
|
+
}).catch((err) => {
|
|
36700
|
+
logger83.error(
|
|
36701
|
+
`Failed to clear cache for namespace: ${guests_namespace_collection}`,
|
|
36702
|
+
err
|
|
36703
|
+
);
|
|
36704
|
+
});
|
|
36705
|
+
return res.modifiedCount;
|
|
36706
|
+
} catch (error) {
|
|
36707
|
+
throw error;
|
|
36708
|
+
}
|
|
36709
|
+
}
|
|
36710
|
+
return {
|
|
36711
|
+
add,
|
|
36712
|
+
getAll,
|
|
36713
|
+
updateVisitorGuestById,
|
|
36714
|
+
deleteVisitorGuest,
|
|
36715
|
+
createTextIndex
|
|
36716
|
+
};
|
|
36717
|
+
}
|
|
36718
|
+
|
|
36719
|
+
// src/services/guest-management.service.ts
|
|
36720
|
+
import { useAtlas as useAtlas54 } from "@7365admin1/node-server-utils";
|
|
36721
|
+
function useGuestManagementService() {
|
|
36722
|
+
const { add: _add, updateVisitorGuestById: _updateVisitorGuestById } = useGuestManagementRepo();
|
|
36723
|
+
async function add(value) {
|
|
36724
|
+
const session = useAtlas54.getClient()?.startSession();
|
|
36725
|
+
session?.startTransaction();
|
|
36726
|
+
try {
|
|
36727
|
+
await _add(value, session);
|
|
36669
36728
|
await session?.commitTransaction();
|
|
36670
|
-
return
|
|
36729
|
+
return "Successfully added guest.";
|
|
36671
36730
|
} catch (error) {
|
|
36672
36731
|
await session?.abortTransaction();
|
|
36673
36732
|
throw error;
|
|
@@ -36675,41 +36734,165 @@ function usePersonService() {
|
|
|
36675
36734
|
session?.endSession();
|
|
36676
36735
|
}
|
|
36677
36736
|
}
|
|
36678
|
-
async function
|
|
36737
|
+
async function updateVisitorGuestById(id, value) {
|
|
36679
36738
|
const session = useAtlas54.getClient()?.startSession();
|
|
36680
|
-
|
|
36681
|
-
throw new BadRequestError104("Database session not available.");
|
|
36682
|
-
}
|
|
36739
|
+
session?.startTransaction();
|
|
36683
36740
|
try {
|
|
36684
|
-
session
|
|
36685
|
-
|
|
36686
|
-
|
|
36687
|
-
throw new NotFoundError23("Person not found.");
|
|
36688
|
-
await _updateById(person._id, { status: "deleted" }, session);
|
|
36689
|
-
if (person.user) {
|
|
36690
|
-
await _updateUserFieldById(
|
|
36691
|
-
{ _id: person.user.toString(), field: "status", value: "suspended" },
|
|
36692
|
-
session
|
|
36693
|
-
);
|
|
36694
|
-
}
|
|
36695
|
-
await session.commitTransaction();
|
|
36696
|
-
return "Resident suspended successfully.";
|
|
36741
|
+
await _updateVisitorGuestById(id, value, session);
|
|
36742
|
+
await session?.commitTransaction();
|
|
36743
|
+
return "Successfully updated guest.";
|
|
36697
36744
|
} catch (error) {
|
|
36698
|
-
await session
|
|
36745
|
+
await session?.abortTransaction();
|
|
36699
36746
|
throw error;
|
|
36700
36747
|
} finally {
|
|
36701
|
-
session
|
|
36748
|
+
session?.endSession();
|
|
36702
36749
|
}
|
|
36703
36750
|
}
|
|
36704
36751
|
return {
|
|
36705
36752
|
add,
|
|
36706
|
-
|
|
36707
|
-
|
|
36708
|
-
|
|
36753
|
+
updateVisitorGuestById
|
|
36754
|
+
};
|
|
36755
|
+
}
|
|
36756
|
+
|
|
36757
|
+
// src/controllers/guest-management.controller.ts
|
|
36758
|
+
import Joi59 from "joi";
|
|
36759
|
+
import { BadRequestError as BadRequestError104, logger as logger84 } from "@7365admin1/node-server-utils";
|
|
36760
|
+
function useGuestManagementController() {
|
|
36761
|
+
const { add: _add, updateVisitorGuestById: _updateVisitorGuestById } = useGuestManagementService();
|
|
36762
|
+
const { getAll: _getAll, deleteVisitorGuest: _deleteVisitorGuest } = useGuestManagementRepo();
|
|
36763
|
+
async function add(req, res, next) {
|
|
36764
|
+
const payload = { ...req.body };
|
|
36765
|
+
const { error } = schemaGuestManagement.validate(payload, {
|
|
36766
|
+
abortEarly: false
|
|
36767
|
+
});
|
|
36768
|
+
if (error) {
|
|
36769
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36770
|
+
logger84.log({ level: "error", message: messages });
|
|
36771
|
+
next(new BadRequestError104(messages));
|
|
36772
|
+
return;
|
|
36773
|
+
}
|
|
36774
|
+
try {
|
|
36775
|
+
const data = await _add(payload);
|
|
36776
|
+
res.status(201).json({ message: data });
|
|
36777
|
+
return;
|
|
36778
|
+
} catch (error2) {
|
|
36779
|
+
logger84.log({ level: "error", message: error2.message });
|
|
36780
|
+
next(error2);
|
|
36781
|
+
return;
|
|
36782
|
+
}
|
|
36783
|
+
}
|
|
36784
|
+
async function getAll(req, res, next) {
|
|
36785
|
+
const allowedFields = ["createdAt", "name"];
|
|
36786
|
+
const validation = Joi59.object({
|
|
36787
|
+
page: Joi59.number().integer().min(1).allow("", null).default(1),
|
|
36788
|
+
limit: Joi59.number().integer().min(1).max(100).allow("", null).default(10),
|
|
36789
|
+
sort: Joi59.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
|
|
36790
|
+
status: Joi59.string().optional().allow(null, ""),
|
|
36791
|
+
search: Joi59.string().optional().allow(null, ""),
|
|
36792
|
+
dateFrom: Joi59.date().iso().optional().allow(null, ""),
|
|
36793
|
+
dateTo: Joi59.date().iso().optional().allow(null, ""),
|
|
36794
|
+
org: Joi59.string().optional().allow(null, ""),
|
|
36795
|
+
site: Joi59.string().optional().allow(null, "")
|
|
36796
|
+
});
|
|
36797
|
+
const query = { ...req.query };
|
|
36798
|
+
const { error } = validation.validate(query, {
|
|
36799
|
+
abortEarly: false
|
|
36800
|
+
});
|
|
36801
|
+
if (error) {
|
|
36802
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36803
|
+
logger84.log({ level: "error", message: messages });
|
|
36804
|
+
next(new BadRequestError104(messages));
|
|
36805
|
+
return;
|
|
36806
|
+
}
|
|
36807
|
+
const search = req.query.search ?? "";
|
|
36808
|
+
const page = parseInt(req.query.page ?? "1");
|
|
36809
|
+
const limit = parseInt(req.query.limit ?? "10");
|
|
36810
|
+
const status = req.query.status ?? "active";
|
|
36811
|
+
const dateTo = req.query.dateTo;
|
|
36812
|
+
const dateFrom = req.query.dateFrom;
|
|
36813
|
+
const org = req.query.org;
|
|
36814
|
+
const site = req.query.site;
|
|
36815
|
+
const sortObj = {};
|
|
36816
|
+
const sortFields = String(req.query.sort).split(",");
|
|
36817
|
+
const sortOrders = String(req.query.order).split(",");
|
|
36818
|
+
sortFields.forEach((field, index) => {
|
|
36819
|
+
if (allowedFields.includes(field)) {
|
|
36820
|
+
const order = sortOrders[index] === "asc" ? 1 : -1;
|
|
36821
|
+
sortObj[field] = order;
|
|
36822
|
+
}
|
|
36823
|
+
});
|
|
36824
|
+
try {
|
|
36825
|
+
const data = await _getAll({
|
|
36826
|
+
search,
|
|
36827
|
+
page,
|
|
36828
|
+
limit,
|
|
36829
|
+
sort: sortObj,
|
|
36830
|
+
status,
|
|
36831
|
+
dateFrom,
|
|
36832
|
+
dateTo,
|
|
36833
|
+
org,
|
|
36834
|
+
site
|
|
36835
|
+
});
|
|
36836
|
+
res.status(200).json(data);
|
|
36837
|
+
return;
|
|
36838
|
+
} catch (error2) {
|
|
36839
|
+
logger84.log({ level: "error", message: error2.message });
|
|
36840
|
+
next(error2);
|
|
36841
|
+
return;
|
|
36842
|
+
}
|
|
36843
|
+
}
|
|
36844
|
+
async function updateVisitorGuestById(req, res, next) {
|
|
36845
|
+
const _id = req.params.id;
|
|
36846
|
+
const payload = { _id, ...req.body };
|
|
36847
|
+
const { error } = schemaUpdateGuestManagement.validate(payload, {
|
|
36848
|
+
abortEarly: false
|
|
36849
|
+
});
|
|
36850
|
+
if (error) {
|
|
36851
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
36852
|
+
logger84.log({ level: "error", message: messages });
|
|
36853
|
+
next(new BadRequestError104(messages));
|
|
36854
|
+
return;
|
|
36855
|
+
}
|
|
36856
|
+
try {
|
|
36857
|
+
const result = await _updateVisitorGuestById(_id, req.body);
|
|
36858
|
+
res.status(200).json({ message: result });
|
|
36859
|
+
return;
|
|
36860
|
+
} catch (error2) {
|
|
36861
|
+
logger84.log({ level: "error", message: error2.message });
|
|
36862
|
+
next(error2);
|
|
36863
|
+
return;
|
|
36864
|
+
}
|
|
36865
|
+
}
|
|
36866
|
+
async function deleteVisitorGuest(req, res, next) {
|
|
36867
|
+
const validation = Joi59.string().hex().required();
|
|
36868
|
+
const _id = req.params.id;
|
|
36869
|
+
const { error } = validation.validate(_id);
|
|
36870
|
+
if (error) {
|
|
36871
|
+
logger84.log({ level: "error", message: error.message });
|
|
36872
|
+
next(new BadRequestError104(error.message));
|
|
36873
|
+
return;
|
|
36874
|
+
}
|
|
36875
|
+
try {
|
|
36876
|
+
await _deleteVisitorGuest(_id);
|
|
36877
|
+
res.status(200).json({ message: "Successfully deleted visitor guest." });
|
|
36878
|
+
return;
|
|
36879
|
+
} catch (error2) {
|
|
36880
|
+
logger84.log({ level: "error", message: error2.message });
|
|
36881
|
+
next(error2);
|
|
36882
|
+
return;
|
|
36883
|
+
}
|
|
36884
|
+
}
|
|
36885
|
+
return {
|
|
36886
|
+
add,
|
|
36887
|
+
getAll,
|
|
36888
|
+
updateVisitorGuestById,
|
|
36889
|
+
deleteVisitorGuest
|
|
36709
36890
|
};
|
|
36710
36891
|
}
|
|
36711
36892
|
|
|
36712
36893
|
// src/controllers/person.controller.ts
|
|
36894
|
+
import Joi60 from "joi";
|
|
36895
|
+
import { BadRequestError as BadRequestError105, logger as logger85 } from "@7365admin1/node-server-utils";
|
|
36713
36896
|
function usePersonController() {
|
|
36714
36897
|
const {
|
|
36715
36898
|
getAll: _getAll,
|
|
@@ -36754,17 +36937,21 @@ function usePersonController() {
|
|
|
36754
36937
|
const validation = Joi60.object({
|
|
36755
36938
|
page: Joi60.number().integer().min(1).allow("", null).default(1),
|
|
36756
36939
|
limit: Joi60.number().integer().min(1).max(100).allow("", null).default(10),
|
|
36757
|
-
sort: Joi60.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("",
|
|
36940
|
+
sort: Joi60.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", null),
|
|
36941
|
+
order: Joi60.string().pattern(/^(asc|desc)(,(asc|desc))*$/i).optional().allow("", null),
|
|
36758
36942
|
status: Joi60.string().optional().allow(null, ""),
|
|
36759
36943
|
search: Joi60.string().optional().allow(null, ""),
|
|
36760
36944
|
dateFrom: Joi60.date().iso().optional().allow(null, ""),
|
|
36761
36945
|
dateTo: Joi60.date().iso().optional().allow(null, ""),
|
|
36762
|
-
org: Joi60.string().optional().allow(null, ""),
|
|
36763
|
-
site: Joi60.string().optional().allow(null, ""),
|
|
36946
|
+
org: Joi60.string().hex().length(24).optional().allow(null, ""),
|
|
36947
|
+
site: Joi60.string().hex().length(24).optional().allow(null, ""),
|
|
36764
36948
|
type: Joi60.alternatives().try(
|
|
36765
36949
|
Joi60.string().valid(...PERSON_TYPES),
|
|
36766
36950
|
Joi60.array().items(Joi60.string().valid(...PERSON_TYPES))
|
|
36767
|
-
).optional().allow(null, "")
|
|
36951
|
+
).optional().allow(null, ""),
|
|
36952
|
+
nric: Joi60.string().optional().allow(null, ""),
|
|
36953
|
+
contact: Joi60.string().optional().allow(null, ""),
|
|
36954
|
+
plateNumber: Joi60.string().optional().allow(null, "")
|
|
36768
36955
|
});
|
|
36769
36956
|
const query = { ...req.query };
|
|
36770
36957
|
const { error } = validation.validate(query, {
|
|
@@ -36785,6 +36972,9 @@ function usePersonController() {
|
|
|
36785
36972
|
const org = req.query.org;
|
|
36786
36973
|
const site = req.query.site;
|
|
36787
36974
|
const type = req.query.type;
|
|
36975
|
+
const nric = req.query.nric;
|
|
36976
|
+
const contact = req.query.contact;
|
|
36977
|
+
const plateNumber = req.query.plateNumber;
|
|
36788
36978
|
const sortObj = {};
|
|
36789
36979
|
const sortFields = String(req.query.sort).split(",");
|
|
36790
36980
|
const sortOrders = String(req.query.order).split(",");
|
|
@@ -36805,7 +36995,10 @@ function usePersonController() {
|
|
|
36805
36995
|
dateTo,
|
|
36806
36996
|
org,
|
|
36807
36997
|
site,
|
|
36808
|
-
type
|
|
36998
|
+
type,
|
|
36999
|
+
nric,
|
|
37000
|
+
contact,
|
|
37001
|
+
plateNumber
|
|
36809
37002
|
});
|
|
36810
37003
|
res.status(200).json(data);
|
|
36811
37004
|
return;
|