@7365admin1/core 2.13.0 → 2.15.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/dist/index.mjs CHANGED
@@ -1388,7 +1388,9 @@ var schemaOccurrenceEntry = Joi5.object({
1388
1388
  subject: Joi5.string().hex().optional().allow(null, ""),
1389
1389
  occurrence: Joi5.string().optional().allow(null, ""),
1390
1390
  signature: Joi5.string().hex().optional().allow(null, ""),
1391
- incidentReportId: Joi5.string().hex().optional().allow(null, "")
1391
+ incidentReportId: Joi5.string().hex().optional().allow(null, ""),
1392
+ eSignature: Joi5.string().required(),
1393
+ createdByName: Joi5.string().optional().allow(null, "")
1392
1394
  });
1393
1395
  var schemaUpdateOccurrenceEntry = Joi5.object({
1394
1396
  _id: Joi5.string().hex().required(),
@@ -1397,7 +1399,9 @@ var schemaUpdateOccurrenceEntry = Joi5.object({
1397
1399
  subject: Joi5.string().hex().optional().allow(null, ""),
1398
1400
  occurrence: Joi5.string().optional().allow(null, ""),
1399
1401
  signature: Joi5.string().hex().optional().allow(null, ""),
1400
- incidentReportId: Joi5.string().hex().optional().allow(null, "")
1402
+ incidentReportId: Joi5.string().hex().optional().allow(null, ""),
1403
+ eSignature: Joi5.string().required(),
1404
+ createdByName: Joi5.string().optional().allow(null, "")
1401
1405
  });
1402
1406
  function MOccurrenceEntry(value) {
1403
1407
  if (value._id && typeof value._id === "string") {
@@ -1454,6 +1458,8 @@ function MOccurrenceEntry(value) {
1454
1458
  incidentReportId: value.incidentReportId,
1455
1459
  signature: value.signature,
1456
1460
  userName: value.userName,
1461
+ eSignature: value.eSignature,
1462
+ createdByName: value.createdByName,
1457
1463
  date: value.date,
1458
1464
  createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1459
1465
  updatedAt: value.updatedAt,
@@ -6424,12 +6430,12 @@ function useUserController() {
6424
6430
  }
6425
6431
  }
6426
6432
  async function createUserByVerification(req, res, next) {
6427
- const allowedTypes2 = ["user-sign-up", "user-invite"];
6433
+ const allowedTypes = ["user-sign-up", "user-invite"];
6428
6434
  const validation = Joi14.object({
6429
6435
  id: Joi14.string().hex().required(),
6430
6436
  name: Joi14.string().required(),
6431
6437
  password: Joi14.string().required(),
6432
- type: Joi14.string().required().valid(...allowedTypes2)
6438
+ type: Joi14.string().required().valid(...allowedTypes)
6433
6439
  });
6434
6440
  const id = req.params.id;
6435
6441
  const payload = { ...req.body };
@@ -15493,12 +15499,37 @@ import {
15493
15499
  import { BadRequestError as BadRequestError77, logger as logger61 } from "@7365admin1/node-server-utils";
15494
15500
  import Joi43 from "joi";
15495
15501
  import { ObjectId as ObjectId45 } from "mongodb";
15496
- var allowedTypes = ["whitelist", "blacklist"];
15497
- var allowedCategories = ["resident", "visitor"];
15502
+ var VehicleType = /* @__PURE__ */ ((VehicleType2) => {
15503
+ VehicleType2["WHITELIST"] = "whitelist";
15504
+ VehicleType2["BLOCKLIST"] = "blocklist";
15505
+ return VehicleType2;
15506
+ })(VehicleType || {});
15507
+ var VehicleCategory = /* @__PURE__ */ ((VehicleCategory2) => {
15508
+ VehicleCategory2["RESIDENT"] = "resident";
15509
+ VehicleCategory2["VISITOR"] = "visitor";
15510
+ return VehicleCategory2;
15511
+ })(VehicleCategory || {});
15512
+ var VehicleStatus = /* @__PURE__ */ ((VehicleStatus2) => {
15513
+ VehicleStatus2["PENDING"] = "pending";
15514
+ VehicleStatus2["ACTIVE"] = "active";
15515
+ VehicleStatus2["INACTIVE"] = "inactive";
15516
+ VehicleStatus2["DELETED"] = "deleted";
15517
+ return VehicleStatus2;
15518
+ })(VehicleStatus || {});
15519
+ var OrgNature = /* @__PURE__ */ ((OrgNature2) => {
15520
+ OrgNature2["PROPERTY_MANAGEMENT_AGENCY"] = "property_management_agency";
15521
+ OrgNature2["SECURITY_AGENCY"] = "security_agency";
15522
+ return OrgNature2;
15523
+ })(OrgNature || {});
15524
+ var ANPRMode = /* @__PURE__ */ ((ANPRMode2) => {
15525
+ ANPRMode2["TRAFFIC_REDLIST"] = "TrafficRedList";
15526
+ ANPRMode2["TRAFFIC_BLACKLIST"] = "TrafficBlackList";
15527
+ return ANPRMode2;
15528
+ })(ANPRMode || {});
15498
15529
  var vehicleSchema = Joi43.object({
15499
15530
  plateNumber: Joi43.string().required(),
15500
- type: Joi43.string().required().allow(...allowedTypes),
15501
- category: Joi43.string().required().allow(...allowedCategories),
15531
+ type: Joi43.string().required().valid(...Object.values(VehicleType)),
15532
+ category: Joi43.string().required().valid(...Object.values(VehicleCategory)),
15502
15533
  name: Joi43.string().required(),
15503
15534
  phoneNumber: Joi43.string().optional().allow("", null),
15504
15535
  recNo: Joi43.string().optional().allow("", null),
@@ -15512,7 +15543,8 @@ var vehicleSchema = Joi43.object({
15512
15543
  seasonPassType: Joi43.string().optional().allow("", null),
15513
15544
  start: Joi43.date().optional().allow("", null),
15514
15545
  end: Joi43.date().optional().allow("", null),
15515
- unitName: Joi43.string().optional().allow("", null)
15546
+ unitName: Joi43.string().optional().allow("", null),
15547
+ status: Joi43.string().optional().valid(...Object.values(VehicleStatus)).allow("")
15516
15548
  });
15517
15549
  function MVehicle(value) {
15518
15550
  const { error } = vehicleSchema.validate(value);
@@ -15541,6 +15573,11 @@ function MVehicle(value) {
15541
15573
  throw new BadRequestError77("Invalid building unit ID format.");
15542
15574
  }
15543
15575
  }
15576
+ const createdAtDate = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
15577
+ const expiredDate = new Date(createdAtDate);
15578
+ expiredDate.setFullYear(expiredDate.getFullYear() + 10);
15579
+ const createdAt = createdAtDate.toISOString();
15580
+ const expiredAt = value.end ?? expiredDate.toISOString();
15544
15581
  return {
15545
15582
  plateNumber: value.plateNumber ?? "",
15546
15583
  type: value.type ?? "",
@@ -15556,11 +15593,11 @@ function MVehicle(value) {
15556
15593
  nric: value.nric ?? "",
15557
15594
  remarks: value.remarks ?? "",
15558
15595
  seasonPassType: value.seasonPassType ?? "",
15559
- start: value.start ?? "",
15560
- end: value.end ?? "",
15561
- status: value.status ?? "active",
15596
+ start: value.start ?? createdAt,
15597
+ end: value.end ?? expiredAt,
15598
+ status: value.status ?? "active" /* ACTIVE */,
15562
15599
  unitName: value.unitName ?? "",
15563
- createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
15600
+ createdAt,
15564
15601
  updatedAt: value.updatedAt ?? "",
15565
15602
  deletedAt: value.deletedAt ?? ""
15566
15603
  };
@@ -15618,18 +15655,44 @@ function useVehicleRepo() {
15618
15655
  }
15619
15656
  const namespace_collection = "vehicles";
15620
15657
  const collection = db.collection(namespace_collection);
15621
- const { delNamespace, setCache, getCache, delCache } = useCache28(namespace_collection);
15658
+ const { delNamespace, setCache, getCache } = useCache28(namespace_collection);
15622
15659
  async function createIndex() {
15623
15660
  try {
15624
15661
  await collection.createIndexes([
15625
15662
  { key: { status: 1 } },
15626
15663
  { key: { type: 1 } },
15627
- { key: { category: 1 } }
15664
+ { key: { category: 1 } },
15665
+ {
15666
+ key: { nric: 1 },
15667
+ name: "uniq_nric_not_deleted_nonempty",
15668
+ unique: true,
15669
+ partialFilterExpression: {
15670
+ nric: { $type: "string", $gt: "" },
15671
+ deletedAt: ""
15672
+ }
15673
+ }
15628
15674
  ]);
15629
15675
  } catch (error) {
15676
+ console.error("createIndexes error:", error);
15630
15677
  throw new InternalServerError28("Failed to create index on vehicle.");
15631
15678
  }
15632
15679
  }
15680
+ async function createTextIndex() {
15681
+ try {
15682
+ await collection.createIndex({
15683
+ name: "text",
15684
+ plateNumber: "text",
15685
+ level: "text",
15686
+ unitName: "text",
15687
+ phoneNumber: "text",
15688
+ nric: "text"
15689
+ });
15690
+ } catch (error) {
15691
+ throw new InternalServerError28(
15692
+ "Failed to create text index on visitor transaction."
15693
+ );
15694
+ }
15695
+ }
15633
15696
  async function add(value, session) {
15634
15697
  try {
15635
15698
  value = MVehicle(value);
@@ -15661,37 +15724,40 @@ function useVehicleRepo() {
15661
15724
  search = "",
15662
15725
  sort = {},
15663
15726
  type = "",
15664
- category = ""
15727
+ category = "",
15728
+ status = ""
15665
15729
  }) {
15666
15730
  page = page > 0 ? page - 1 : 0;
15667
- const query = { status: "active" };
15731
+ const baseQuery = {
15732
+ ...status && { status },
15733
+ ...type && { type },
15734
+ ...type && { type }
15735
+ };
15736
+ let query = { ...baseQuery };
15737
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15668
15738
  const cacheOptions = {
15669
- status: "active",
15739
+ ...status && { status },
15740
+ ...type && { type },
15741
+ ...type && { type },
15670
15742
  page: String(page),
15671
- limit: String(limit)
15743
+ limit: String(limit),
15744
+ sort: JSON.stringify(sort)
15672
15745
  };
15673
- sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15674
- cacheOptions.sort = JSON.stringify(sort);
15675
15746
  if (search) {
15676
- query.$or = [{ name: { $regex: search, $options: "i" } }];
15747
+ query.$text = { $search: search };
15677
15748
  cacheOptions.search = search;
15678
15749
  }
15679
- if (type) {
15680
- query.type = type;
15681
- cacheOptions.type = type;
15682
- }
15683
- if (category) {
15684
- query.category = category;
15685
- cacheOptions.category = category;
15686
- }
15687
15750
  const cacheKey = makeCacheKey26(namespace_collection, cacheOptions);
15688
15751
  const cachedData = await getCache(cacheKey);
15689
15752
  if (cachedData) {
15690
15753
  logger62.info(`Cache hit for key: ${cacheKey}`);
15691
15754
  return cachedData;
15692
15755
  }
15756
+ const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15693
15757
  try {
15694
- const items = await collection.aggregate([
15758
+ let items = [];
15759
+ let length = 0;
15760
+ items = await collection.aggregate([
15695
15761
  { $match: query },
15696
15762
  { $sort: sort },
15697
15763
  { $skip: page * limit },
@@ -15717,17 +15783,65 @@ function useVehicleRepo() {
15717
15783
  level: 1,
15718
15784
  unit: "$units.name",
15719
15785
  plateNumber: 1,
15720
- recNo: 1
15786
+ recNo: 1,
15787
+ nric: 1
15721
15788
  }
15722
15789
  }
15723
15790
  ]).toArray();
15724
- const length = await collection.countDocuments(query);
15791
+ length = await collection.countDocuments(query);
15792
+ if ((!items || items.length === 0) && search) {
15793
+ const escaped = escapeRegex(search);
15794
+ const regexQuery = {
15795
+ ...baseQuery,
15796
+ ...type && { type },
15797
+ ...category && { category },
15798
+ $or: [
15799
+ { name: { $regex: escaped, $options: "i" } },
15800
+ { plateNumber: { $regex: escaped, $options: "i" } },
15801
+ { company: { $regex: escaped, $options: "i" } },
15802
+ { level: { $regex: escaped, $options: "i" } },
15803
+ { unitName: { $regex: escaped, $options: "i" } },
15804
+ { contact: { $regex: escaped, $options: "i" } },
15805
+ { nric: { $regex: escaped, $options: "i" } }
15806
+ ]
15807
+ };
15808
+ items = await collection.aggregate([
15809
+ { $match: regexQuery },
15810
+ { $sort: sort },
15811
+ { $skip: page * limit },
15812
+ { $limit: limit },
15813
+ {
15814
+ $lookup: {
15815
+ from: "building-units",
15816
+ localField: "unit",
15817
+ foreignField: "_id",
15818
+ as: "units",
15819
+ pipeline: [{ $project: { name: 1 } }]
15820
+ }
15821
+ },
15822
+ { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15823
+ {
15824
+ $project: {
15825
+ name: 1,
15826
+ type: 1,
15827
+ category: 1,
15828
+ status: 1,
15829
+ phoneNumber: 1,
15830
+ block: 1,
15831
+ level: 1,
15832
+ unit: "$units.name",
15833
+ plateNumber: 1,
15834
+ recNo: 1,
15835
+ nric: 1
15836
+ }
15837
+ }
15838
+ ]).toArray();
15839
+ length = await collection.countDocuments(regexQuery);
15840
+ }
15725
15841
  const data = paginate21(items, page, limit, length);
15726
- setCache(cacheKey, data, 15 * 60).then(() => {
15727
- logger62.info(`Cache set for key: ${cacheKey}`);
15728
- }).catch((err) => {
15729
- logger62.error(`Failed to set cache for key: ${cacheKey}`, err);
15730
- });
15842
+ setCache(cacheKey, data, 15 * 60).then(() => logger62.info(`Cache set for key: ${cacheKey}`)).catch(
15843
+ (err) => logger62.error(`Failed to set cache for key: ${cacheKey}`, err)
15844
+ );
15731
15845
  return data;
15732
15846
  } catch (error) {
15733
15847
  throw error;
@@ -15821,7 +15935,10 @@ function useVehicleRepo() {
15821
15935
  type: 1,
15822
15936
  category: 1,
15823
15937
  status: 1,
15824
- recNo: 1
15938
+ recNo: 1,
15939
+ start: 1,
15940
+ end: 1,
15941
+ seasonPassType: 1
15825
15942
  }
15826
15943
  }
15827
15944
  ]).toArray();
@@ -15870,7 +15987,7 @@ function useVehicleRepo() {
15870
15987
  throw error2;
15871
15988
  }
15872
15989
  }
15873
- async function updateVehicle(_id, value) {
15990
+ async function updateVehicle(_id, value, session) {
15874
15991
  try {
15875
15992
  _id = new ObjectId46(_id);
15876
15993
  } catch (error) {
@@ -15881,7 +15998,11 @@ function useVehicleRepo() {
15881
15998
  ...value,
15882
15999
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
15883
16000
  };
15884
- const res = await collection.updateOne({ _id }, { $set: updateValue });
16001
+ const res = await collection.updateOne(
16002
+ { _id },
16003
+ { $set: updateValue },
16004
+ { session }
16005
+ );
15885
16006
  if (res.modifiedCount === 0) {
15886
16007
  throw new InternalServerError28("Unable to update vehicle.");
15887
16008
  }
@@ -15960,8 +16081,126 @@ function useVehicleRepo() {
15960
16081
  );
15961
16082
  }
15962
16083
  }
16084
+ async function getVehiclesByNRIC({
16085
+ page = 1,
16086
+ limit = 10,
16087
+ search = "",
16088
+ sort = {}
16089
+ }) {
16090
+ page = page > 0 ? page - 1 : 0;
16091
+ const baseQuery = {
16092
+ deletedAt: ""
16093
+ };
16094
+ let query = { ...baseQuery };
16095
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
16096
+ const cacheOptions = {
16097
+ deletedAt: "",
16098
+ page: String(page),
16099
+ limit: String(limit),
16100
+ sort: JSON.stringify(sort)
16101
+ };
16102
+ if (search) {
16103
+ query.$text = { $search: search };
16104
+ cacheOptions.search = search;
16105
+ }
16106
+ const cacheKey = makeCacheKey26(namespace_collection, cacheOptions);
16107
+ const cachedData = await getCache(cacheKey);
16108
+ if (cachedData) {
16109
+ logger62.info(`Cache hit for key: ${cacheKey}`);
16110
+ return cachedData;
16111
+ }
16112
+ const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16113
+ try {
16114
+ let items = [];
16115
+ let length = 0;
16116
+ items = await collection.aggregate([
16117
+ { $match: query },
16118
+ { $sort: sort },
16119
+ { $skip: page * limit },
16120
+ { $limit: limit },
16121
+ {
16122
+ $project: {
16123
+ name: 1,
16124
+ phoneNumber: 1,
16125
+ plateNumber: 1,
16126
+ recNo: 1,
16127
+ nric: 1,
16128
+ status: 1,
16129
+ type: 1
16130
+ }
16131
+ }
16132
+ ]).toArray();
16133
+ length = await collection.countDocuments(query);
16134
+ if ((!items || items.length === 0) && search) {
16135
+ const escaped = escapeRegex(search);
16136
+ const regexQuery = {
16137
+ ...baseQuery,
16138
+ $or: [
16139
+ { name: { $regex: escaped, $options: "i" } },
16140
+ { plateNumber: { $regex: escaped, $options: "i" } },
16141
+ { level: { $regex: escaped, $options: "i" } },
16142
+ { nric: { $regex: escaped, $options: "i" } }
16143
+ ]
16144
+ };
16145
+ items = await collection.aggregate([
16146
+ { $match: regexQuery },
16147
+ { $sort: sort },
16148
+ { $skip: page * limit },
16149
+ { $limit: limit },
16150
+ {
16151
+ $project: {
16152
+ name: 1,
16153
+ phoneNumber: 1,
16154
+ plateNumber: 1,
16155
+ recNo: 1,
16156
+ nric: 1,
16157
+ status: 1,
16158
+ type: 1
16159
+ }
16160
+ }
16161
+ ]).toArray();
16162
+ length = await collection.countDocuments(regexQuery);
16163
+ }
16164
+ const data = paginate21(items, page, limit, length);
16165
+ setCache(cacheKey, data, 15 * 60).then(() => logger62.info(`Cache set for key: ${cacheKey}`)).catch(
16166
+ (err) => logger62.error(`Failed to set cache for key: ${cacheKey}`, err)
16167
+ );
16168
+ return data;
16169
+ } catch (error) {
16170
+ throw error;
16171
+ }
16172
+ }
16173
+ async function deleteExpiredVehicles(session) {
16174
+ try {
16175
+ const tenYearsAgo = /* @__PURE__ */ new Date();
16176
+ tenYearsAgo.setFullYear(tenYearsAgo.getFullYear() - 10);
16177
+ const res = await collection.updateMany(
16178
+ {
16179
+ status: { $ne: "deleted" },
16180
+ end: { $exists: true, $lte: tenYearsAgo }
16181
+ // check only end
16182
+ },
16183
+ { $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } },
16184
+ { session }
16185
+ );
16186
+ if (res.modifiedCount === 0)
16187
+ throw new InternalServerError28("Unable to delete vehicle.");
16188
+ delNamespace().then(() => {
16189
+ logger62.info(`Cache cleared for namespace: ${namespace_collection}`);
16190
+ }).catch((err) => {
16191
+ logger62.error(
16192
+ `Failed to clear cache for namespace: ${namespace_collection}`,
16193
+ err
16194
+ );
16195
+ });
16196
+ return res.modifiedCount;
16197
+ } catch (error) {
16198
+ throw error;
16199
+ }
16200
+ }
15963
16201
  return {
15964
16202
  createIndex,
16203
+ createTextIndex,
15965
16204
  add,
15966
16205
  getVehicles,
15967
16206
  getSeasonPassTypes,
@@ -15969,7 +16208,9 @@ function useVehicleRepo() {
15969
16208
  updateVehicle,
15970
16209
  deleteVehicle,
15971
16210
  getByPlaceNumber,
15972
- getVehicleByPlateNumber
16211
+ getVehicleByPlateNumber,
16212
+ getVehiclesByNRIC,
16213
+ deleteExpiredVehicles
15973
16214
  };
15974
16215
  }
15975
16216
 
@@ -16355,7 +16596,7 @@ function useDahuaService() {
16355
16596
  username: Joi45.string().required(),
16356
16597
  password: Joi45.string().required(),
16357
16598
  plateNumber: Joi45.string().required(),
16358
- mode: Joi45.string().valid("TrafficBlackList", "TrafficRedList").required(),
16599
+ mode: Joi45.string().valid(...Object.values(ANPRMode)).required(),
16359
16600
  start: Joi45.string().isoDate().optional().allow("", null),
16360
16601
  end: Joi45.string().isoDate().optional().allow("", null),
16361
16602
  owner: Joi45.string().optional().allow("", null)
@@ -16379,7 +16620,7 @@ function useDahuaService() {
16379
16620
  host: value.host,
16380
16621
  username: value.username,
16381
16622
  password: value.password,
16382
- endpoint: `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&BeginTime=${value.start}&CancelTime=${value.end}&+OpenGate=true&MasterOfCar=${value.owner}`
16623
+ endpoint: `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&BeginTime=${value.start}&CancelTime=${value.end}&OpenGate=true&MasterOfCar=${value.owner}`
16383
16624
  });
16384
16625
  return response;
16385
16626
  } catch (error2) {
@@ -16427,27 +16668,243 @@ import {
16427
16668
  logger as logger64,
16428
16669
  useAtlas as useAtlas39
16429
16670
  } from "@7365admin1/node-server-utils";
16671
+ function formatDahuaDate(date) {
16672
+ const pad = (n) => String(n).padStart(2, "0");
16673
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(
16674
+ date.getDate()
16675
+ )} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(
16676
+ date.getSeconds()
16677
+ )}`;
16678
+ }
16430
16679
  function useVehicleService() {
16431
- const { add: _add, deleteVehicle: _deleteVehicle } = useVehicleRepo();
16680
+ const {
16681
+ add: _add,
16682
+ deleteVehicle: _deleteVehicle,
16683
+ updateVehicle: _updateVehicle,
16684
+ getVehicleById: _getVehicleById,
16685
+ deleteExpiredVehicles: _deleteExpiredVehicles
16686
+ } = useVehicleRepo();
16432
16687
  const {
16433
16688
  addPlateNumber: _addPlateNumber,
16434
16689
  removePlateNumber: _removePlateNumber
16435
16690
  } = useDahuaService();
16436
16691
  const { getAllCameraWithPassword: _getAllSiteCameras } = useSiteCameraRepo();
16692
+ const { getById: _getById } = useOrgRepo();
16437
16693
  async function add(value) {
16694
+ const session = useAtlas39.getClient()?.startSession();
16695
+ if (!session)
16696
+ throw new Error("Unable to start session for vehicle service.");
16697
+ const org = await _getById(value.org);
16698
+ if (!org)
16699
+ throw new BadRequestError80("Org not found");
16700
+ if (!Object.values(OrgNature).includes(org.nature)) {
16701
+ throw new BadRequestError80(
16702
+ "This organization is not allowed to add vehicles."
16703
+ );
16704
+ }
16705
+ value.status = "active" /* ACTIVE */;
16706
+ const addedBySecurity = org.nature === "security_agency" /* SECURITY_AGENCY */;
16707
+ const addedByPM = org.nature === "property_management_agency" /* PROPERTY_MANAGEMENT_AGENCY */;
16708
+ if (addedBySecurity)
16709
+ value.status = "pending" /* PENDING */;
16710
+ const isBlocklist = value.type === "blocklist" /* BLOCKLIST */;
16711
+ if (isBlocklist)
16712
+ value.status = "inactive" /* INACTIVE */;
16713
+ const _type = value.type;
16714
+ const _mode = isBlocklist ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
16715
+ const hasStart = typeof value.start === "string" ? value.start.trim() !== "" : !!value.start;
16716
+ const hasEnd = typeof value.end === "string" ? value.end.trim() !== "" : !!value.end;
16717
+ let startDate = null;
16718
+ let endDate = null;
16719
+ let start = "";
16720
+ let end = "";
16721
+ let startDateDahua;
16722
+ let endDateDahua;
16723
+ if (addedByPM && !hasStart && !hasEnd) {
16724
+ startDate = /* @__PURE__ */ new Date();
16725
+ endDate = new Date(startDate);
16726
+ endDate.setFullYear(endDate.getFullYear() + 10);
16727
+ start = startDate.toISOString();
16728
+ end = endDate.toISOString();
16729
+ startDateDahua = formatDahuaDate(startDate);
16730
+ endDateDahua = formatDahuaDate(endDate);
16731
+ } else {
16732
+ if (hasStart) {
16733
+ startDate = new Date(value.start);
16734
+ start = startDate.toISOString();
16735
+ startDateDahua = formatDahuaDate(startDate);
16736
+ }
16737
+ if (hasEnd) {
16738
+ endDate = new Date(value.end);
16739
+ end = endDate.toISOString();
16740
+ endDateDahua = formatDahuaDate(endDate);
16741
+ }
16742
+ }
16743
+ const owner = String(value.name ?? "").substring(0, 15);
16744
+ try {
16745
+ session.startTransaction();
16746
+ let message = "Vehicle plate number needs approval from property management.";
16747
+ if (value.status && value.status !== "pending" /* PENDING */) {
16748
+ const siteCameras = [];
16749
+ let page = 1;
16750
+ let pages = 1;
16751
+ const limit = 20;
16752
+ do {
16753
+ const siteCameraReq = await _getAllSiteCameras({
16754
+ site: value.site,
16755
+ type: "anpr",
16756
+ direction: ["both", "entry"],
16757
+ page,
16758
+ limit
16759
+ });
16760
+ pages = siteCameraReq.pages || 1;
16761
+ siteCameras.push(...siteCameraReq.items);
16762
+ page++;
16763
+ } while (page < pages);
16764
+ if (!siteCameras.length)
16765
+ throw new BadRequestError80("No site cameras found.");
16766
+ for (const camera of siteCameras) {
16767
+ const { host, username, password } = camera;
16768
+ const dahuaPayload = {
16769
+ host,
16770
+ username,
16771
+ password,
16772
+ plateNumber: value.plateNumber,
16773
+ mode: _mode,
16774
+ owner,
16775
+ ...startDateDahua ? { start: startDateDahua } : "",
16776
+ ...endDateDahua ? { end: endDateDahua } : ""
16777
+ };
16778
+ const dahuaResponse = await _addPlateNumber(dahuaPayload);
16779
+ if (dahuaResponse?.statusCode !== 200) {
16780
+ throw new BadRequestError80(
16781
+ `Failed to add plate number to ANPR ${_type}`
16782
+ );
16783
+ }
16784
+ const responseData = dahuaResponse?.data.toString("utf-8");
16785
+ value.recNo = responseData.split("=")[1]?.trim();
16786
+ message = `Vehicle plate number added to ${_type} successfully.`;
16787
+ }
16788
+ }
16789
+ const formattedValue = {
16790
+ ...value,
16791
+ start,
16792
+ // ISO string or ""
16793
+ end
16794
+ // ISO string or ""
16795
+ };
16796
+ await _add(formattedValue, session);
16797
+ await session.commitTransaction();
16798
+ return message;
16799
+ } catch (error) {
16800
+ logger64.error("Error in vehicle service add:", error);
16801
+ await session.abortTransaction();
16802
+ throw error;
16803
+ } finally {
16804
+ session.endSession();
16805
+ }
16806
+ }
16807
+ async function deleteVehicle(_id, recno, site, type, bypass = false) {
16808
+ const session = useAtlas39.getClient()?.startSession();
16809
+ if (!session) {
16810
+ throw new Error("Unable to start session for vehicle service.");
16811
+ }
16812
+ const _mode = type !== "whitelist" /* WHITELIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
16813
+ try {
16814
+ session.startTransaction();
16815
+ const siteCameras = [];
16816
+ let page = 1;
16817
+ let pages = 1;
16818
+ const limit = 20;
16819
+ do {
16820
+ const siteCameraReq = await _getAllSiteCameras({
16821
+ site,
16822
+ type: "anpr",
16823
+ page,
16824
+ limit
16825
+ });
16826
+ pages = siteCameraReq.pages || 1;
16827
+ siteCameras.push(...siteCameraReq.items);
16828
+ page++;
16829
+ } while (page < pages);
16830
+ if (!siteCameras.length) {
16831
+ throw new BadRequestError80("No site cameras found.");
16832
+ }
16833
+ for (const camera of siteCameras) {
16834
+ const host = camera.host;
16835
+ const username = camera.username;
16836
+ const password = camera.password;
16837
+ const dahuaPayload = {
16838
+ host,
16839
+ username,
16840
+ password,
16841
+ recno,
16842
+ mode: _mode
16843
+ };
16844
+ const dahuaResponse = await _removePlateNumber(dahuaPayload);
16845
+ if (!bypass && dahuaResponse?.statusCode !== 200) {
16846
+ throw new BadRequestError80(
16847
+ `Failed to remove plate number to ANPR from ${type}`
16848
+ );
16849
+ }
16850
+ }
16851
+ await _deleteVehicle(_id, session);
16852
+ await session.commitTransaction();
16853
+ return `Vehicle plate number deleted from ${type} record successfully.`;
16854
+ } catch (error) {
16855
+ await session.abortTransaction();
16856
+ if (error instanceof AppError14)
16857
+ throw error;
16858
+ throw new InternalServerError29("Failed to delete vehicle");
16859
+ } finally {
16860
+ session.endSession();
16861
+ }
16862
+ }
16863
+ async function approveVehicleById(id, orgId, siteId) {
16438
16864
  const session = useAtlas39.getClient()?.startSession();
16439
16865
  if (!session) {
16440
16866
  throw new Error("Unable to start session for vehicle service.");
16441
16867
  }
16868
+ const org = await _getById(orgId);
16869
+ if (!org)
16870
+ throw new BadRequestError80("Org not found");
16871
+ const allowedNatures2 = ["property_management_agency"];
16872
+ if (!allowedNatures2.includes(org.nature)) {
16873
+ throw new BadRequestError80(
16874
+ "Only property management can approve vehicles."
16875
+ );
16876
+ }
16877
+ const vehicle = await _getVehicleById(id);
16878
+ if (!vehicle) {
16879
+ throw new BadRequestError80("Vehicle not found");
16880
+ }
16881
+ const owner = vehicle.name.substring(0, 15);
16882
+ const hasStart = typeof vehicle.start === "string" ? vehicle.start.trim() !== "" : !!vehicle.start;
16883
+ const hasEnd = typeof vehicle.end === "string" ? vehicle.end.trim() !== "" : !!vehicle.end;
16884
+ let startDate = null;
16885
+ let endDate = null;
16886
+ if (!hasStart && !hasEnd) {
16887
+ startDate = /* @__PURE__ */ new Date();
16888
+ endDate = new Date(startDate);
16889
+ endDate.setFullYear(endDate.getFullYear() + 10);
16890
+ } else {
16891
+ startDate = hasStart ? new Date(vehicle.start) : null;
16892
+ endDate = hasEnd ? new Date(vehicle.end) : null;
16893
+ }
16894
+ const startIso = startDate ? startDate.toISOString() : "";
16895
+ const endIso = endDate ? endDate.toISOString() : "";
16896
+ const startDahua = startDate ? formatDahuaDate(startDate) : "";
16897
+ const endDahua = endDate ? formatDahuaDate(endDate) : "";
16442
16898
  try {
16443
16899
  session.startTransaction();
16444
16900
  const siteCameras = [];
16445
16901
  let page = 1;
16446
16902
  let pages = 1;
16447
16903
  const limit = 20;
16904
+ let value = {};
16448
16905
  do {
16449
16906
  const siteCameraReq = await _getAllSiteCameras({
16450
- site: value.site,
16907
+ site: siteId,
16451
16908
  type: "anpr",
16452
16909
  direction: ["both", "entry"],
16453
16910
  page,
@@ -16466,11 +16923,11 @@ function useVehicleService() {
16466
16923
  host,
16467
16924
  username,
16468
16925
  password,
16469
- plateNumber: value.plateNumber,
16470
- mode: "TrafficRedList",
16471
- start: value.start ? new Date(value.start).toISOString() : "",
16472
- end: value.end ? new Date(value.end).toISOString() : "",
16473
- owner: value.name
16926
+ plateNumber: vehicle.plateNumber,
16927
+ mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
16928
+ start: startDahua,
16929
+ end: endDahua,
16930
+ owner
16474
16931
  };
16475
16932
  const dahuaResponse = await _addPlateNumber(dahuaPayload);
16476
16933
  if (dahuaResponse?.statusCode !== 200) {
@@ -16478,15 +16935,16 @@ function useVehicleService() {
16478
16935
  }
16479
16936
  const responseData = dahuaResponse?.data.toString("utf-8");
16480
16937
  value.recNo = responseData.split("=")[1]?.trim();
16481
- const formattedValue = {
16482
- ...value,
16483
- start: value.start ? new Date(value.start).toISOString() : "",
16484
- end: value.end ? new Date(value.end).toISOString() : ""
16485
- };
16486
- await _add(formattedValue, session);
16487
16938
  }
16939
+ value.status = "active";
16940
+ const formattedValue = {
16941
+ ...value,
16942
+ start: startIso,
16943
+ end: endIso
16944
+ };
16945
+ await _updateVehicle(id, formattedValue, session);
16488
16946
  await session.commitTransaction();
16489
- return "Vehicle plate number added successfully.";
16947
+ return "Vehicle plate number approved and added successfully.";
16490
16948
  } catch (error) {
16491
16949
  logger64.error("Error in vehicle service add:", error);
16492
16950
  await session.abortTransaction();
@@ -16495,50 +16953,16 @@ function useVehicleService() {
16495
16953
  session.endSession();
16496
16954
  }
16497
16955
  }
16498
- async function deleteVehicle(_id, recno, site, bypass = false) {
16956
+ async function processDeletingExpiredVehicles() {
16499
16957
  const session = useAtlas39.getClient()?.startSession();
16500
16958
  if (!session) {
16501
16959
  throw new Error("Unable to start session for vehicle service.");
16502
16960
  }
16503
16961
  try {
16504
16962
  session.startTransaction();
16505
- const siteCameras = [];
16506
- let page = 1;
16507
- let pages = 1;
16508
- const limit = 20;
16509
- do {
16510
- const siteCameraReq = await _getAllSiteCameras({
16511
- site,
16512
- type: "anpr",
16513
- page,
16514
- limit
16515
- });
16516
- pages = siteCameraReq.pages || 1;
16517
- siteCameras.push(...siteCameraReq.items);
16518
- page++;
16519
- } while (page < pages);
16520
- if (!siteCameras.length) {
16521
- throw new BadRequestError80("No site cameras found.");
16522
- }
16523
- for (const camera of siteCameras) {
16524
- const host = camera.host;
16525
- const username = camera.username;
16526
- const password = camera.password;
16527
- const dahuaPayload = {
16528
- host,
16529
- username,
16530
- password,
16531
- recno,
16532
- mode: "TrafficRedList"
16533
- };
16534
- const dahuaResponse = await _removePlateNumber(dahuaPayload);
16535
- if (!bypass && dahuaResponse?.statusCode !== 200) {
16536
- throw new BadRequestError80("Failed to remove plate number to ANPR");
16537
- }
16538
- }
16539
- await _deleteVehicle(_id, session);
16963
+ await _deleteExpiredVehicles();
16540
16964
  await session.commitTransaction();
16541
- return "Vehicle plate number deleted successfully.";
16965
+ return `Expired Vehicle plate numbers deleted successfully.`;
16542
16966
  } catch (error) {
16543
16967
  await session.abortTransaction();
16544
16968
  if (error instanceof AppError14)
@@ -16550,7 +16974,9 @@ function useVehicleService() {
16550
16974
  }
16551
16975
  return {
16552
16976
  add,
16553
- deleteVehicle
16977
+ deleteVehicle,
16978
+ approveVehicleById,
16979
+ processDeletingExpiredVehicles
16554
16980
  };
16555
16981
  }
16556
16982
 
@@ -16558,12 +16984,17 @@ function useVehicleService() {
16558
16984
  import { BadRequestError as BadRequestError81, logger as logger65 } from "@7365admin1/node-server-utils";
16559
16985
  import Joi46 from "joi";
16560
16986
  function useVehicleController() {
16561
- const { add: _add, deleteVehicle: _deleteVehicle } = useVehicleService();
16987
+ const {
16988
+ add: _add,
16989
+ deleteVehicle: _deleteVehicle,
16990
+ approveVehicleById: _approveVehicleById
16991
+ } = useVehicleService();
16562
16992
  const {
16563
16993
  getSeasonPassTypes: _getSeasonPassTypes,
16564
16994
  getVehicles: _getVehicles,
16565
16995
  getVehicleById: _getVehicleById,
16566
- updateVehicle: _updateVehicle
16996
+ updateVehicle: _updateVehicle,
16997
+ getVehiclesByNRIC: _getVehiclesByNRIC
16567
16998
  } = useVehicleRepo();
16568
16999
  async function add(req, res, next) {
16569
17000
  const payload = req.body;
@@ -16594,8 +17025,9 @@ function useVehicleController() {
16594
17025
  limit: Joi46.number().integer().min(1).max(100).allow("", null).default(10),
16595
17026
  sort: Joi46.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
16596
17027
  order: Joi46.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
16597
- type: Joi46.string().optional().allow("", ...allowedTypes),
16598
- category: Joi46.string().optional().allow("", ...allowedCategories)
17028
+ type: Joi46.string().optional().valid(...Object.values(VehicleType)),
17029
+ category: Joi46.string().optional().valid(...Object.values(VehicleCategory)),
17030
+ status: Joi46.string().optional().valid(...Object.values(VehicleStatus)).allow("")
16599
17031
  });
16600
17032
  const query = { ...req.query };
16601
17033
  const { error } = validation.validate(query);
@@ -16609,6 +17041,7 @@ function useVehicleController() {
16609
17041
  const limit = parseInt(req.query.limit ?? "10");
16610
17042
  const type = req.query.type ?? "";
16611
17043
  const category = req.query.category ?? "";
17044
+ const status = req.query.status ?? "";
16612
17045
  const sortObj = {};
16613
17046
  const sortFields = String(req.query.sort).split(",");
16614
17047
  const sortOrders = String(req.query.order).split(",");
@@ -16625,7 +17058,8 @@ function useVehicleController() {
16625
17058
  limit,
16626
17059
  sort: sortObj,
16627
17060
  type,
16628
- category
17061
+ category,
17062
+ status
16629
17063
  });
16630
17064
  res.json(data);
16631
17065
  return;
@@ -16681,7 +17115,8 @@ function useVehicleController() {
16681
17115
  block: Joi46.number().integer().optional().allow(0, null),
16682
17116
  level: Joi46.string().optional().allow("", null),
16683
17117
  unit: Joi46.string().optional().allow("", null),
16684
- plateNumber: Joi46.string().optional().allow("", null)
17118
+ plateNumber: Joi46.string().optional().allow("", null),
17119
+ nric: Joi46.string().optional().allow("", null)
16685
17120
  });
16686
17121
  const _id = req.params.id;
16687
17122
  const payload = { ...req.body };
@@ -16702,23 +17137,23 @@ function useVehicleController() {
16702
17137
  }
16703
17138
  }
16704
17139
  async function deleteVehicle(req, res, next) {
16705
- const site = req.params.site ?? "";
16706
- const _id = req.params.id ?? "";
16707
- const recno = req.params.recno ?? "";
16708
- const bypass = req.query.bypass === "true";
17140
+ const _id = req.params.id;
16709
17141
  const deleteVehicleSchema = Joi46.object({
16710
- _id: Joi46.string().hex().required(),
17142
+ _id: Joi46.string().hex().length(24).required(),
16711
17143
  recno: Joi46.string().required(),
16712
- bypass: Joi46.boolean().optional()
17144
+ site: Joi46.string().hex().length(24).required(),
17145
+ type: Joi46.string().valid("whitelist", "blocklist").required(),
17146
+ bypass: Joi46.boolean().optional().default(true)
16713
17147
  });
16714
- const { error } = deleteVehicleSchema.validate({ _id, recno, bypass });
17148
+ const { error, value } = deleteVehicleSchema.validate({ _id, ...req.body });
16715
17149
  if (error) {
16716
17150
  logger65.log({ level: "error", message: error.message });
16717
17151
  next(new BadRequestError81(error.message));
16718
17152
  return;
16719
17153
  }
17154
+ const { recno, site, type, bypass } = value;
16720
17155
  try {
16721
- const data = await _deleteVehicle(_id, recno, site, bypass);
17156
+ const data = await _deleteVehicle(_id, recno, site, type, bypass);
16722
17157
  res.json({
16723
17158
  message: "Vehicle deleted successfully.",
16724
17159
  data
@@ -16729,13 +17164,83 @@ function useVehicleController() {
16729
17164
  return;
16730
17165
  }
16731
17166
  }
17167
+ async function approveVehicleById(req, res, next) {
17168
+ const validation = Joi46.object({
17169
+ _id: Joi46.string().hex().length(24).required(),
17170
+ org: Joi46.string().hex().length(24).required(),
17171
+ site: Joi46.string().hex().length(24).required()
17172
+ });
17173
+ const _id = req.params.id;
17174
+ const payload = { ...req.body };
17175
+ const { error, value } = validation.validate({ _id, ...payload });
17176
+ if (error) {
17177
+ logger65.log({ level: "error", message: error.message });
17178
+ next(new BadRequestError81(error.message));
17179
+ return;
17180
+ }
17181
+ try {
17182
+ await _approveVehicleById(value._id, value.org, value.site);
17183
+ res.json({ message: "Successfully approved and updated vehicle." });
17184
+ return;
17185
+ } catch (error2) {
17186
+ logger65.log({ level: "error", message: error2.message });
17187
+ next(error2);
17188
+ return;
17189
+ }
17190
+ }
17191
+ async function getVehiclesByNRIC(req, res, next) {
17192
+ const allowedFields = ["start", "end"];
17193
+ const allowedOrder = ["asc", "desc"];
17194
+ const validation = Joi46.object({
17195
+ search: Joi46.string().optional().allow("", null),
17196
+ page: Joi46.number().integer().min(1).allow("", null).default(1),
17197
+ limit: Joi46.number().integer().min(1).max(100).allow("", null).default(10),
17198
+ sort: Joi46.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
17199
+ order: Joi46.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder)
17200
+ });
17201
+ const query = { ...req.query };
17202
+ const { error } = validation.validate(query);
17203
+ if (error) {
17204
+ logger65.log({ level: "error", message: error.message });
17205
+ next(new BadRequestError81(error.message));
17206
+ return;
17207
+ }
17208
+ const search = req.query.search ?? "";
17209
+ const page = parseInt(req.query.page ?? "1");
17210
+ const limit = parseInt(req.query.limit ?? "10");
17211
+ const sortObj = {};
17212
+ const sortFields = String(req.query.sort).split(",");
17213
+ const sortOrders = String(req.query.order).split(",");
17214
+ sortFields.forEach((field, index) => {
17215
+ if (allowedFields.includes(field)) {
17216
+ const order = sortOrders[index] === "asc" ? 1 : -1;
17217
+ sortObj[field] = order;
17218
+ }
17219
+ });
17220
+ try {
17221
+ const data = await _getVehiclesByNRIC({
17222
+ search,
17223
+ page,
17224
+ limit,
17225
+ sort: sortObj
17226
+ });
17227
+ res.json(data);
17228
+ return;
17229
+ } catch (error2) {
17230
+ logger65.log({ level: "error", message: error2.message });
17231
+ next(error2);
17232
+ return;
17233
+ }
17234
+ }
16732
17235
  return {
16733
17236
  add,
16734
17237
  getVehicles,
16735
17238
  getSeasonPassTypes,
16736
17239
  getVehicleById,
16737
17240
  updateVehicle,
16738
- deleteVehicle
17241
+ deleteVehicle,
17242
+ approveVehicleById,
17243
+ getVehiclesByNRIC
16739
17244
  };
16740
17245
  }
16741
17246
 
@@ -16837,7 +17342,7 @@ function useSiteCameraController() {
16837
17342
  }
16838
17343
  async function getAll(req, res, next) {
16839
17344
  const query = req.query;
16840
- const allowedTypes2 = ["ip", "exit", "entry", "both"];
17345
+ const allowedTypes = ["ip", "exit", "entry", "both"];
16841
17346
  const validation = Joi47.object({
16842
17347
  type: Joi47.string().optional(),
16843
17348
  site: Joi47.string().optional(),
@@ -20153,7 +20658,10 @@ function usePatrolQuestionRepo() {
20153
20658
  const query = {
20154
20659
  status,
20155
20660
  ...search && {
20156
- $or: [{ name: { $regex: search, $options: "i" } }]
20661
+ $or: [
20662
+ { question: { $regex: search, $options: "i" } },
20663
+ { answers: { $regex: search, $options: "i" } }
20664
+ ]
20157
20665
  },
20158
20666
  ...ObjectId58.isValid(site) && { site: new ObjectId58(site) }
20159
20667
  };
@@ -20581,9 +21089,6 @@ function usePatrolRouteRepo() {
20581
21089
  page = page > 0 ? page - 1 : 0;
20582
21090
  const query = {
20583
21091
  status,
20584
- ...search && {
20585
- $or: [{ name: { $regex: search, $options: "i" } }]
20586
- },
20587
21092
  ...ObjectId60.isValid(site) && { site: new ObjectId60(site) }
20588
21093
  };
20589
21094
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
@@ -20603,14 +21108,57 @@ function usePatrolRouteRepo() {
20603
21108
  }
20604
21109
  try {
20605
21110
  const basePipeline = [
20606
- { $match: query },
21111
+ {
21112
+ $lookup: {
21113
+ from: "members",
21114
+ localField: "assignee",
21115
+ foreignField: "_id",
21116
+ as: "assigneeData"
21117
+ }
21118
+ },
21119
+ {
21120
+ $match: {
21121
+ ...query,
21122
+ ...search && {
21123
+ $or: [
21124
+ { name: { $regex: search, $options: "i" } },
21125
+ { "assigneeData.name": { $regex: search, $options: "i" } },
21126
+ { start: { $regex: search, $options: "i" } },
21127
+ { end: { $regex: search, $options: "i" } }
21128
+ ]
21129
+ }
21130
+ }
21131
+ },
20607
21132
  { $sort: sort },
20608
21133
  { $skip: page * limit },
20609
21134
  { $limit: limit }
20610
21135
  ];
21136
+ const countPipeline = [
21137
+ {
21138
+ $lookup: {
21139
+ from: "members",
21140
+ localField: "assignee",
21141
+ foreignField: "_id",
21142
+ as: "assigneeData"
21143
+ }
21144
+ },
21145
+ {
21146
+ $match: {
21147
+ ...query,
21148
+ ...search && {
21149
+ $or: [
21150
+ { name: { $regex: search, $options: "i" } },
21151
+ { "assigneeData.name": { $regex: search, $options: "i" } },
21152
+ { start: { $regex: search, $options: "i" } },
21153
+ { end: { $regex: search, $options: "i" } }
21154
+ ]
21155
+ }
21156
+ }
21157
+ }
21158
+ ];
20611
21159
  const [items, countResult] = await Promise.all([
20612
21160
  collection.aggregate(basePipeline, { session }).toArray(),
20613
- collection.aggregate([{ $match: query }, { $count: "total" }], { session }).toArray()
21161
+ collection.aggregate([...countPipeline, { $count: "total" }], { session }).toArray()
20614
21162
  ]);
20615
21163
  const totalCount = countResult[0]?.total || 0;
20616
21164
  const data = paginate27(items, page, limit, totalCount);
@@ -20719,24 +21267,16 @@ function usePatrolRouteRepo() {
20719
21267
  {
20720
21268
  $multiply: [
20721
21269
  {
20722
- $hour: {
20723
- $dateAdd: {
20724
- startDate: { $toDate: "$start" },
20725
- unit: "hour",
20726
- amount: 8
20727
- }
21270
+ $toInt: {
21271
+ $arrayElemAt: [{ $split: ["$start", ":"] }, 0]
20728
21272
  }
20729
21273
  },
20730
21274
  60
20731
21275
  ]
20732
21276
  },
20733
21277
  {
20734
- $minute: {
20735
- $dateAdd: {
20736
- startDate: { $toDate: "$start" },
20737
- unit: "hour",
20738
- amount: 8
20739
- }
21278
+ $toInt: {
21279
+ $arrayElemAt: [{ $split: ["$start", ":"] }, 1]
20740
21280
  }
20741
21281
  }
20742
21282
  ]
@@ -20749,24 +21289,16 @@ function usePatrolRouteRepo() {
20749
21289
  {
20750
21290
  $multiply: [
20751
21291
  {
20752
- $hour: {
20753
- $dateAdd: {
20754
- startDate: { $toDate: "$end" },
20755
- unit: "hour",
20756
- amount: 8
20757
- }
21292
+ $toInt: {
21293
+ $arrayElemAt: [{ $split: ["$end", ":"] }, 0]
20758
21294
  }
20759
21295
  },
20760
21296
  60
20761
21297
  ]
20762
21298
  },
20763
21299
  {
20764
- $minute: {
20765
- $dateAdd: {
20766
- startDate: { $toDate: "$end" },
20767
- unit: "hour",
20768
- amount: 8
20769
- }
21300
+ $toInt: {
21301
+ $arrayElemAt: [{ $split: ["$end", ":"] }, 1]
20770
21302
  }
20771
21303
  }
20772
21304
  ]
@@ -21104,6 +21636,8 @@ var schemeLogCamera = Joi65.object({
21104
21636
  start: Joi65.string().optional().allow("", null),
21105
21637
  end: Joi65.string().optional().allow("", null),
21106
21638
  duration: Joi65.number().optional().allow("", null),
21639
+ status: Joi65.string().optional().allow("", null),
21640
+ description: Joi65.string().optional().allow("", null),
21107
21641
  questions: Joi65.array().items(
21108
21642
  Joi65.object({
21109
21643
  questionId: Joi65.string().hex().required(),
@@ -21124,7 +21658,7 @@ var schemaPatrolLog = Joi65.object({
21124
21658
  start: Joi65.string().required(),
21125
21659
  end: Joi65.string().required(),
21126
21660
  cameras: Joi65.array().items(schemeLogCamera).required(),
21127
- status: Joi65.array().items(Joi65.string().valid("complete", "late")).required(),
21661
+ status: Joi65.array().items(Joi65.string().valid("complete", "late", "incomplete")).required(),
21128
21662
  createdAt: Joi65.date().optional(),
21129
21663
  updatedAt: Joi65.date().optional(),
21130
21664
  deletedAt: Joi65.date().optional()
@@ -21137,7 +21671,7 @@ var schemaUpdatePatrolLog = Joi65.object({
21137
21671
  start: Joi65.string().optional().allow(null, ""),
21138
21672
  end: Joi65.string().optional().allow(null, ""),
21139
21673
  cameras: Joi65.array().items(schemeLogCamera).optional().allow(null, ""),
21140
- status: Joi65.array().items(Joi65.string().valid("complete", "late")).optional().allow(null, "")
21674
+ status: Joi65.array().items(Joi65.string().valid("complete", "late", "incomplete")).optional().allow(null, "")
21141
21675
  });
21142
21676
  function MPatrolLog(value) {
21143
21677
  const { error } = schemaPatrolLog.validate(value);
@@ -21275,9 +21809,6 @@ function usePatrolLogRepo() {
21275
21809
  }, session) {
21276
21810
  page = page > 0 ? page - 1 : 0;
21277
21811
  const query = {
21278
- ...search && {
21279
- $or: [{ name: { $regex: search, $options: "i" } }]
21280
- },
21281
21812
  ...ObjectId62.isValid(site) && { site: new ObjectId62(site) }
21282
21813
  };
21283
21814
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
@@ -21297,14 +21828,113 @@ function usePatrolLogRepo() {
21297
21828
  }
21298
21829
  try {
21299
21830
  const basePipeline = [
21300
- { $match: query },
21831
+ {
21832
+ $lookup: {
21833
+ from: "members",
21834
+ localField: "assignee",
21835
+ foreignField: "_id",
21836
+ as: "assigneeData"
21837
+ }
21838
+ },
21839
+ {
21840
+ $match: {
21841
+ ...query,
21842
+ ...search && {
21843
+ $or: [
21844
+ { name: { $regex: search, $options: "i" } },
21845
+ { "assigneeData.name": { $regex: search, $options: "i" } },
21846
+ {
21847
+ $expr: {
21848
+ $regexMatch: {
21849
+ input: {
21850
+ $dateToString: {
21851
+ date: { $toDate: "$start" },
21852
+ format: "%H:%M",
21853
+ timezone: "Asia/Singapore"
21854
+ }
21855
+ },
21856
+ regex: search,
21857
+ options: "i"
21858
+ }
21859
+ }
21860
+ },
21861
+ {
21862
+ $expr: {
21863
+ $regexMatch: {
21864
+ input: {
21865
+ $dateToString: {
21866
+ date: { $toDate: "$start" },
21867
+ format: "%H:%M",
21868
+ timezone: "Asia/Singapore"
21869
+ }
21870
+ },
21871
+ regex: search,
21872
+ options: "i"
21873
+ }
21874
+ }
21875
+ }
21876
+ ]
21877
+ }
21878
+ }
21879
+ },
21301
21880
  { $sort: sort },
21302
21881
  { $skip: page * limit },
21303
21882
  { $limit: limit }
21304
21883
  ];
21884
+ const countPipeline = [
21885
+ {
21886
+ $lookup: {
21887
+ from: "members",
21888
+ localField: "assignee",
21889
+ foreignField: "_id",
21890
+ as: "assigneeData"
21891
+ }
21892
+ },
21893
+ {
21894
+ $match: {
21895
+ ...query,
21896
+ ...search && {
21897
+ $or: [
21898
+ { name: { $regex: search, $options: "i" } },
21899
+ { "assigneeData.name": { $regex: search, $options: "i" } },
21900
+ {
21901
+ $expr: {
21902
+ $regexMatch: {
21903
+ input: {
21904
+ $dateToString: {
21905
+ date: { $toDate: "$start" },
21906
+ format: "%H:%M",
21907
+ timezone: "Asia/Singapore"
21908
+ }
21909
+ },
21910
+ regex: search,
21911
+ options: "i"
21912
+ }
21913
+ }
21914
+ },
21915
+ {
21916
+ $expr: {
21917
+ $regexMatch: {
21918
+ input: {
21919
+ $dateToString: {
21920
+ date: { $toDate: "$start" },
21921
+ format: "%H:%M",
21922
+ timezone: "Asia/Singapore"
21923
+ }
21924
+ },
21925
+ regex: search,
21926
+ options: "i"
21927
+ }
21928
+ }
21929
+ }
21930
+ ]
21931
+ }
21932
+ }
21933
+ }
21934
+ ];
21305
21935
  const [items, countResult] = await Promise.all([
21306
21936
  collection.aggregate(basePipeline, { session }).toArray(),
21307
- collection.aggregate([{ $match: query }, { $count: "total" }], { session }).toArray()
21937
+ collection.aggregate([...countPipeline, { $count: "total" }], { session }).toArray()
21308
21938
  ]);
21309
21939
  const totalCount = countResult[0]?.total || 0;
21310
21940
  const data = paginate28(items, page, limit, totalCount);
@@ -24197,20 +24827,19 @@ function useDocumentManagementController() {
24197
24827
  // src/models/bulletin-board.model.ts
24198
24828
  import Joi75 from "joi";
24199
24829
  import { ObjectId as ObjectId71 } from "mongodb";
24830
+ var BULLETIN_RECIPIENTS = [
24831
+ "resident",
24832
+ "security_agency",
24833
+ "cleaning_services",
24834
+ "mechanical_electrical",
24835
+ "property_management_agency"
24836
+ ];
24837
+ var STATUS_VALUES = ["active", "expired", "deleted"];
24200
24838
  var schemaBulletinBoard = Joi75.object({
24201
24839
  _id: Joi75.string().hex().optional().allow("", null),
24202
24840
  site: Joi75.string().hex().optional().allow("", null),
24203
24841
  orgId: Joi75.string().hex().optional().allow("", null),
24204
- recipients: Joi75.array().items(
24205
- Joi75.string().valid(
24206
- "admin",
24207
- "organization",
24208
- "site",
24209
- "service-provider",
24210
- "service-provider-member",
24211
- "resident"
24212
- )
24213
- ).optional(),
24842
+ recipients: Joi75.array().items(Joi75.string().valid(...BULLETIN_RECIPIENTS)).unique().optional(),
24214
24843
  title: Joi75.string().optional().allow("", null),
24215
24844
  content: Joi75.string().optional().allow("", null),
24216
24845
  file: Joi75.array().items(
@@ -24223,23 +24852,14 @@ var schemaBulletinBoard = Joi75.object({
24223
24852
  noExpiration: Joi75.boolean().optional(),
24224
24853
  startDate: Joi75.date().optional().allow("", null),
24225
24854
  endDate: Joi75.date().optional().allow("", null),
24226
- status: Joi75.string().optional().allow("", null),
24855
+ status: Joi75.string().valid(...STATUS_VALUES).optional(),
24227
24856
  createdAt: Joi75.date().optional().allow(null),
24228
24857
  updatedAt: Joi75.date().optional().allow(null),
24229
24858
  deletedAt: Joi75.date().optional().allow(null)
24230
24859
  });
24231
24860
  var schemaUpdateBulletinBoard = Joi75.object({
24232
24861
  _id: Joi75.string().hex().required(),
24233
- recipients: Joi75.array().items(
24234
- Joi75.string().valid(
24235
- "admin",
24236
- "organization",
24237
- "site",
24238
- "service-provider",
24239
- "service-provider-member",
24240
- "resident"
24241
- )
24242
- ).optional(),
24862
+ recipients: Joi75.array().items(Joi75.string().valid(...BULLETIN_RECIPIENTS)).unique().optional(),
24243
24863
  title: Joi75.string().optional().allow("", null),
24244
24864
  content: Joi75.string().optional().allow("", null),
24245
24865
  file: Joi75.array().items(
@@ -24252,7 +24872,7 @@ var schemaUpdateBulletinBoard = Joi75.object({
24252
24872
  noExpiration: Joi75.boolean().optional(),
24253
24873
  startDate: Joi75.date().optional().allow("", null),
24254
24874
  endDate: Joi75.date().optional().allow("", null),
24255
- status: Joi75.string().optional().allow("", null)
24875
+ status: Joi75.string().valid(...STATUS_VALUES).optional()
24256
24876
  });
24257
24877
  function MBulletinBoard(value) {
24258
24878
  const { error } = schemaBulletinBoard.validate(value);
@@ -24352,7 +24972,8 @@ function useBulletinBoardRepo() {
24352
24972
  limit = 10,
24353
24973
  sort = {},
24354
24974
  site = "",
24355
- status = "active"
24975
+ status = "active",
24976
+ recipients = []
24356
24977
  }, session) {
24357
24978
  page = page > 0 ? page - 1 : 0;
24358
24979
  try {
@@ -24362,7 +24983,9 @@ function useBulletinBoardRepo() {
24362
24983
  }
24363
24984
  const query = {
24364
24985
  site,
24365
- status
24986
+ status,
24987
+ ...search && { $text: { $search: search } },
24988
+ ...recipients?.length && { recipients: { $in: recipients } }
24366
24989
  };
24367
24990
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
24368
24991
  const cacheOptions = {
@@ -24370,12 +24993,10 @@ function useBulletinBoardRepo() {
24370
24993
  status,
24371
24994
  sort: JSON.stringify(sort),
24372
24995
  page,
24373
- limit
24996
+ limit,
24997
+ ...search && { search },
24998
+ ...recipients?.length && { recipients: recipients.sort().join(",") }
24374
24999
  };
24375
- if (search) {
24376
- query.$text = { $search: search };
24377
- cacheOptions.search = search;
24378
- }
24379
25000
  const cacheKey = makeCacheKey39(namespace_collection, cacheOptions);
24380
25001
  const cachedData = await getCache(cacheKey);
24381
25002
  if (cachedData) {
@@ -24467,7 +25088,7 @@ function useBulletinBoardRepo() {
24467
25088
  throw error;
24468
25089
  }
24469
25090
  }
24470
- async function deleteBulletinBoardById(_id) {
25091
+ async function deleteBulletinBoardById(_id, session) {
24471
25092
  try {
24472
25093
  _id = new ObjectId72(_id);
24473
25094
  } catch (error) {
@@ -24479,7 +25100,11 @@ function useBulletinBoardRepo() {
24479
25100
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
24480
25101
  deletedAt: (/* @__PURE__ */ new Date()).toISOString()
24481
25102
  };
24482
- const res = await collection.updateOne({ _id }, { $set: updateValue });
25103
+ const res = await collection.updateOne(
25104
+ { _id },
25105
+ { $set: updateValue },
25106
+ { session }
25107
+ );
24483
25108
  if (res.modifiedCount === 0) {
24484
25109
  throw new InternalServerError42("Unable to delete bulletin board.");
24485
25110
  }
@@ -24545,8 +25170,11 @@ function useBulletinBoardService() {
24545
25170
  const {
24546
25171
  add: _add,
24547
25172
  updateBulletinBoardById: _updateBulletinBoardById,
24548
- processExpiredBulletinBoards: _processExpiredBulletinBoards
25173
+ processExpiredBulletinBoards: _processExpiredBulletinBoards,
25174
+ deleteBulletinBoardById: _deleteBulletinBoardById,
25175
+ getBulletinBoardById: _getBulletinBoardById
24549
25176
  } = useBulletinBoardRepo();
25177
+ const { deleteFileById: _deleteFileById } = useFileRepo();
24550
25178
  async function add(value) {
24551
25179
  const session = useAtlas65.getClient()?.startSession();
24552
25180
  session?.startTransaction();
@@ -24589,10 +25217,31 @@ function useBulletinBoardService() {
24589
25217
  session?.endSession();
24590
25218
  }
24591
25219
  }
25220
+ async function deleteBulletinBoardById(id) {
25221
+ const session = useAtlas65.getClient()?.startSession();
25222
+ session?.startTransaction();
25223
+ try {
25224
+ const existingBulletinBoard = await _getBulletinBoardById(id);
25225
+ if (Array.isArray(existingBulletinBoard.file)) {
25226
+ for (const file of existingBulletinBoard.file) {
25227
+ await _deleteFileById(file, session);
25228
+ }
25229
+ }
25230
+ await _deleteBulletinBoardById(id, session);
25231
+ await session?.commitTransaction();
25232
+ return "Successfully deleted bulletin board.";
25233
+ } catch (error) {
25234
+ await session?.abortTransaction();
25235
+ throw error;
25236
+ } finally {
25237
+ session?.endSession();
25238
+ }
25239
+ }
24592
25240
  return {
24593
25241
  add,
24594
25242
  updateBulletinBoardById,
24595
- processExpiredBulletinBoards
25243
+ processExpiredBulletinBoards,
25244
+ deleteBulletinBoardById
24596
25245
  };
24597
25246
  }
24598
25247
 
@@ -24600,12 +25249,12 @@ function useBulletinBoardService() {
24600
25249
  import { BadRequestError as BadRequestError123, logger as logger104 } from "@7365admin1/node-server-utils";
24601
25250
  import Joi76 from "joi";
24602
25251
  function useBulletinBoardController() {
24603
- const { add: _add, updateBulletinBoardById: _updateBulletinBoardById } = useBulletinBoardService();
24604
25252
  const {
24605
- getAll: _getAll,
24606
- getBulletinBoardById: _getBulletinBoardById,
25253
+ add: _add,
25254
+ updateBulletinBoardById: _updateBulletinBoardById,
24607
25255
  deleteBulletinBoardById: _deleteBulletinBoardById
24608
- } = useBulletinBoardRepo();
25256
+ } = useBulletinBoardService();
25257
+ const { getAll: _getAll, getBulletinBoardById: _getBulletinBoardById } = useBulletinBoardRepo();
24609
25258
  async function add(req, res, next) {
24610
25259
  const payload = { ...req.body };
24611
25260
  const { error } = schemaBulletinBoard.validate(payload, {
@@ -24637,12 +25286,23 @@ function useBulletinBoardController() {
24637
25286
  sort: Joi76.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
24638
25287
  order: Joi76.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
24639
25288
  site: Joi76.string().hex().required(),
24640
- status: Joi76.string().optional().allow(null, "")
25289
+ status: Joi76.string().optional().allow(null, ""),
25290
+ recipients: Joi76.string().optional().allow("", null).custom((value, helpers) => {
25291
+ const parsed = value.split(",").map((v) => v.trim()).filter(Boolean);
25292
+ for (const r of parsed) {
25293
+ if (!BULLETIN_RECIPIENTS.includes(r)) {
25294
+ return helpers.error("any.only");
25295
+ }
25296
+ }
25297
+ return value;
25298
+ }).messages({
25299
+ "any.only": `Recipients must be one of: ${BULLETIN_RECIPIENTS.join(
25300
+ ", "
25301
+ )}`
25302
+ })
24641
25303
  });
24642
25304
  const query = { ...req.query };
24643
- const { error } = validation.validate(query, {
24644
- abortEarly: false
24645
- });
25305
+ const { error } = validation.validate(query, { abortEarly: false });
24646
25306
  if (error) {
24647
25307
  const messages = error.details.map((d) => d.message).join(", ");
24648
25308
  logger104.log({ level: "error", message: messages });
@@ -24654,9 +25314,15 @@ function useBulletinBoardController() {
24654
25314
  const limit = parseInt(req.query.limit ?? "10");
24655
25315
  const site = req.query.site ?? "";
24656
25316
  const status = req.query.status ?? "active";
25317
+ const recipientsRaw = req.query.recipients ?? "";
25318
+ const recipients = recipientsRaw && typeof recipientsRaw === "string" ? Array.from(
25319
+ new Set(
25320
+ recipientsRaw.split(",").map((r) => r.trim()).filter(Boolean)
25321
+ )
25322
+ ) : [];
24657
25323
  const sortObj = {};
24658
- const sortFields = String(req.query.sort).split(",");
24659
- const sortOrders = String(req.query.order).split(",");
25324
+ const sortFields = String(req.query.sort ?? "").split(",").filter(Boolean);
25325
+ const sortOrders = String(req.query.order ?? "").split(",").filter(Boolean);
24660
25326
  sortFields.forEach((field, index) => {
24661
25327
  if (allowedFields.includes(field)) {
24662
25328
  const order = sortOrders[index] === "asc" ? 1 : -1;
@@ -24670,7 +25336,8 @@ function useBulletinBoardController() {
24670
25336
  limit,
24671
25337
  sort: sortObj,
24672
25338
  site,
24673
- status
25339
+ status,
25340
+ recipients
24674
25341
  });
24675
25342
  res.status(200).json(data);
24676
25343
  return;
@@ -24753,6 +25420,16 @@ function useBulletinBoardController() {
24753
25420
  import { BadRequestError as BadRequestError124, logger as logger105 } from "@7365admin1/node-server-utils";
24754
25421
  import { ObjectId as ObjectId73 } from "mongodb";
24755
25422
  import Joi77 from "joi";
25423
+
25424
+ // src/types/enums/billing-frequency.enum.ts
25425
+ var EBillingFrequency = /* @__PURE__ */ ((EBillingFrequency2) => {
25426
+ EBillingFrequency2["MONTHLY"] = "monthly";
25427
+ EBillingFrequency2["QAURTERLY"] = "quarterly";
25428
+ EBillingFrequency2["ANNUALLY"] = "annually";
25429
+ return EBillingFrequency2;
25430
+ })(EBillingFrequency || {});
25431
+
25432
+ // src/models/site-billing-item.model.ts
24756
25433
  var schemaUnits = Joi77.object({
24757
25434
  _id: Joi77.string().hex().optional(),
24758
25435
  name: Joi77.string().optional()
@@ -24763,7 +25440,7 @@ var schemaBillingItem = Joi77.object({
24763
25440
  org: Joi77.string().hex().required(),
24764
25441
  name: Joi77.string().required(),
24765
25442
  amount: Joi77.number().required(),
24766
- frequency: Joi77.string().valid("month", "quarter", "annual").required(),
25443
+ frequency: Joi77.string().valid(...Object.values(EBillingFrequency)).required(),
24767
25444
  billingType: Joi77.string().valid("recurring", "non-recurring").required(),
24768
25445
  dueInDays: Joi77.number().optional().allow(null, ""),
24769
25446
  date: Joi77.string().required(),
@@ -24788,7 +25465,7 @@ var schemaUpdateSiteBillingItem = Joi77.object({
24788
25465
  org: Joi77.string().hex().optional().allow(null, ""),
24789
25466
  name: Joi77.string().optional().allow(null, ""),
24790
25467
  amount: Joi77.number().optional().allow(null, ""),
24791
- frequency: Joi77.string().valid("month", "quarter", "annual").optional().allow(null, ""),
25468
+ frequency: Joi77.string().valid(...Object.values(EBillingFrequency)).optional().allow(null, ""),
24792
25469
  billingType: Joi77.string().valid("recurring", "non-recurring").optional().allow(null, ""),
24793
25470
  dueInDays: Joi77.number().optional().allow(null, ""),
24794
25471
  date: Joi77.string().optional().allow(null, ""),
@@ -24942,7 +25619,16 @@ function useSiteBillingItemRepo() {
24942
25619
  ...search && {
24943
25620
  $or: [
24944
25621
  { name: { $regex: search, $options: "i" } },
24945
- { frequency: { $regex: search, $options: "i" } }
25622
+ { frequency: { $regex: search, $options: "i" } },
25623
+ {
25624
+ $expr: {
25625
+ $regexMatch: {
25626
+ input: { $toString: "$totalAmount" },
25627
+ regex: search,
25628
+ options: "i"
25629
+ }
25630
+ }
25631
+ }
24946
25632
  ]
24947
25633
  },
24948
25634
  ...ObjectId74.isValid(site) && { site: new ObjectId74(site) }
@@ -26009,14 +26695,16 @@ var schemaEventManagement = Joi81.object({
26009
26695
  title: Joi81.string().required(),
26010
26696
  description: Joi81.string().optional().allow(""),
26011
26697
  dateTime: Joi81.alternatives().try(Joi81.string(), Joi81.date()).required(),
26012
- status: Joi81.string().optional().default("planned")
26698
+ status: Joi81.string().optional().default("planned"),
26699
+ type: Joi81.string().optional().default("TASK")
26013
26700
  });
26014
26701
  var schemaUpdateEventManagement = Joi81.object({
26015
26702
  _id: Joi81.string().hex().required(),
26016
26703
  title: Joi81.string().optional().allow(null, ""),
26017
26704
  description: Joi81.string().optional().allow(null, ""),
26018
26705
  dateTime: Joi81.alternatives().try(Joi81.string(), Joi81.date()).optional().allow(null, ""),
26019
- status: Joi81.string().optional().allow(null, "")
26706
+ status: Joi81.string().optional().allow(null, ""),
26707
+ type: Joi81.string().optional().allow(null, "")
26020
26708
  });
26021
26709
  function MEventManagement(value) {
26022
26710
  if (value._id && typeof value._id === "string") {
@@ -26040,6 +26728,7 @@ function MEventManagement(value) {
26040
26728
  description: value.description ?? "",
26041
26729
  dateTime: value.dateTime,
26042
26730
  status: value.status ?? "planned",
26731
+ type: value.type ?? "TASK",
26043
26732
  createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
26044
26733
  updatedAt: value.updatedAt,
26045
26734
  deletedAt: value.deletedAt
@@ -26114,7 +26803,9 @@ function useEventManagementRepo() {
26114
26803
  limit = 10,
26115
26804
  sort = {},
26116
26805
  site = "",
26117
- status = ""
26806
+ status = "",
26807
+ type = "",
26808
+ date = ""
26118
26809
  }, session) {
26119
26810
  page = page > 0 ? page - 1 : 0;
26120
26811
  try {
@@ -26124,7 +26815,14 @@ function useEventManagementRepo() {
26124
26815
  }
26125
26816
  const baseQuery = {
26126
26817
  site,
26127
- status: status ? status : { $ne: "deleted" }
26818
+ status: status ? status : { $ne: "deleted" },
26819
+ ...type && { type },
26820
+ ...date && {
26821
+ dateTime: {
26822
+ $gte: `${date}T00:00:00.000Z`,
26823
+ $lt: `${date}T23:59:59.999Z`
26824
+ }
26825
+ }
26128
26826
  };
26129
26827
  let query = { ...baseQuery };
26130
26828
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
@@ -26133,7 +26831,8 @@ function useEventManagementRepo() {
26133
26831
  status: status ? status : { $ne: "deleted" },
26134
26832
  sort: JSON.stringify(sort),
26135
26833
  page,
26136
- limit
26834
+ limit,
26835
+ ...type && { type }
26137
26836
  };
26138
26837
  if (search) {
26139
26838
  query.$text = { $search: search };
@@ -26420,7 +27119,9 @@ function useEventManagementController() {
26420
27119
  sort: Joi82.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
26421
27120
  order: Joi82.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
26422
27121
  site: Joi82.string().hex().required(),
26423
- status: Joi82.string().optional()
27122
+ status: Joi82.string().optional(),
27123
+ type: Joi82.string().optional().valid("TASK", "EVENT").allow(null, ""),
27124
+ date: Joi82.string().optional().allow(null, "")
26424
27125
  });
26425
27126
  const query = { ...req.query };
26426
27127
  const { error } = validation.validate(query, {
@@ -26437,6 +27138,8 @@ function useEventManagementController() {
26437
27138
  const limit = parseInt(req.query.limit ?? "10");
26438
27139
  const site = req.query.site ?? "";
26439
27140
  const status = req.query.status ?? "";
27141
+ const type = req.query.type ?? "";
27142
+ const date = req.query.date ?? "";
26440
27143
  const sortObj = {};
26441
27144
  const sortFields = String(req.query.sort).split(",");
26442
27145
  const sortOrders = String(req.query.order).split(",");
@@ -26453,7 +27156,9 @@ function useEventManagementController() {
26453
27156
  limit,
26454
27157
  sort: sortObj,
26455
27158
  site,
26456
- status
27159
+ status,
27160
+ type,
27161
+ date
26457
27162
  });
26458
27163
  res.status(200).json(data);
26459
27164
  return;
@@ -26779,6 +27484,7 @@ function useSiteUnitBillingRepo() {
26779
27484
  }
26780
27485
  };
26781
27486
  }
27487
+ const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
26782
27488
  const query = {
26783
27489
  paymentStatus,
26784
27490
  status,
@@ -26786,7 +27492,16 @@ function useSiteUnitBillingRepo() {
26786
27492
  $or: [
26787
27493
  { unitOwner: { $regex: search, $options: "i" } },
26788
27494
  { billName: { $regex: search, $options: "i" } },
26789
- { unit: { $regex: search, $options: "i" } }
27495
+ { unit: { $regex: unitSearchRegex, $options: "i" } },
27496
+ {
27497
+ $expr: {
27498
+ $regexMatch: {
27499
+ input: { $toString: "$amountPaid" },
27500
+ regex: search,
27501
+ options: "i"
27502
+ }
27503
+ }
27504
+ }
26790
27505
  ]
26791
27506
  },
26792
27507
  ...ObjectId80.isValid(site) && { site: new ObjectId80(site) },
@@ -27213,14 +27928,14 @@ function useSiteUnitBillingService() {
27213
27928
  function isBillingChecker(billing_item, todayMonth, todayDate) {
27214
27929
  const billingMonth = Number(billing_item.month);
27215
27930
  const billingDay = Number(billing_item.date);
27216
- if (billing_item.frequency === "month") {
27931
+ if (billing_item.frequency === "monthly" /* MONTHLY */) {
27217
27932
  return todayDate === billingDay;
27218
27933
  }
27219
- if (billing_item.frequency === "quarter") {
27934
+ if (billing_item.frequency === "quarterly" /* QAURTERLY */) {
27220
27935
  const monthDiff = todayMonth - billingMonth;
27221
27936
  return monthDiff % 3 === 0 && todayDate === billingDay;
27222
27937
  }
27223
- if (billing_item.frequency === "annual") {
27938
+ if (billing_item.frequency === "annually" /* ANNUALLY */) {
27224
27939
  return todayMonth === billingMonth && todayDate === billingDay;
27225
27940
  }
27226
27941
  return false;
@@ -27412,11 +28127,12 @@ var EAccessCardTypes = /* @__PURE__ */ ((EAccessCardTypes2) => {
27412
28127
  EAccessCardTypes2["QR"] = "QRCODE";
27413
28128
  return EAccessCardTypes2;
27414
28129
  })(EAccessCardTypes || {});
27415
- var EAccessCardUserTypes = /* @__PURE__ */ ((EAccessCardUserTypes3) => {
27416
- EAccessCardUserTypes3["RESIDENT"] = "Resident/Tenant";
27417
- EAccessCardUserTypes3["CONTRACTOR"] = "Contractor";
27418
- EAccessCardUserTypes3["VISITOR"] = "Visitor";
27419
- return EAccessCardUserTypes3;
28130
+ var EAccessCardUserTypes = /* @__PURE__ */ ((EAccessCardUserTypes2) => {
28131
+ EAccessCardUserTypes2["RESIDENT"] = "Resident/Tenant";
28132
+ EAccessCardUserTypes2["CONTRACTOR"] = "Contractor";
28133
+ EAccessCardUserTypes2["VISITOR"] = "Visitor";
28134
+ EAccessCardUserTypes2["DEFAULT"] = "Visitor/Resident";
28135
+ return EAccessCardUserTypes2;
27420
28136
  })(EAccessCardUserTypes || {});
27421
28137
  var AccessTypeProps = /* @__PURE__ */ ((AccessTypeProps2) => {
27422
28138
  AccessTypeProps2["NORMAL"] = "Normal";
@@ -28015,7 +28731,7 @@ function UseAccessManagementRepo() {
28015
28731
  {
28016
28732
  $match: {
28017
28733
  ...defaultQuery,
28018
- status: { $ne: "deleted" }
28734
+ status: { $eq: "active" }
28019
28735
  }
28020
28736
  },
28021
28737
  // ✅ Only project needed fields before heavy lookups
@@ -28077,16 +28793,16 @@ function UseAccessManagementRepo() {
28077
28793
  preserveNullAndEmptyArrays: false
28078
28794
  }
28079
28795
  },
28080
- // Groups by unit _id and keeps only the first occurrence
28081
- {
28082
- $group: {
28083
- _id: "$level.units._id",
28084
- doc: { $first: "$$ROOT" }
28085
- }
28086
- },
28087
- {
28088
- $replaceRoot: { newRoot: "$doc" }
28089
- },
28796
+ // // Groups by unit _id and keeps only the first occurrence
28797
+ // {
28798
+ // $group: {
28799
+ // _id: "$level.units._id",
28800
+ // doc: { $first: "$$ROOT" },
28801
+ // },
28802
+ // },
28803
+ // {
28804
+ // $replaceRoot: { newRoot: "$doc" },
28805
+ // },
28090
28806
  // ✅ Apply search filter
28091
28807
  {
28092
28808
  $match: {
@@ -28098,7 +28814,6 @@ function UseAccessManagementRepo() {
28098
28814
  totalCount: [{ $count: "count" }],
28099
28815
  items: [
28100
28816
  // ✅ Sort BEFORE skip/limit for correct pagination
28101
- { $sort: { _id: -1 } },
28102
28817
  { $skip: page * limit },
28103
28818
  { $limit: limit },
28104
28819
  // ✅ Users lookup - optimized with index hint
@@ -28136,7 +28851,7 @@ function UseAccessManagementRepo() {
28136
28851
  userType
28137
28852
  }
28138
28853
  },
28139
- { $project: { _id: 1, userId: 1, type: 1, cardNo: 1, isActivated: 1 } }
28854
+ { $project: { _id: 1, userId: 1, type: 1, cardNo: 1, isActivated: 1, replacementStatus: 1 } }
28140
28855
  ],
28141
28856
  as: "accessCards"
28142
28857
  }
@@ -28155,7 +28870,21 @@ function UseAccessManagementRepo() {
28155
28870
  $filter: {
28156
28871
  input: "$accessCards",
28157
28872
  as: "card",
28158
- cond: { $ne: ["$$card.userId", null] }
28873
+ cond: { $and: [{ $ne: ["$$card.userId", null] }, { $eq: ["$$card.isActivated", true] }] }
28874
+ }
28875
+ },
28876
+ f_replaced: {
28877
+ $filter: {
28878
+ input: "$accessCards",
28879
+ as: "card",
28880
+ cond: { $and: [{ $eq: ["$$card.isActivated", false] }, { $ne: ["$$card.replacementStatus", null] }] }
28881
+ }
28882
+ },
28883
+ f_deleted: {
28884
+ $filter: {
28885
+ input: "$accessCards",
28886
+ as: "card",
28887
+ cond: { $and: [{ $eq: ["$$card.isActivated", false] }, { $eq: ["$$card.replacementStatus", null] }] }
28159
28888
  }
28160
28889
  }
28161
28890
  }
@@ -28187,6 +28916,14 @@ function UseAccessManagementRepo() {
28187
28916
  non_physical: { $size: { $filter: { input: { $ifNull: ["$f_Assigned", []] }, as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } } }
28188
28917
  }
28189
28918
  },
28919
+ replaced: {
28920
+ physical: { $filter: { input: "$f_replaced", as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } },
28921
+ non_physical: { $filter: { input: "$f_replaced", as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } }
28922
+ },
28923
+ deleted: {
28924
+ physical: { $filter: { input: "$f_deleted", as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } },
28925
+ non_physical: { $filter: { input: "$f_deleted", as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } }
28926
+ },
28190
28927
  totalCardCount: {
28191
28928
  $add: [
28192
28929
  { $size: { $filter: { input: { $ifNull: ["$f_Available", []] }, as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } } },
@@ -28196,7 +28933,8 @@ function UseAccessManagementRepo() {
28196
28933
  ]
28197
28934
  }
28198
28935
  }
28199
- }
28936
+ },
28937
+ { $sort: { totalCardCount: -1 } }
28200
28938
  ]
28201
28939
  }
28202
28940
  }
@@ -28642,16 +29380,26 @@ function UseAccessManagementRepo() {
28642
29380
  async function cardReplacementRepo(params) {
28643
29381
  const session = useAtlas74.getClient()?.startSession();
28644
29382
  try {
28645
- const { cardId, remarks } = params;
29383
+ const { cardId, remarks, issuedCardId, unitId, userId } = params;
28646
29384
  const id = new ObjectId83(cardId);
29385
+ const newCardId = new ObjectId83(issuedCardId);
29386
+ const unit = new ObjectId83(unitId);
29387
+ const user = new ObjectId83(userId);
28647
29388
  session?.startTransaction();
28648
- const card = await collection().findOneAndUpdate(
28649
- { _id: id },
28650
- { $set: { remarks, replacementStatus: "Pending", requestDate: /* @__PURE__ */ new Date(), isActivated: false } },
28651
- { returnDocument: "after", session }
28652
- );
29389
+ const sessionResult = await Promise.all([
29390
+ await collection().findOneAndUpdate(
29391
+ { _id: id },
29392
+ { $set: { remarks, replacementStatus: "Complete", requestDate: /* @__PURE__ */ new Date(), isActivated: false } },
29393
+ { returnDocument: "after", session }
29394
+ ),
29395
+ await collection().findOneAndUpdate(
29396
+ { _id: newCardId },
29397
+ { $set: { updatedAt: /* @__PURE__ */ new Date(), assignedUnit: unit, userId: user } },
29398
+ { returnDocument: "after", session }
29399
+ )
29400
+ ]);
28653
29401
  await session?.commitTransaction();
28654
- return card;
29402
+ return sessionResult;
28655
29403
  } catch (error) {
28656
29404
  await session?.abortTransaction();
28657
29405
  throw new Error(error.message);
@@ -28659,6 +29407,398 @@ function UseAccessManagementRepo() {
28659
29407
  await session?.endSession();
28660
29408
  }
28661
29409
  }
29410
+ async function visitorAccessCardsRepo(params) {
29411
+ try {
29412
+ const page = params.page ? params.page - 1 : 0;
29413
+ const siteId = new ObjectId83(params.site);
29414
+ const search = params.search;
29415
+ const type = params.type;
29416
+ const limit = Number(params.limit);
29417
+ const query = {
29418
+ site: siteId,
29419
+ assignedUnit: { $ne: null },
29420
+ type: "NFC" /* NFC */
29421
+ };
29422
+ if (search) {
29423
+ query.$or = [{ accessLevel: { $regex: search, $options: "i" } }, { cardNo: { $regex: search, $options: "i" } }];
29424
+ }
29425
+ if (type) {
29426
+ query.userType = type;
29427
+ } else {
29428
+ query.userType = { $ne: "Resident/Tenant" /* RESIDENT */ };
29429
+ }
29430
+ const res = await collection().aggregate([
29431
+ {
29432
+ $match: query
29433
+ },
29434
+ {
29435
+ $project: {
29436
+ cardNo: 1,
29437
+ accessLevel: 1,
29438
+ site: 1,
29439
+ userType: 1,
29440
+ qrTag: 1,
29441
+ qrTagCardNo: 1,
29442
+ qrData: 1
29443
+ }
29444
+ },
29445
+ {
29446
+ $addFields: {
29447
+ extractedCardNo: {
29448
+ $substr: ["$cardNo", { $subtract: [{ $strLenCP: "$cardNo" }, 5] }, 5]
29449
+ }
29450
+ }
29451
+ },
29452
+ {
29453
+ $facet: {
29454
+ totalCount: [{ $count: "count" }],
29455
+ items: [{ $sort: { _id: -1 } }, { $skip: page * limit }, { $limit: limit }]
29456
+ }
29457
+ }
29458
+ ]).toArray();
29459
+ const length = res[0].totalCount[0] ? res[0].totalCount[0].count : 0;
29460
+ const items = res[0].items;
29461
+ const paginatedResult = paginate38(items, page, limit, length);
29462
+ return paginatedResult;
29463
+ } catch (error) {
29464
+ throw new Error(error.message);
29465
+ }
29466
+ }
29467
+ async function getCardReplacementRepo(params) {
29468
+ try {
29469
+ const site = new ObjectId83(params.site);
29470
+ const search = params.search;
29471
+ const statusFilter = params.statusFilter;
29472
+ const dateFrom = params.dateFrom;
29473
+ const dateTo = params.dateTo;
29474
+ const page = Number(params.page) - 1;
29475
+ const limit = Number(params.limit);
29476
+ const query = {
29477
+ site,
29478
+ replacementStatus: { $in: ["Complete", "Issuance", "Pending"] },
29479
+ isActivated: false
29480
+ };
29481
+ const searchQuery = buildSearchQuery(search);
29482
+ if (statusFilter) {
29483
+ query.replacementStatus = statusFilter;
29484
+ }
29485
+ if (dateFrom && dateTo) {
29486
+ query.requestDate = { $gte: new Date(dateFrom), $lte: new Date(dateTo) };
29487
+ } else if (dateFrom) {
29488
+ query.requestDate = { $gte: new Date(dateFrom) };
29489
+ } else if (dateTo) {
29490
+ query.requestDate = { $lte: new Date(dateTo) };
29491
+ }
29492
+ const res = await collection().aggregate([
29493
+ {
29494
+ $match: { ...query }
29495
+ },
29496
+ {
29497
+ $lookup: {
29498
+ from: "building-units",
29499
+ let: { unit: "$assignedUnit" },
29500
+ pipeline: [
29501
+ {
29502
+ $match: {
29503
+ $expr: {
29504
+ $eq: ["$_id", "$$unit"]
29505
+ }
29506
+ }
29507
+ },
29508
+ {
29509
+ $project: {
29510
+ _id: 1,
29511
+ name: 1,
29512
+ level: 1
29513
+ }
29514
+ }
29515
+ ],
29516
+ as: "unit"
29517
+ }
29518
+ },
29519
+ {
29520
+ $unwind: { path: "$unit", preserveNullAndEmptyArrays: true }
29521
+ },
29522
+ {
29523
+ $lookup: {
29524
+ from: "building-levels",
29525
+ let: { level: "$unit.level" },
29526
+ pipeline: [
29527
+ {
29528
+ $match: {
29529
+ $expr: {
29530
+ $eq: ["$_id", "$$level"]
29531
+ }
29532
+ }
29533
+ },
29534
+ {
29535
+ $project: {
29536
+ _id: 1,
29537
+ level: 1,
29538
+ block: 1
29539
+ }
29540
+ }
29541
+ ],
29542
+ as: "level"
29543
+ }
29544
+ },
29545
+ {
29546
+ $unwind: { path: "$level", preserveNullAndEmptyArrays: true }
29547
+ },
29548
+ {
29549
+ $lookup: {
29550
+ from: "buildings",
29551
+ let: { block: "$level.block" },
29552
+ pipeline: [
29553
+ {
29554
+ $match: {
29555
+ $expr: {
29556
+ $eq: ["$_id", "$$block"]
29557
+ }
29558
+ }
29559
+ },
29560
+ {
29561
+ $project: {
29562
+ _id: 1,
29563
+ name: 1
29564
+ }
29565
+ }
29566
+ ],
29567
+ as: "building"
29568
+ }
29569
+ },
29570
+ {
29571
+ $unwind: { path: "$building", preserveNullAndEmptyArrays: true }
29572
+ },
29573
+ {
29574
+ $lookup: {
29575
+ from: "users",
29576
+ let: { id: "$userId" },
29577
+ pipeline: [
29578
+ {
29579
+ $match: {
29580
+ $expr: {
29581
+ $eq: ["$_id", "$$id"]
29582
+ }
29583
+ }
29584
+ },
29585
+ {
29586
+ $project: {
29587
+ givenName: 1,
29588
+ surname: 1
29589
+ }
29590
+ }
29591
+ ],
29592
+ as: "user"
29593
+ }
29594
+ },
29595
+ {
29596
+ $unwind: { path: "$user", preserveNullAndEmptyArrays: true }
29597
+ },
29598
+ {
29599
+ $match: { ...searchQuery }
29600
+ },
29601
+ {
29602
+ $facet: {
29603
+ data: [
29604
+ { $skip: page * limit },
29605
+ { $limit: limit },
29606
+ {
29607
+ $project: {
29608
+ _id: 1,
29609
+ cardNo: 1,
29610
+ accessLevel: 1,
29611
+ liftAccessLevel: 1,
29612
+ replacementStatus: 1,
29613
+ remarks: 1,
29614
+ block: "$building.name",
29615
+ level: "$level.level",
29616
+ unit: "$unit.name",
29617
+ user: "$user"
29618
+ }
29619
+ }
29620
+ ],
29621
+ totalCount: [{ $count: "count" }]
29622
+ }
29623
+ }
29624
+ ], { allowDiskUse: true }).toArray();
29625
+ const count = res[0]?.totalCount[0] ? res[0].totalCount[0].count : 0;
29626
+ const pagination = paginate38(res[0].data, page, limit, count);
29627
+ return pagination;
29628
+ } catch (error) {
29629
+ throw new Error(error.message);
29630
+ }
29631
+ }
29632
+ async function getAccessManagementSettingsRepo(params) {
29633
+ try {
29634
+ const { site } = params;
29635
+ const siteId = new ObjectId83(site);
29636
+ const res = await collectionName("entrypass-settings").findOne({ site: siteId }, { allowDiskUse: true });
29637
+ return res;
29638
+ } catch (error) {
29639
+ throw new Error(error.message);
29640
+ }
29641
+ }
29642
+ async function bulkPhysicalAccessCardRepo(params) {
29643
+ const session = useAtlas74.getClient()?.startSession();
29644
+ try {
29645
+ const { dataJson, site } = params;
29646
+ const rawItems = JSON.parse(dataJson).filter((_, index) => index !== -1);
29647
+ const items = await Promise.all(
29648
+ rawItems.map(async (item) => {
29649
+ const date = new Date(item["startDate (format MM/DD/YYYY)"]);
29650
+ const endDate = new Date(date.setFullYear(date.getFullYear() + 10));
29651
+ const cardNumber = String(Number(item["cardNo (number 0-65535 ex. 301)"] || 0)).padStart(6, "0");
29652
+ const facilityCode = String(Number(item["facilityCode (number 0-255 ex. 11)"] || 0)).padStart(4, "0");
29653
+ const pin = item["pin (number 6 digits only)"] ? item["pin (number 6 digits only)"].toString().padStart(6, "0") : "123456";
29654
+ const match = item["accessLevel (number ex. 1)"];
29655
+ const accessLevel = match ? match : null;
29656
+ const userType = item["userType(Contractor, Visitor, Resident/Tenant, Visitor/Resident)"];
29657
+ const rawAccessGroup = item["accessGroup (value ex. Full Access, No Access)"];
29658
+ const accessGroup = typeof rawAccessGroup === "string" ? rawAccessGroup.split(",").map((v) => v.trim()).filter((v) => v !== "") : [];
29659
+ return new MAccessCard({
29660
+ site: new ObjectId83(site),
29661
+ type: "NFC" /* NFC */,
29662
+ staffNo: null,
29663
+ accessLevel,
29664
+ accessGroup,
29665
+ accessType: "Normal" /* NORMAL */,
29666
+ cardNo: `${facilityCode}${cardNumber}`,
29667
+ pin,
29668
+ qrData: await createQrData({ cardNumber: `${facilityCode}${cardNumber}` }),
29669
+ startDate: new Date(item["startDate (format MM/DD/YYYY)"]),
29670
+ endDate: new Date(item["endDate (format MM/DD/YYYY)"] || endDate),
29671
+ isActivated: true,
29672
+ isAntiPassBack: false,
29673
+ isLiftCard: item["isLiftCard (TRUE OR FALSE)"] === "TRUE" ? true : false,
29674
+ liftAccessLevel: item["liftAccessLevel(number ex. 1)"] || null,
29675
+ createdAt: /* @__PURE__ */ new Date(),
29676
+ updatedAt: /* @__PURE__ */ new Date(),
29677
+ assignedUnit: null,
29678
+ userType: userType.toLowerCase() === "contractor" ? "Contractor" /* CONTRACTOR */ : userType.toLowerCase() === "visitor" ? "Visitor" /* VISITOR */ : userType.toLowerCase() === "resident/tenant" ? "Resident/Tenant" /* RESIDENT */ : "Visitor/Resident" /* DEFAULT */,
29679
+ doorName: item["Door Name (ex. Main Door)"] || null,
29680
+ liftName: item["Lift Name (ex. Main Lift)"] || null
29681
+ });
29682
+ })
29683
+ );
29684
+ const result = await collection().insertMany(items, { session, ordered: false });
29685
+ const mapping = items.map((item, i) => ({
29686
+ cardNo: item.cardNo,
29687
+ insertedId: result.insertedIds[i]
29688
+ }));
29689
+ return mapping;
29690
+ } catch (error) {
29691
+ throw new Error(error.message);
29692
+ }
29693
+ }
29694
+ async function assignAccessCardToUnitRepo(params) {
29695
+ const session = useAtlas74.getClient()?.startSession();
29696
+ try {
29697
+ session?.startTransaction();
29698
+ const { units, quantity, type, site, userType, accessLevel, liftAccessLevel } = params;
29699
+ const [convertedUnits, convertedSite] = await Promise.all([Promise.all(units.map((id) => new ObjectId83(id))), new ObjectId83(site)]);
29700
+ const totalRequired = quantity * convertedUnits.length;
29701
+ const availableCards = await collection().find({
29702
+ assignedUnit: null,
29703
+ userId: null,
29704
+ type,
29705
+ userType,
29706
+ isActivated: true,
29707
+ site: convertedSite,
29708
+ accessLevel: accessLevel ?? null,
29709
+ liftAccessLevel: liftAccessLevel ?? null
29710
+ }).limit(totalRequired).toArray();
29711
+ if (availableCards.length < totalRequired) {
29712
+ session?.abortTransaction();
29713
+ return {
29714
+ status: "error",
29715
+ message: `Insufficient ${type} access cards. Need ${totalRequired}, but only ${availableCards.length} available.`
29716
+ };
29717
+ }
29718
+ const bulkUpdates = availableCards.map((card, index) => ({
29719
+ updateOne: {
29720
+ filter: { _id: card._id },
29721
+ update: {
29722
+ $set: {
29723
+ assignedUnit: convertedUnits[Math.floor(index / quantity)]
29724
+ }
29725
+ }
29726
+ }
29727
+ }));
29728
+ await collection().bulkWrite(bulkUpdates, { session });
29729
+ session?.commitTransaction();
29730
+ return {
29731
+ status: "success",
29732
+ message: "Cards assigned to units successfully.",
29733
+ assigned: bulkUpdates.length
29734
+ };
29735
+ } catch (error) {
29736
+ session?.abortTransaction();
29737
+ return Promise.reject(error.message);
29738
+ } finally {
29739
+ session?.endSession();
29740
+ }
29741
+ }
29742
+ async function deleteCardRepo(params) {
29743
+ try {
29744
+ const { cardId, remarks } = params;
29745
+ const id = new ObjectId83(cardId);
29746
+ const result = await collection().findOneAndUpdate({ _id: id }, { $set: { isActivated: false, updatedAt: /* @__PURE__ */ new Date(), remarks, requestDate: /* @__PURE__ */ new Date() } }, { returnDocument: "after" });
29747
+ return result;
29748
+ } catch (error) {
29749
+ throw new Error(error.message);
29750
+ }
29751
+ }
29752
+ async function getCardDetailsRepo(params) {
29753
+ try {
29754
+ const { siteId, cardId } = params;
29755
+ const convertedSiteId = new ObjectId83(siteId);
29756
+ const convertedCardId = new ObjectId83(cardId);
29757
+ const card = await collection().findOne(
29758
+ {
29759
+ _id: convertedCardId,
29760
+ site: convertedSiteId,
29761
+ type: "NFC" /* NFC */,
29762
+ userType: "Visitor/Resident" /* DEFAULT */
29763
+ },
29764
+ {
29765
+ projection: {
29766
+ userId: 1,
29767
+ site: 1,
29768
+ type: 1,
29769
+ userType: 1,
29770
+ cardNo: 1,
29771
+ isActivated: 1,
29772
+ replacementStatus: 1,
29773
+ vmsRemarks: 1,
29774
+ remarks: 1,
29775
+ requestDate: 1,
29776
+ createdAt: 1,
29777
+ updatedAt: 1
29778
+ }
29779
+ }
29780
+ );
29781
+ if (!card)
29782
+ return null;
29783
+ const site = await collectionName("sites").findOne(
29784
+ { _id: card.site },
29785
+ { projection: { name: 1, status: 1 } }
29786
+ );
29787
+ const user = card.userId ? await collectionName("users").findOne(
29788
+ { _id: card.userId },
29789
+ { projection: { name: 1, email: 1 } }
29790
+ ) : null;
29791
+ const status = card.userId === null && card.isActivated === true ? "available" : card.userId !== null && card.isActivated === true ? "assigned" : card.isActivated === false && card.replacementStatus !== null ? "replaced" : "deleted";
29792
+ return {
29793
+ ...card,
29794
+ status,
29795
+ site,
29796
+ user
29797
+ };
29798
+ } catch (error) {
29799
+ throw new Error(error.message);
29800
+ }
29801
+ }
28662
29802
  return {
28663
29803
  createIndexes,
28664
29804
  createIndexForEntrypass,
@@ -28674,7 +29814,14 @@ function UseAccessManagementRepo() {
28674
29814
  replaceCardRepo,
28675
29815
  updateNFCStatusRepo,
28676
29816
  doorAndLiftDropdownRepo,
28677
- cardReplacementRepo
29817
+ cardReplacementRepo,
29818
+ visitorAccessCardsRepo,
29819
+ getCardReplacementRepo,
29820
+ getAccessManagementSettingsRepo,
29821
+ bulkPhysicalAccessCardRepo,
29822
+ assignAccessCardToUnitRepo,
29823
+ deleteCardRepo,
29824
+ getCardDetailsRepo
28678
29825
  };
28679
29826
  }
28680
29827
 
@@ -28684,6 +29831,8 @@ import Joi85 from "joi";
28684
29831
  // src/services/access-management.service.ts
28685
29832
  import { parseStringPromise as parseStringPromise2 } from "xml2js";
28686
29833
  import { useCache as useCache47 } from "@7365admin1/node-server-utils";
29834
+ import { Readable } from "stream";
29835
+ import * as xlsx from "xlsx";
28687
29836
  var namespace = "cache:acm";
28688
29837
  function useAccessManagementSvc() {
28689
29838
  const {
@@ -28699,7 +29848,14 @@ function useAccessManagementSvc() {
28699
29848
  replaceCardRepo,
28700
29849
  updateNFCStatusRepo,
28701
29850
  doorAndLiftDropdownRepo,
28702
- cardReplacementRepo
29851
+ cardReplacementRepo,
29852
+ visitorAccessCardsRepo,
29853
+ getCardReplacementRepo,
29854
+ getAccessManagementSettingsRepo,
29855
+ bulkPhysicalAccessCardRepo,
29856
+ assignAccessCardToUnitRepo,
29857
+ deleteCardRepo,
29858
+ getCardDetailsRepo
28703
29859
  } = UseAccessManagementRepo();
28704
29860
  const addPhysicalCardSvc = async (payload) => {
28705
29861
  try {
@@ -28884,12 +30040,93 @@ function useAccessManagementSvc() {
28884
30040
  throw new Error(err.message);
28885
30041
  }
28886
30042
  };
30043
+ const visitorAccessCardsSvc = async (params) => {
30044
+ try {
30045
+ const response = await visitorAccessCardsRepo({ ...params });
30046
+ return response;
30047
+ } catch (err) {
30048
+ throw new Error(err.message);
30049
+ }
30050
+ };
30051
+ const getCardReplacementSvc = async (params) => {
30052
+ try {
30053
+ const response = await getCardReplacementRepo({ ...params });
30054
+ return response;
30055
+ } catch (err) {
30056
+ throw new Error(err.message);
30057
+ }
30058
+ };
30059
+ const getAccessManagementSettingsSvc = async (params) => {
30060
+ try {
30061
+ const response = await getAccessManagementSettingsRepo({ ...params });
30062
+ return response;
30063
+ } catch (err) {
30064
+ throw new Error(err.message);
30065
+ }
30066
+ };
30067
+ const convertBufferFile = async (bufferFile) => {
30068
+ return new Promise((resolve, reject) => {
30069
+ const fileStream = Readable.from(bufferFile);
30070
+ let fileBuffer = Buffer.alloc(0);
30071
+ fileStream.on("data", (chunk) => {
30072
+ fileBuffer = Buffer.concat([fileBuffer, chunk]);
30073
+ });
30074
+ fileStream.on("end", () => {
30075
+ try {
30076
+ const workbook = xlsx.read(fileBuffer, { type: "buffer" });
30077
+ const sheetName = workbook.SheetNames[0];
30078
+ const sheet = workbook.Sheets[sheetName];
30079
+ const jsonData = xlsx.utils.sheet_to_json(sheet, { raw: false });
30080
+ resolve(jsonData);
30081
+ } catch (error) {
30082
+ reject("Error parsing file");
30083
+ }
30084
+ });
30085
+ fileStream.on("error", (err) => {
30086
+ reject("Error Reading File: " + err.message);
30087
+ });
30088
+ });
30089
+ };
30090
+ const bulkPhysicalAccessCardSvc = async (params) => {
30091
+ try {
30092
+ const response = await bulkPhysicalAccessCardRepo({ ...params });
30093
+ return response;
30094
+ } catch (err) {
30095
+ throw new Error(err.message);
30096
+ }
30097
+ };
30098
+ const assignAccessCardToUnitSvc = async (params) => {
30099
+ try {
30100
+ const response = await assignAccessCardToUnitRepo({ ...params });
30101
+ return response;
30102
+ } catch (err) {
30103
+ throw new Error(err.message);
30104
+ }
30105
+ };
30106
+ const deleteCardSvc = async (params) => {
30107
+ try {
30108
+ const response = await deleteCardRepo({ ...params });
30109
+ return response;
30110
+ } catch (err) {
30111
+ throw new Error(err.message);
30112
+ }
30113
+ };
30114
+ const getCardDetailsSvc = async (params) => {
30115
+ try {
30116
+ const response = await getCardDetailsRepo({ ...params });
30117
+ return response;
30118
+ } catch (err) {
30119
+ throw new Error(err.message);
30120
+ }
30121
+ };
28887
30122
  return {
28888
30123
  addPhysicalCardSvc,
28889
30124
  addNonPhysicalCardSvc,
28890
30125
  doorAccessLevelsSvc,
28891
30126
  liftAccessLevelsSvc,
28892
30127
  accessGroupsSvc,
30128
+ setCache,
30129
+ getCache,
28893
30130
  accessManagementSettingsSvc,
28894
30131
  allAccessCardsCountsSvc,
28895
30132
  availableAccessCardsSvc,
@@ -28900,11 +30137,21 @@ function useAccessManagementSvc() {
28900
30137
  replaceCardSvc,
28901
30138
  updateNFCStatusSvc,
28902
30139
  doorAndLiftDropdownSvc,
28903
- cardReplacementSvc
30140
+ cardReplacementSvc,
30141
+ visitorAccessCardsSvc,
30142
+ getCardReplacementSvc,
30143
+ getAccessManagementSettingsSvc,
30144
+ convertBufferFile,
30145
+ bulkPhysicalAccessCardSvc,
30146
+ assignAccessCardToUnitSvc,
30147
+ deleteCardSvc,
30148
+ getCardDetailsSvc
28904
30149
  };
28905
30150
  }
28906
30151
 
28907
30152
  // src/controllers/access-management.controller.ts
30153
+ import { useCache as useCache48 } from "@7365admin1/node-server-utils";
30154
+ var namespace2 = "cache:acm";
28908
30155
  function useAccessManagementController() {
28909
30156
  const {
28910
30157
  addPhysicalCardSvc,
@@ -28912,6 +30159,8 @@ function useAccessManagementController() {
28912
30159
  doorAccessLevelsSvc,
28913
30160
  liftAccessLevelsSvc,
28914
30161
  accessGroupsSvc,
30162
+ setCache,
30163
+ getCache,
28915
30164
  accessManagementSettingsSvc,
28916
30165
  allAccessCardsCountsSvc,
28917
30166
  availableAccessCardsSvc,
@@ -28922,7 +30171,15 @@ function useAccessManagementController() {
28922
30171
  replaceCardSvc,
28923
30172
  updateNFCStatusSvc,
28924
30173
  doorAndLiftDropdownSvc,
28925
- cardReplacementSvc
30174
+ cardReplacementSvc,
30175
+ visitorAccessCardsSvc,
30176
+ getCardReplacementSvc,
30177
+ getAccessManagementSettingsSvc,
30178
+ convertBufferFile,
30179
+ bulkPhysicalAccessCardSvc,
30180
+ assignAccessCardToUnitSvc,
30181
+ deleteCardSvc,
30182
+ getCardDetailsSvc
28926
30183
  } = useAccessManagementSvc();
28927
30184
  const addPhysicalCard = async (req, res) => {
28928
30185
  try {
@@ -29113,6 +30370,7 @@ function useAccessManagementController() {
29113
30370
  const allAccessCardsCounts = async (req, res) => {
29114
30371
  try {
29115
30372
  const { site, userType } = req.query;
30373
+ const user = req.cookies?.sid;
29116
30374
  const schema2 = Joi85.object({
29117
30375
  site: Joi85.string().hex().required(),
29118
30376
  userType: Joi85.string().required()
@@ -29121,7 +30379,19 @@ function useAccessManagementController() {
29121
30379
  if (error) {
29122
30380
  throw new Error(`${error.message}`);
29123
30381
  }
30382
+ const key = `${namespace2}:${user}:access-card-counts`;
30383
+ const listKey = `${namespace2}:${user}:list`;
30384
+ const { redis } = useCache48(key);
30385
+ const cachedData = await getCache({ key, redis });
30386
+ if (cachedData) {
30387
+ console.log("\u26A1 Cache hit:", key);
30388
+ redis.expire(key, 60).catch(console.error);
30389
+ redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
30390
+ return res.status(200).json({ message: "Success", data: cachedData });
30391
+ }
29124
30392
  const result = await allAccessCardsCountsSvc({ site, userType });
30393
+ await setCache({ key, data: result, ttlSeconds: 60, redis });
30394
+ redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
29125
30395
  return res.status(200).json({ message: "Success", data: result });
29126
30396
  } catch (error) {
29127
30397
  return res.status(400).json({
@@ -29138,11 +30408,24 @@ function useAccessManagementController() {
29138
30408
  userType: Joi85.string().optional().allow("", null),
29139
30409
  type: Joi85.string().optional().allow("", null)
29140
30410
  });
30411
+ const user = req.cookies?.sid;
29141
30412
  const { error } = schema2.validate({ site, userType, type });
29142
30413
  if (error) {
29143
30414
  return res.status(400).json({ message: error.message });
29144
30415
  }
30416
+ const key = `${namespace2}:${user}:available-access-cards`;
30417
+ const listKey = `${namespace2}:${user}:list`;
30418
+ const { redis } = useCache48(key);
30419
+ const cachedData = await getCache({ key, redis });
30420
+ if (cachedData) {
30421
+ console.log("\u26A1 Cache hit:", key);
30422
+ redis.expire(key, 60).catch(console.error);
30423
+ redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
30424
+ return res.status(200).json({ message: "Success", data: cachedData });
30425
+ }
29145
30426
  const result = await availableAccessCardsSvc({ site, userType, type });
30427
+ await setCache({ key, data: result, ttlSeconds: 60, redis });
30428
+ redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
29146
30429
  return res.status(200).json({ message: "Success", data: result });
29147
30430
  } catch (error) {
29148
30431
  return res.status(400).json({
@@ -29324,16 +30607,181 @@ function useAccessManagementController() {
29324
30607
  };
29325
30608
  const cardReplacement = async (req, res) => {
29326
30609
  try {
29327
- const { cardId, remarks } = req.body;
30610
+ const { cardId, remarks, unitId, issuedCardId, userId } = req.body;
29328
30611
  const schema2 = Joi85.object({
29329
30612
  cardId: Joi85.string().required(),
30613
+ remarks: Joi85.string().optional().allow("", null),
30614
+ unitId: Joi85.string().hex().required(),
30615
+ issuedCardId: Joi85.string().required(),
30616
+ userId: Joi85.string().hex().required()
30617
+ });
30618
+ const { error } = schema2.validate({ cardId, remarks, unitId, issuedCardId, userId });
30619
+ if (error) {
30620
+ return res.status(400).json({ message: error.message });
30621
+ }
30622
+ const result = await cardReplacementSvc({ cardId, remarks, unitId, issuedCardId, userId });
30623
+ return res.status(200).json({ message: "Success", data: result });
30624
+ } catch (error) {
30625
+ return res.status(500).json({
30626
+ data: null,
30627
+ message: error.message
30628
+ });
30629
+ }
30630
+ };
30631
+ const visitorAccessCards = async (req, res) => {
30632
+ try {
30633
+ const {
30634
+ page = 1,
30635
+ limit = 10,
30636
+ search,
30637
+ type,
30638
+ site
30639
+ } = req.query;
30640
+ const schema2 = Joi85.object({
30641
+ site: Joi85.string().hex().required(),
30642
+ page: Joi85.number().required(),
30643
+ limit: Joi85.number().optional().default(10),
30644
+ type: Joi85.string().required().valid(...Object.values(EAccessCardUserTypes)),
30645
+ search: Joi85.string().optional().allow("", null)
30646
+ });
30647
+ const { error } = schema2.validate({ site, page, limit, type, search });
30648
+ if (error) {
30649
+ return res.status(400).json({ message: error.message });
30650
+ }
30651
+ const result = await visitorAccessCardsSvc({ site, page, limit, type, search });
30652
+ return res.status(200).json({ message: "Success", data: result });
30653
+ } catch (error) {
30654
+ return res.status(500).json({
30655
+ data: null,
30656
+ message: error.message
30657
+ });
30658
+ }
30659
+ };
30660
+ const getCardReplacement = async (req, res) => {
30661
+ try {
30662
+ const site = req.params.site;
30663
+ const {
30664
+ search,
30665
+ statusFilter,
30666
+ dateFrom,
30667
+ dateTo,
30668
+ page = 0,
30669
+ limit = 10
30670
+ } = req.query;
30671
+ const schema2 = Joi85.object({
30672
+ search: Joi85.string().optional().allow("", null),
30673
+ statusFilter: Joi85.string().optional().allow("", null),
30674
+ dateFrom: Joi85.date().optional().allow("", null),
30675
+ dateTo: Joi85.date().optional().allow("", null),
30676
+ page: Joi85.number().optional().default(0),
30677
+ limit: Joi85.number().optional().default(10),
30678
+ site: Joi85.string().hex().required()
30679
+ });
30680
+ const { error } = schema2.validate({ search, statusFilter, dateFrom, dateTo, page, limit, site });
30681
+ if (error) {
30682
+ return res.status(400).json({ message: error.message });
30683
+ }
30684
+ const result = await getCardReplacementSvc({ site, page, limit, search, statusFilter, dateFrom, dateTo });
30685
+ return res.status(200).json({ message: "Success", data: result });
30686
+ } catch (error) {
30687
+ return res.status(500).json({
30688
+ data: null,
30689
+ message: error.message
30690
+ });
30691
+ }
30692
+ };
30693
+ const getAccessManagementSettings = async (req, res) => {
30694
+ try {
30695
+ const { site } = req.params;
30696
+ const schema2 = Joi85.object({
30697
+ site: Joi85.string().hex().required()
30698
+ });
30699
+ const { error } = schema2.validate({ site });
30700
+ if (error) {
30701
+ return res.status(400).json({ message: error.message });
30702
+ }
30703
+ const result = await getAccessManagementSettingsSvc({ site });
30704
+ return res.status(200).json({ message: "Success", data: result });
30705
+ } catch (error) {
30706
+ return res.status(500).json({
30707
+ data: null,
30708
+ message: error.message
30709
+ });
30710
+ }
30711
+ };
30712
+ const bulkPhysicalAccessCard = async (req, res) => {
30713
+ try {
30714
+ const { site } = req.query;
30715
+ if (!req.file)
30716
+ throw new Error("File is required!");
30717
+ const xlsData = await convertBufferFile(req.file.buffer);
30718
+ const dataJson = JSON.stringify(xlsData);
30719
+ const result = await bulkPhysicalAccessCardSvc({ dataJson, site });
30720
+ return res.status(200).json({ message: "Success", data: result });
30721
+ } catch (error) {
30722
+ return res.status(500).json({
30723
+ data: null,
30724
+ message: error.message
30725
+ });
30726
+ }
30727
+ };
30728
+ const assignAccessCardToUnit = async (req, res) => {
30729
+ try {
30730
+ const { units, type, quantity, site, userType, accessLevel, liftAccessLevel } = req.body;
30731
+ const schema2 = Joi85.object({
30732
+ units: Joi85.array().items(Joi85.string().hex()).required(),
30733
+ quantity: Joi85.number().required(),
30734
+ type: Joi85.string().required(),
30735
+ site: Joi85.string().hex().required(),
30736
+ userType: Joi85.string().required(),
30737
+ accessLevel: Joi85.string().optional().allow("", null),
30738
+ liftAccessLevel: Joi85.string().optional().allow("", null)
30739
+ });
30740
+ const { error } = schema2.validate({ units, quantity, type, site, userType, accessLevel, liftAccessLevel });
30741
+ if (error) {
30742
+ return res.status(400).json({ message: error.message });
30743
+ }
30744
+ const result = await assignAccessCardToUnitSvc({ units, quantity, type, site, userType, accessLevel, liftAccessLevel });
30745
+ return res.status(200).json({ message: "Success", data: result });
30746
+ } catch (error) {
30747
+ return res.status(500).json({
30748
+ data: null,
30749
+ message: error.message
30750
+ });
30751
+ }
30752
+ };
30753
+ const deleteCard = async (req, res) => {
30754
+ try {
30755
+ const { cardId, remarks } = req.body;
30756
+ const schema2 = Joi85.object({
30757
+ cardId: Joi85.string().hex().required(),
29330
30758
  remarks: Joi85.string().optional().allow("", null)
29331
30759
  });
29332
30760
  const { error } = schema2.validate({ cardId, remarks });
29333
30761
  if (error) {
29334
30762
  return res.status(400).json({ message: error.message });
29335
30763
  }
29336
- const result = await cardReplacementSvc({ cardId, remarks });
30764
+ const result = await deleteCardSvc({ cardId, remarks });
30765
+ return res.status(200).json({ message: "Success", data: result });
30766
+ } catch (error) {
30767
+ return res.status(500).json({
30768
+ data: null,
30769
+ message: error.message
30770
+ });
30771
+ }
30772
+ };
30773
+ const getCardDetails = async (req, res) => {
30774
+ try {
30775
+ const { siteId, cardId } = req.query;
30776
+ const schema2 = Joi85.object({
30777
+ siteId: Joi85.string().hex().required(),
30778
+ cardId: Joi85.string().hex().required()
30779
+ });
30780
+ const { error } = schema2.validate({ siteId, cardId });
30781
+ if (error) {
30782
+ return res.status(400).json({ message: error.message });
30783
+ }
30784
+ const result = await getCardDetailsSvc({ siteId, cardId });
29337
30785
  return res.status(200).json({ message: "Success", data: result });
29338
30786
  } catch (error) {
29339
30787
  return res.status(500).json({
@@ -29358,7 +30806,14 @@ function useAccessManagementController() {
29358
30806
  replaceCard,
29359
30807
  updateNFCStatus,
29360
30808
  doorAndLiftDropdown,
29361
- cardReplacement
30809
+ cardReplacement,
30810
+ visitorAccessCards,
30811
+ getCardReplacement,
30812
+ getAccessManagementSettings,
30813
+ bulkPhysicalAccessCard,
30814
+ assignAccessCardToUnit,
30815
+ deleteCard,
30816
+ getCardDetails
29362
30817
  };
29363
30818
  }
29364
30819
 
@@ -29428,7 +30883,7 @@ import {
29428
30883
  makeCacheKey as makeCacheKey45,
29429
30884
  paginate as paginate39,
29430
30885
  useAtlas as useAtlas75,
29431
- useCache as useCache48
30886
+ useCache as useCache49
29432
30887
  } from "@7365admin1/node-server-utils";
29433
30888
  import { ObjectId as ObjectId85 } from "mongodb";
29434
30889
  function useNfcPatrolTagRepo() {
@@ -29439,8 +30894,8 @@ function useNfcPatrolTagRepo() {
29439
30894
  const namespace_collection = "nfc-patrol-tags";
29440
30895
  const namespace_collection_nfc_patrol_routes = "nfc-patrol-routes";
29441
30896
  const collection = db.collection(namespace_collection);
29442
- const { delNamespace, getCache, setCache } = useCache48(namespace_collection);
29443
- const { delNamespace: delNamespaceNfcPatrolRoutes } = useCache48(
30897
+ const { delNamespace, getCache, setCache } = useCache49(namespace_collection);
30898
+ const { delNamespace: delNamespaceNfcPatrolRoutes } = useCache49(
29444
30899
  namespace_collection_nfc_patrol_routes
29445
30900
  );
29446
30901
  async function createIndexes() {
@@ -29887,7 +31342,7 @@ import {
29887
31342
  NotFoundError as NotFoundError37,
29888
31343
  paginate as paginate40,
29889
31344
  useAtlas as useAtlas77,
29890
- useCache as useCache49
31345
+ useCache as useCache50
29891
31346
  } from "@7365admin1/node-server-utils";
29892
31347
  import { ObjectId as ObjectId87 } from "mongodb";
29893
31348
  function useOccurrenceBookRepo() {
@@ -29919,7 +31374,7 @@ function useOccurrenceBookRepo() {
29919
31374
  }
29920
31375
  const namespace_collection = "occurrence-books";
29921
31376
  const collection = db.collection(namespace_collection);
29922
- const { delNamespace, getCache, setCache } = useCache49(namespace_collection);
31377
+ const { delNamespace, getCache, setCache } = useCache50(namespace_collection);
29923
31378
  async function add(value, session) {
29924
31379
  try {
29925
31380
  value = MOccurrenceBook(value);
@@ -30497,7 +31952,7 @@ import {
30497
31952
  NotFoundError as NotFoundError38,
30498
31953
  paginate as paginate41,
30499
31954
  useAtlas as useAtlas79,
30500
- useCache as useCache50
31955
+ useCache as useCache51
30501
31956
  } from "@7365admin1/node-server-utils";
30502
31957
  import { ObjectId as ObjectId89 } from "mongodb";
30503
31958
  function useBulletinVideoRepo() {
@@ -30516,7 +31971,7 @@ function useBulletinVideoRepo() {
30516
31971
  }
30517
31972
  const namespace_collection = "bulletin-videos";
30518
31973
  const collection = db.collection(namespace_collection);
30519
- const { delNamespace, getCache, setCache } = useCache50(namespace_collection);
31974
+ const { delNamespace, getCache, setCache } = useCache51(namespace_collection);
30520
31975
  async function add(value, session) {
30521
31976
  try {
30522
31977
  value = MBulletinVideo(value);
@@ -31048,7 +32503,7 @@ import {
31048
32503
  InternalServerError as InternalServerError51,
31049
32504
  logger as logger127,
31050
32505
  useAtlas as useAtlas81,
31051
- useCache as useCache51,
32506
+ useCache as useCache52,
31052
32507
  paginate as paginate42,
31053
32508
  makeCacheKey as makeCacheKey48
31054
32509
  } from "@7365admin1/node-server-utils";
@@ -31060,7 +32515,7 @@ function useStatementOfAccountRepo() {
31060
32515
  }
31061
32516
  const namespace_collection = "site.statement-of-accounts";
31062
32517
  const collection = db.collection(namespace_collection);
31063
- const { delNamespace, getCache, setCache } = useCache51(namespace_collection);
32518
+ const { delNamespace, getCache, setCache } = useCache52(namespace_collection);
31064
32519
  async function createTextIndex() {
31065
32520
  try {
31066
32521
  await collection.createIndex({
@@ -31110,9 +32565,9 @@ function useStatementOfAccountRepo() {
31110
32565
  page = page > 0 ? page - 1 : 0;
31111
32566
  let dateExpr = {};
31112
32567
  if (dateFrom && dateTo) {
31113
- const startDate = new Date(dateFrom);
32568
+ let startDate = new Date(dateFrom);
31114
32569
  startDate.setHours(0, 0, 0, 0);
31115
- const endDate = new Date(dateTo);
32570
+ let endDate = new Date(dateTo);
31116
32571
  endDate.setHours(23, 59, 59, 999);
31117
32572
  dateExpr = {
31118
32573
  $expr: {
@@ -31123,13 +32578,15 @@ function useStatementOfAccountRepo() {
31123
32578
  }
31124
32579
  };
31125
32580
  }
32581
+ const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
31126
32582
  const query = {
31127
32583
  ...status && status !== "all" && { status },
31128
32584
  ...search && {
31129
32585
  $or: [
31130
32586
  { unitOwner: { $regex: search, $options: "i" } },
31131
- { unit: { $regex: search, $options: "i" } },
31132
- { email: { $regex: search, $options: "i" } }
32587
+ { unit: { $regex: unitSearchRegex, $options: "i" } },
32588
+ { email: { $regex: search, $options: "i" } },
32589
+ { category: { $regex: search, $options: "i" } }
31133
32590
  ]
31134
32591
  },
31135
32592
  ...ObjectId91.isValid(site) && { site: new ObjectId91(site) },
@@ -31823,7 +33280,7 @@ import {
31823
33280
  NotFoundError as NotFoundError40,
31824
33281
  paginate as paginate43,
31825
33282
  useAtlas as useAtlas83,
31826
- useCache as useCache52
33283
+ useCache as useCache53
31827
33284
  } from "@7365admin1/node-server-utils";
31828
33285
  import { ObjectId as ObjectId93 } from "mongodb";
31829
33286
  function useEntryPassSettingsRepo() {
@@ -31833,7 +33290,7 @@ function useEntryPassSettingsRepo() {
31833
33290
  }
31834
33291
  const namespace_collection = "site.entrypass-settings";
31835
33292
  const collection = db.collection(namespace_collection);
31836
- const { delNamespace, getCache, setCache } = useCache52(namespace_collection);
33293
+ const { delNamespace, getCache, setCache } = useCache53(namespace_collection);
31837
33294
  async function createTextIndex() {
31838
33295
  try {
31839
33296
  await collection.createIndex({
@@ -32395,7 +33852,7 @@ import {
32395
33852
  logger as logger133,
32396
33853
  makeCacheKey as makeCacheKey50,
32397
33854
  useAtlas as useAtlas84,
32398
- useCache as useCache53
33855
+ useCache as useCache54
32399
33856
  } from "@7365admin1/node-server-utils";
32400
33857
  function useDashboardRepo() {
32401
33858
  const db = useAtlas84.getDb();
@@ -32406,7 +33863,7 @@ function useDashboardRepo() {
32406
33863
  const work_order_collection = db.collection("work-orders");
32407
33864
  const visitor_collection = db.collection("visitor.transactions");
32408
33865
  const namespace_collection = "dashboard";
32409
- const { delNamespace, getCache, setCache } = useCache53(namespace_collection);
33866
+ const { delNamespace, getCache, setCache } = useCache54(namespace_collection);
32410
33867
  async function getAll({
32411
33868
  site = ""
32412
33869
  }, session) {
@@ -32570,7 +34027,7 @@ import {
32570
34027
  makeCacheKey as makeCacheKey51,
32571
34028
  paginate as paginate44,
32572
34029
  useAtlas as useAtlas85,
32573
- useCache as useCache54
34030
+ useCache as useCache55
32574
34031
  } from "@7365admin1/node-server-utils";
32575
34032
  import { ObjectId as ObjectId95 } from "mongodb";
32576
34033
  function useNfcPatrolRouteRepo() {
@@ -32580,7 +34037,7 @@ function useNfcPatrolRouteRepo() {
32580
34037
  }
32581
34038
  const namespace_collection = "nfc-patrol-routes";
32582
34039
  const collection = db.collection(namespace_collection);
32583
- const { delNamespace, getCache, setCache } = useCache54(namespace_collection);
34040
+ const { delNamespace, getCache, setCache } = useCache55(namespace_collection);
32584
34041
  async function createIndexes() {
32585
34042
  try {
32586
34043
  await collection.createIndexes([
@@ -33317,7 +34774,7 @@ import {
33317
34774
  NotFoundError as NotFoundError43,
33318
34775
  paginate as paginate45,
33319
34776
  useAtlas as useAtlas87,
33320
- useCache as useCache55
34777
+ useCache as useCache56
33321
34778
  } from "@7365admin1/node-server-utils";
33322
34779
  import { ObjectId as ObjectId97 } from "mongodb";
33323
34780
  function useIncidentReportRepo() {
@@ -33348,7 +34805,7 @@ function useIncidentReportRepo() {
33348
34805
  }
33349
34806
  const namespace_collection = "incident-reports";
33350
34807
  const collection = db.collection(namespace_collection);
33351
- const { delNamespace, getCache, setCache } = useCache55(namespace_collection);
34808
+ const { delNamespace, getCache, setCache } = useCache56(namespace_collection);
33352
34809
  async function add(value, session) {
33353
34810
  try {
33354
34811
  value = MIncidentReport(value);
@@ -33423,6 +34880,9 @@ function useIncidentReportRepo() {
33423
34880
  $regex: search,
33424
34881
  $options: "i"
33425
34882
  }
34883
+ },
34884
+ {
34885
+ approvedByName: { $regex: search, $options: "i" }
33426
34886
  }
33427
34887
  ]
33428
34888
  },
@@ -34089,7 +35549,7 @@ import {
34089
35549
  makeCacheKey as makeCacheKey53,
34090
35550
  NotFoundError as NotFoundError45,
34091
35551
  useAtlas as useAtlas89,
34092
- useCache as useCache56
35552
+ useCache as useCache57
34093
35553
  } from "@7365admin1/node-server-utils";
34094
35554
  import { ObjectId as ObjectId99 } from "mongodb";
34095
35555
  function useNfcPatrolSettingsRepository() {
@@ -34099,7 +35559,7 @@ function useNfcPatrolSettingsRepository() {
34099
35559
  }
34100
35560
  const namespace_collection = "site.nfc-patrol-settings";
34101
35561
  const collection = db.collection(namespace_collection);
34102
- const { delNamespace, setCache, getCache } = useCache56(namespace_collection);
35562
+ const { delNamespace, setCache, getCache } = useCache57(namespace_collection);
34103
35563
  async function createIndexes() {
34104
35564
  try {
34105
35565
  await collection.createIndexes([{ key: { site: 1 }, unique: true }]);
@@ -34344,7 +35804,7 @@ import {
34344
35804
  NotFoundError as NotFoundError46,
34345
35805
  paginate as paginate46,
34346
35806
  useAtlas as useAtlas91,
34347
- useCache as useCache57
35807
+ useCache as useCache58
34348
35808
  } from "@7365admin1/node-server-utils";
34349
35809
 
34350
35810
  // src/models/occurrence-subject.model.ts
@@ -34421,7 +35881,7 @@ function useOccurrenceSubjectRepo() {
34421
35881
  }
34422
35882
  const namespace_collection = "occurrence-subjects";
34423
35883
  const collection = db.collection(namespace_collection);
34424
- const { delNamespace, getCache, setCache } = useCache57(namespace_collection);
35884
+ const { delNamespace, getCache, setCache } = useCache58(namespace_collection);
34425
35885
  async function add(value, session) {
34426
35886
  try {
34427
35887
  value = MOccurrenceSubject(value);
@@ -34989,7 +36449,7 @@ import {
34989
36449
  NotFoundError as NotFoundError47,
34990
36450
  paginate as paginate47,
34991
36451
  useAtlas as useAtlas93,
34992
- useCache as useCache58
36452
+ useCache as useCache59
34993
36453
  } from "@7365admin1/node-server-utils";
34994
36454
  import { ObjectId as ObjectId103 } from "mongodb";
34995
36455
  function useOnlineFormRepo() {
@@ -34999,7 +36459,7 @@ function useOnlineFormRepo() {
34999
36459
  }
35000
36460
  const namespace_collection = "online-forms";
35001
36461
  const collection = db.collection(namespace_collection);
35002
- const { delNamespace, getCache, setCache } = useCache58(namespace_collection);
36462
+ const { delNamespace, getCache, setCache } = useCache59(namespace_collection);
35003
36463
  async function createTextIndex() {
35004
36464
  try {
35005
36465
  await collection.createIndex({
@@ -35745,7 +37205,7 @@ import {
35745
37205
  makeCacheKey as makeCacheKey56,
35746
37206
  paginate as paginate48,
35747
37207
  useAtlas as useAtlas95,
35748
- useCache as useCache59
37208
+ useCache as useCache60
35749
37209
  } from "@7365admin1/node-server-utils";
35750
37210
  import { ObjectId as ObjectId105 } from "mongodb";
35751
37211
  function useNfcPatrolLogRepo() {
@@ -35755,7 +37215,7 @@ function useNfcPatrolLogRepo() {
35755
37215
  }
35756
37216
  const namespace_collection = "nfc-patrol-logs";
35757
37217
  const collection = db.collection(namespace_collection);
35758
- const { delNamespace, getCache, setCache } = useCache59(namespace_collection);
37218
+ const { delNamespace, getCache, setCache } = useCache60(namespace_collection);
35759
37219
  async function createIndexes() {
35760
37220
  try {
35761
37221
  await collection.createIndexes([
@@ -35978,7 +37438,9 @@ function useNfcPatrolLogController() {
35978
37438
  };
35979
37439
  }
35980
37440
  export {
37441
+ ANPRMode,
35981
37442
  AccessTypeProps,
37443
+ BULLETIN_RECIPIENTS,
35982
37444
  DEVICE_STATUS,
35983
37445
  EAccessCardTypes,
35984
37446
  EAccessCardUserTypes,
@@ -36037,12 +37499,15 @@ export {
36037
37499
  MVerification,
36038
37500
  MVisitorTransaction,
36039
37501
  MWorkOrder,
37502
+ OrgNature,
36040
37503
  PERSON_TYPES,
37504
+ STATUS_VALUES,
36041
37505
  UseAccessManagementRepo,
36042
- allowedCategories,
37506
+ VehicleCategory,
37507
+ VehicleStatus,
37508
+ VehicleType,
36043
37509
  allowedFieldsSite,
36044
37510
  allowedNatures,
36045
- allowedTypes,
36046
37511
  attendanceSchema,
36047
37512
  attendanceSettingsSchema,
36048
37513
  chatSchema,