@7365admin1/core 2.14.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.js CHANGED
@@ -30,7 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ ANPRMode: () => ANPRMode,
33
34
  AccessTypeProps: () => AccessTypeProps,
35
+ BULLETIN_RECIPIENTS: () => BULLETIN_RECIPIENTS,
34
36
  DEVICE_STATUS: () => DEVICE_STATUS,
35
37
  EAccessCardTypes: () => EAccessCardTypes,
36
38
  EAccessCardUserTypes: () => EAccessCardUserTypes,
@@ -89,12 +91,15 @@ __export(src_exports, {
89
91
  MVerification: () => MVerification,
90
92
  MVisitorTransaction: () => MVisitorTransaction,
91
93
  MWorkOrder: () => MWorkOrder,
94
+ OrgNature: () => OrgNature,
92
95
  PERSON_TYPES: () => PERSON_TYPES,
96
+ STATUS_VALUES: () => STATUS_VALUES,
93
97
  UseAccessManagementRepo: () => UseAccessManagementRepo,
94
- allowedCategories: () => allowedCategories,
98
+ VehicleCategory: () => VehicleCategory,
99
+ VehicleStatus: () => VehicleStatus,
100
+ VehicleType: () => VehicleType,
95
101
  allowedFieldsSite: () => allowedFieldsSite,
96
102
  allowedNatures: () => allowedNatures,
97
- allowedTypes: () => allowedTypes,
98
103
  attendanceSchema: () => attendanceSchema,
99
104
  attendanceSettingsSchema: () => attendanceSettingsSchema,
100
105
  chatSchema: () => chatSchema,
@@ -1669,7 +1674,9 @@ var schemaOccurrenceEntry = import_joi5.default.object({
1669
1674
  subject: import_joi5.default.string().hex().optional().allow(null, ""),
1670
1675
  occurrence: import_joi5.default.string().optional().allow(null, ""),
1671
1676
  signature: import_joi5.default.string().hex().optional().allow(null, ""),
1672
- incidentReportId: import_joi5.default.string().hex().optional().allow(null, "")
1677
+ incidentReportId: import_joi5.default.string().hex().optional().allow(null, ""),
1678
+ eSignature: import_joi5.default.string().required(),
1679
+ createdByName: import_joi5.default.string().optional().allow(null, "")
1673
1680
  });
1674
1681
  var schemaUpdateOccurrenceEntry = import_joi5.default.object({
1675
1682
  _id: import_joi5.default.string().hex().required(),
@@ -1678,7 +1685,9 @@ var schemaUpdateOccurrenceEntry = import_joi5.default.object({
1678
1685
  subject: import_joi5.default.string().hex().optional().allow(null, ""),
1679
1686
  occurrence: import_joi5.default.string().optional().allow(null, ""),
1680
1687
  signature: import_joi5.default.string().hex().optional().allow(null, ""),
1681
- incidentReportId: import_joi5.default.string().hex().optional().allow(null, "")
1688
+ incidentReportId: import_joi5.default.string().hex().optional().allow(null, ""),
1689
+ eSignature: import_joi5.default.string().required(),
1690
+ createdByName: import_joi5.default.string().optional().allow(null, "")
1682
1691
  });
1683
1692
  function MOccurrenceEntry(value) {
1684
1693
  if (value._id && typeof value._id === "string") {
@@ -1735,6 +1744,8 @@ function MOccurrenceEntry(value) {
1735
1744
  incidentReportId: value.incidentReportId,
1736
1745
  signature: value.signature,
1737
1746
  userName: value.userName,
1747
+ eSignature: value.eSignature,
1748
+ createdByName: value.createdByName,
1738
1749
  date: value.date,
1739
1750
  createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1740
1751
  updatedAt: value.updatedAt,
@@ -6617,12 +6628,12 @@ function useUserController() {
6617
6628
  }
6618
6629
  }
6619
6630
  async function createUserByVerification(req, res, next) {
6620
- const allowedTypes2 = ["user-sign-up", "user-invite"];
6631
+ const allowedTypes = ["user-sign-up", "user-invite"];
6621
6632
  const validation = import_joi14.default.object({
6622
6633
  id: import_joi14.default.string().hex().required(),
6623
6634
  name: import_joi14.default.string().required(),
6624
6635
  password: import_joi14.default.string().required(),
6625
- type: import_joi14.default.string().required().valid(...allowedTypes2)
6636
+ type: import_joi14.default.string().required().valid(...allowedTypes)
6626
6637
  });
6627
6638
  const id = req.params.id;
6628
6639
  const payload = { ...req.body };
@@ -15533,12 +15544,37 @@ var import_node_server_utils81 = require("@7365admin1/node-server-utils");
15533
15544
  var import_node_server_utils80 = require("@7365admin1/node-server-utils");
15534
15545
  var import_joi43 = __toESM(require("joi"));
15535
15546
  var import_mongodb45 = require("mongodb");
15536
- var allowedTypes = ["whitelist", "blacklist"];
15537
- var allowedCategories = ["resident", "visitor"];
15547
+ var VehicleType = /* @__PURE__ */ ((VehicleType2) => {
15548
+ VehicleType2["WHITELIST"] = "whitelist";
15549
+ VehicleType2["BLOCKLIST"] = "blocklist";
15550
+ return VehicleType2;
15551
+ })(VehicleType || {});
15552
+ var VehicleCategory = /* @__PURE__ */ ((VehicleCategory2) => {
15553
+ VehicleCategory2["RESIDENT"] = "resident";
15554
+ VehicleCategory2["VISITOR"] = "visitor";
15555
+ return VehicleCategory2;
15556
+ })(VehicleCategory || {});
15557
+ var VehicleStatus = /* @__PURE__ */ ((VehicleStatus2) => {
15558
+ VehicleStatus2["PENDING"] = "pending";
15559
+ VehicleStatus2["ACTIVE"] = "active";
15560
+ VehicleStatus2["INACTIVE"] = "inactive";
15561
+ VehicleStatus2["DELETED"] = "deleted";
15562
+ return VehicleStatus2;
15563
+ })(VehicleStatus || {});
15564
+ var OrgNature = /* @__PURE__ */ ((OrgNature2) => {
15565
+ OrgNature2["PROPERTY_MANAGEMENT_AGENCY"] = "property_management_agency";
15566
+ OrgNature2["SECURITY_AGENCY"] = "security_agency";
15567
+ return OrgNature2;
15568
+ })(OrgNature || {});
15569
+ var ANPRMode = /* @__PURE__ */ ((ANPRMode2) => {
15570
+ ANPRMode2["TRAFFIC_REDLIST"] = "TrafficRedList";
15571
+ ANPRMode2["TRAFFIC_BLACKLIST"] = "TrafficBlackList";
15572
+ return ANPRMode2;
15573
+ })(ANPRMode || {});
15538
15574
  var vehicleSchema = import_joi43.default.object({
15539
15575
  plateNumber: import_joi43.default.string().required(),
15540
- type: import_joi43.default.string().required().allow(...allowedTypes),
15541
- category: import_joi43.default.string().required().allow(...allowedCategories),
15576
+ type: import_joi43.default.string().required().valid(...Object.values(VehicleType)),
15577
+ category: import_joi43.default.string().required().valid(...Object.values(VehicleCategory)),
15542
15578
  name: import_joi43.default.string().required(),
15543
15579
  phoneNumber: import_joi43.default.string().optional().allow("", null),
15544
15580
  recNo: import_joi43.default.string().optional().allow("", null),
@@ -15552,7 +15588,8 @@ var vehicleSchema = import_joi43.default.object({
15552
15588
  seasonPassType: import_joi43.default.string().optional().allow("", null),
15553
15589
  start: import_joi43.default.date().optional().allow("", null),
15554
15590
  end: import_joi43.default.date().optional().allow("", null),
15555
- unitName: import_joi43.default.string().optional().allow("", null)
15591
+ unitName: import_joi43.default.string().optional().allow("", null),
15592
+ status: import_joi43.default.string().optional().valid(...Object.values(VehicleStatus)).allow("")
15556
15593
  });
15557
15594
  function MVehicle(value) {
15558
15595
  const { error } = vehicleSchema.validate(value);
@@ -15581,6 +15618,11 @@ function MVehicle(value) {
15581
15618
  throw new import_node_server_utils80.BadRequestError("Invalid building unit ID format.");
15582
15619
  }
15583
15620
  }
15621
+ const createdAtDate = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
15622
+ const expiredDate = new Date(createdAtDate);
15623
+ expiredDate.setFullYear(expiredDate.getFullYear() + 10);
15624
+ const createdAt = createdAtDate.toISOString();
15625
+ const expiredAt = value.end ?? expiredDate.toISOString();
15584
15626
  return {
15585
15627
  plateNumber: value.plateNumber ?? "",
15586
15628
  type: value.type ?? "",
@@ -15596,11 +15638,11 @@ function MVehicle(value) {
15596
15638
  nric: value.nric ?? "",
15597
15639
  remarks: value.remarks ?? "",
15598
15640
  seasonPassType: value.seasonPassType ?? "",
15599
- start: value.start ?? "",
15600
- end: value.end ?? "",
15601
- status: value.status ?? "active",
15641
+ start: value.start ?? createdAt,
15642
+ end: value.end ?? expiredAt,
15643
+ status: value.status ?? "active" /* ACTIVE */,
15602
15644
  unitName: value.unitName ?? "",
15603
- createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
15645
+ createdAt,
15604
15646
  updatedAt: value.updatedAt ?? "",
15605
15647
  deletedAt: value.deletedAt ?? ""
15606
15648
  };
@@ -15658,18 +15700,44 @@ function useVehicleRepo() {
15658
15700
  }
15659
15701
  const namespace_collection = "vehicles";
15660
15702
  const collection = db.collection(namespace_collection);
15661
- const { delNamespace, setCache, getCache, delCache } = (0, import_node_server_utils81.useCache)(namespace_collection);
15703
+ const { delNamespace, setCache, getCache } = (0, import_node_server_utils81.useCache)(namespace_collection);
15662
15704
  async function createIndex() {
15663
15705
  try {
15664
15706
  await collection.createIndexes([
15665
15707
  { key: { status: 1 } },
15666
15708
  { key: { type: 1 } },
15667
- { key: { category: 1 } }
15709
+ { key: { category: 1 } },
15710
+ {
15711
+ key: { nric: 1 },
15712
+ name: "uniq_nric_not_deleted_nonempty",
15713
+ unique: true,
15714
+ partialFilterExpression: {
15715
+ nric: { $type: "string", $gt: "" },
15716
+ deletedAt: ""
15717
+ }
15718
+ }
15668
15719
  ]);
15669
15720
  } catch (error) {
15721
+ console.error("createIndexes error:", error);
15670
15722
  throw new import_node_server_utils81.InternalServerError("Failed to create index on vehicle.");
15671
15723
  }
15672
15724
  }
15725
+ async function createTextIndex() {
15726
+ try {
15727
+ await collection.createIndex({
15728
+ name: "text",
15729
+ plateNumber: "text",
15730
+ level: "text",
15731
+ unitName: "text",
15732
+ phoneNumber: "text",
15733
+ nric: "text"
15734
+ });
15735
+ } catch (error) {
15736
+ throw new import_node_server_utils81.InternalServerError(
15737
+ "Failed to create text index on visitor transaction."
15738
+ );
15739
+ }
15740
+ }
15673
15741
  async function add(value, session) {
15674
15742
  try {
15675
15743
  value = MVehicle(value);
@@ -15701,37 +15769,40 @@ function useVehicleRepo() {
15701
15769
  search = "",
15702
15770
  sort = {},
15703
15771
  type = "",
15704
- category = ""
15772
+ category = "",
15773
+ status = ""
15705
15774
  }) {
15706
15775
  page = page > 0 ? page - 1 : 0;
15707
- const query = { status: "active" };
15776
+ const baseQuery = {
15777
+ ...status && { status },
15778
+ ...type && { type },
15779
+ ...type && { type }
15780
+ };
15781
+ let query = { ...baseQuery };
15782
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15708
15783
  const cacheOptions = {
15709
- status: "active",
15784
+ ...status && { status },
15785
+ ...type && { type },
15786
+ ...type && { type },
15710
15787
  page: String(page),
15711
- limit: String(limit)
15788
+ limit: String(limit),
15789
+ sort: JSON.stringify(sort)
15712
15790
  };
15713
- sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15714
- cacheOptions.sort = JSON.stringify(sort);
15715
15791
  if (search) {
15716
- query.$or = [{ name: { $regex: search, $options: "i" } }];
15792
+ query.$text = { $search: search };
15717
15793
  cacheOptions.search = search;
15718
15794
  }
15719
- if (type) {
15720
- query.type = type;
15721
- cacheOptions.type = type;
15722
- }
15723
- if (category) {
15724
- query.category = category;
15725
- cacheOptions.category = category;
15726
- }
15727
15795
  const cacheKey = (0, import_node_server_utils81.makeCacheKey)(namespace_collection, cacheOptions);
15728
15796
  const cachedData = await getCache(cacheKey);
15729
15797
  if (cachedData) {
15730
15798
  import_node_server_utils81.logger.info(`Cache hit for key: ${cacheKey}`);
15731
15799
  return cachedData;
15732
15800
  }
15801
+ const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15733
15802
  try {
15734
- const items = await collection.aggregate([
15803
+ let items = [];
15804
+ let length = 0;
15805
+ items = await collection.aggregate([
15735
15806
  { $match: query },
15736
15807
  { $sort: sort },
15737
15808
  { $skip: page * limit },
@@ -15757,17 +15828,65 @@ function useVehicleRepo() {
15757
15828
  level: 1,
15758
15829
  unit: "$units.name",
15759
15830
  plateNumber: 1,
15760
- recNo: 1
15831
+ recNo: 1,
15832
+ nric: 1
15761
15833
  }
15762
15834
  }
15763
15835
  ]).toArray();
15764
- const length = await collection.countDocuments(query);
15836
+ length = await collection.countDocuments(query);
15837
+ if ((!items || items.length === 0) && search) {
15838
+ const escaped = escapeRegex(search);
15839
+ const regexQuery = {
15840
+ ...baseQuery,
15841
+ ...type && { type },
15842
+ ...category && { category },
15843
+ $or: [
15844
+ { name: { $regex: escaped, $options: "i" } },
15845
+ { plateNumber: { $regex: escaped, $options: "i" } },
15846
+ { company: { $regex: escaped, $options: "i" } },
15847
+ { level: { $regex: escaped, $options: "i" } },
15848
+ { unitName: { $regex: escaped, $options: "i" } },
15849
+ { contact: { $regex: escaped, $options: "i" } },
15850
+ { nric: { $regex: escaped, $options: "i" } }
15851
+ ]
15852
+ };
15853
+ items = await collection.aggregate([
15854
+ { $match: regexQuery },
15855
+ { $sort: sort },
15856
+ { $skip: page * limit },
15857
+ { $limit: limit },
15858
+ {
15859
+ $lookup: {
15860
+ from: "building-units",
15861
+ localField: "unit",
15862
+ foreignField: "_id",
15863
+ as: "units",
15864
+ pipeline: [{ $project: { name: 1 } }]
15865
+ }
15866
+ },
15867
+ { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15868
+ {
15869
+ $project: {
15870
+ name: 1,
15871
+ type: 1,
15872
+ category: 1,
15873
+ status: 1,
15874
+ phoneNumber: 1,
15875
+ block: 1,
15876
+ level: 1,
15877
+ unit: "$units.name",
15878
+ plateNumber: 1,
15879
+ recNo: 1,
15880
+ nric: 1
15881
+ }
15882
+ }
15883
+ ]).toArray();
15884
+ length = await collection.countDocuments(regexQuery);
15885
+ }
15765
15886
  const data = (0, import_node_server_utils81.paginate)(items, page, limit, length);
15766
- setCache(cacheKey, data, 15 * 60).then(() => {
15767
- import_node_server_utils81.logger.info(`Cache set for key: ${cacheKey}`);
15768
- }).catch((err) => {
15769
- import_node_server_utils81.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
15770
- });
15887
+ setCache(cacheKey, data, 15 * 60).then(() => import_node_server_utils81.logger.info(`Cache set for key: ${cacheKey}`)).catch(
15888
+ (err) => import_node_server_utils81.logger.error(`Failed to set cache for key: ${cacheKey}`, err)
15889
+ );
15771
15890
  return data;
15772
15891
  } catch (error) {
15773
15892
  throw error;
@@ -15861,7 +15980,10 @@ function useVehicleRepo() {
15861
15980
  type: 1,
15862
15981
  category: 1,
15863
15982
  status: 1,
15864
- recNo: 1
15983
+ recNo: 1,
15984
+ start: 1,
15985
+ end: 1,
15986
+ seasonPassType: 1
15865
15987
  }
15866
15988
  }
15867
15989
  ]).toArray();
@@ -15910,7 +16032,7 @@ function useVehicleRepo() {
15910
16032
  throw error2;
15911
16033
  }
15912
16034
  }
15913
- async function updateVehicle(_id, value) {
16035
+ async function updateVehicle(_id, value, session) {
15914
16036
  try {
15915
16037
  _id = new import_mongodb46.ObjectId(_id);
15916
16038
  } catch (error) {
@@ -15921,7 +16043,11 @@ function useVehicleRepo() {
15921
16043
  ...value,
15922
16044
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
15923
16045
  };
15924
- const res = await collection.updateOne({ _id }, { $set: updateValue });
16046
+ const res = await collection.updateOne(
16047
+ { _id },
16048
+ { $set: updateValue },
16049
+ { session }
16050
+ );
15925
16051
  if (res.modifiedCount === 0) {
15926
16052
  throw new import_node_server_utils81.InternalServerError("Unable to update vehicle.");
15927
16053
  }
@@ -16000,8 +16126,126 @@ function useVehicleRepo() {
16000
16126
  );
16001
16127
  }
16002
16128
  }
16129
+ async function getVehiclesByNRIC({
16130
+ page = 1,
16131
+ limit = 10,
16132
+ search = "",
16133
+ sort = {}
16134
+ }) {
16135
+ page = page > 0 ? page - 1 : 0;
16136
+ const baseQuery = {
16137
+ deletedAt: ""
16138
+ };
16139
+ let query = { ...baseQuery };
16140
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
16141
+ const cacheOptions = {
16142
+ deletedAt: "",
16143
+ page: String(page),
16144
+ limit: String(limit),
16145
+ sort: JSON.stringify(sort)
16146
+ };
16147
+ if (search) {
16148
+ query.$text = { $search: search };
16149
+ cacheOptions.search = search;
16150
+ }
16151
+ const cacheKey = (0, import_node_server_utils81.makeCacheKey)(namespace_collection, cacheOptions);
16152
+ const cachedData = await getCache(cacheKey);
16153
+ if (cachedData) {
16154
+ import_node_server_utils81.logger.info(`Cache hit for key: ${cacheKey}`);
16155
+ return cachedData;
16156
+ }
16157
+ const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16158
+ try {
16159
+ let items = [];
16160
+ let length = 0;
16161
+ items = await collection.aggregate([
16162
+ { $match: query },
16163
+ { $sort: sort },
16164
+ { $skip: page * limit },
16165
+ { $limit: limit },
16166
+ {
16167
+ $project: {
16168
+ name: 1,
16169
+ phoneNumber: 1,
16170
+ plateNumber: 1,
16171
+ recNo: 1,
16172
+ nric: 1,
16173
+ status: 1,
16174
+ type: 1
16175
+ }
16176
+ }
16177
+ ]).toArray();
16178
+ length = await collection.countDocuments(query);
16179
+ if ((!items || items.length === 0) && search) {
16180
+ const escaped = escapeRegex(search);
16181
+ const regexQuery = {
16182
+ ...baseQuery,
16183
+ $or: [
16184
+ { name: { $regex: escaped, $options: "i" } },
16185
+ { plateNumber: { $regex: escaped, $options: "i" } },
16186
+ { level: { $regex: escaped, $options: "i" } },
16187
+ { nric: { $regex: escaped, $options: "i" } }
16188
+ ]
16189
+ };
16190
+ items = await collection.aggregate([
16191
+ { $match: regexQuery },
16192
+ { $sort: sort },
16193
+ { $skip: page * limit },
16194
+ { $limit: limit },
16195
+ {
16196
+ $project: {
16197
+ name: 1,
16198
+ phoneNumber: 1,
16199
+ plateNumber: 1,
16200
+ recNo: 1,
16201
+ nric: 1,
16202
+ status: 1,
16203
+ type: 1
16204
+ }
16205
+ }
16206
+ ]).toArray();
16207
+ length = await collection.countDocuments(regexQuery);
16208
+ }
16209
+ const data = (0, import_node_server_utils81.paginate)(items, page, limit, length);
16210
+ setCache(cacheKey, data, 15 * 60).then(() => import_node_server_utils81.logger.info(`Cache set for key: ${cacheKey}`)).catch(
16211
+ (err) => import_node_server_utils81.logger.error(`Failed to set cache for key: ${cacheKey}`, err)
16212
+ );
16213
+ return data;
16214
+ } catch (error) {
16215
+ throw error;
16216
+ }
16217
+ }
16218
+ async function deleteExpiredVehicles(session) {
16219
+ try {
16220
+ const tenYearsAgo = /* @__PURE__ */ new Date();
16221
+ tenYearsAgo.setFullYear(tenYearsAgo.getFullYear() - 10);
16222
+ const res = await collection.updateMany(
16223
+ {
16224
+ status: { $ne: "deleted" },
16225
+ end: { $exists: true, $lte: tenYearsAgo }
16226
+ // check only end
16227
+ },
16228
+ { $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } },
16229
+ { session }
16230
+ );
16231
+ if (res.modifiedCount === 0)
16232
+ throw new import_node_server_utils81.InternalServerError("Unable to delete vehicle.");
16233
+ delNamespace().then(() => {
16234
+ import_node_server_utils81.logger.info(`Cache cleared for namespace: ${namespace_collection}`);
16235
+ }).catch((err) => {
16236
+ import_node_server_utils81.logger.error(
16237
+ `Failed to clear cache for namespace: ${namespace_collection}`,
16238
+ err
16239
+ );
16240
+ });
16241
+ return res.modifiedCount;
16242
+ } catch (error) {
16243
+ throw error;
16244
+ }
16245
+ }
16003
16246
  return {
16004
16247
  createIndex,
16248
+ createTextIndex,
16005
16249
  add,
16006
16250
  getVehicles,
16007
16251
  getSeasonPassTypes,
@@ -16009,7 +16253,9 @@ function useVehicleRepo() {
16009
16253
  updateVehicle,
16010
16254
  deleteVehicle,
16011
16255
  getByPlaceNumber,
16012
- getVehicleByPlateNumber
16256
+ getVehicleByPlateNumber,
16257
+ getVehiclesByNRIC,
16258
+ deleteExpiredVehicles
16013
16259
  };
16014
16260
  }
16015
16261
 
@@ -16395,7 +16641,7 @@ function useDahuaService() {
16395
16641
  username: import_joi45.default.string().required(),
16396
16642
  password: import_joi45.default.string().required(),
16397
16643
  plateNumber: import_joi45.default.string().required(),
16398
- mode: import_joi45.default.string().valid("TrafficBlackList", "TrafficRedList").required(),
16644
+ mode: import_joi45.default.string().valid(...Object.values(ANPRMode)).required(),
16399
16645
  start: import_joi45.default.string().isoDate().optional().allow("", null),
16400
16646
  end: import_joi45.default.string().isoDate().optional().allow("", null),
16401
16647
  owner: import_joi45.default.string().optional().allow("", null)
@@ -16419,7 +16665,7 @@ function useDahuaService() {
16419
16665
  host: value.host,
16420
16666
  username: value.username,
16421
16667
  password: value.password,
16422
- endpoint: `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&BeginTime=${value.start}&CancelTime=${value.end}&+OpenGate=true&MasterOfCar=${value.owner}`
16668
+ endpoint: `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&BeginTime=${value.start}&CancelTime=${value.end}&OpenGate=true&MasterOfCar=${value.owner}`
16423
16669
  });
16424
16670
  return response;
16425
16671
  } catch (error2) {
@@ -16461,18 +16707,148 @@ function useDahuaService() {
16461
16707
 
16462
16708
  // src/services/vehicle.service.ts
16463
16709
  var import_node_server_utils83 = require("@7365admin1/node-server-utils");
16710
+ function formatDahuaDate(date) {
16711
+ const pad = (n) => String(n).padStart(2, "0");
16712
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(
16713
+ date.getDate()
16714
+ )} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(
16715
+ date.getSeconds()
16716
+ )}`;
16717
+ }
16464
16718
  function useVehicleService() {
16465
- const { add: _add, deleteVehicle: _deleteVehicle } = useVehicleRepo();
16719
+ const {
16720
+ add: _add,
16721
+ deleteVehicle: _deleteVehicle,
16722
+ updateVehicle: _updateVehicle,
16723
+ getVehicleById: _getVehicleById,
16724
+ deleteExpiredVehicles: _deleteExpiredVehicles
16725
+ } = useVehicleRepo();
16466
16726
  const {
16467
16727
  addPlateNumber: _addPlateNumber,
16468
16728
  removePlateNumber: _removePlateNumber
16469
16729
  } = useDahuaService();
16470
16730
  const { getAllCameraWithPassword: _getAllSiteCameras } = useSiteCameraRepo();
16731
+ const { getById: _getById } = useOrgRepo();
16471
16732
  async function add(value) {
16733
+ const session = import_node_server_utils83.useAtlas.getClient()?.startSession();
16734
+ if (!session)
16735
+ throw new Error("Unable to start session for vehicle service.");
16736
+ const org = await _getById(value.org);
16737
+ if (!org)
16738
+ throw new import_node_server_utils83.BadRequestError("Org not found");
16739
+ if (!Object.values(OrgNature).includes(org.nature)) {
16740
+ throw new import_node_server_utils83.BadRequestError(
16741
+ "This organization is not allowed to add vehicles."
16742
+ );
16743
+ }
16744
+ value.status = "active" /* ACTIVE */;
16745
+ const addedBySecurity = org.nature === "security_agency" /* SECURITY_AGENCY */;
16746
+ const addedByPM = org.nature === "property_management_agency" /* PROPERTY_MANAGEMENT_AGENCY */;
16747
+ if (addedBySecurity)
16748
+ value.status = "pending" /* PENDING */;
16749
+ const isBlocklist = value.type === "blocklist" /* BLOCKLIST */;
16750
+ if (isBlocklist)
16751
+ value.status = "inactive" /* INACTIVE */;
16752
+ const _type = value.type;
16753
+ const _mode = isBlocklist ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
16754
+ const hasStart = typeof value.start === "string" ? value.start.trim() !== "" : !!value.start;
16755
+ const hasEnd = typeof value.end === "string" ? value.end.trim() !== "" : !!value.end;
16756
+ let startDate = null;
16757
+ let endDate = null;
16758
+ let start = "";
16759
+ let end = "";
16760
+ let startDateDahua;
16761
+ let endDateDahua;
16762
+ if (addedByPM && !hasStart && !hasEnd) {
16763
+ startDate = /* @__PURE__ */ new Date();
16764
+ endDate = new Date(startDate);
16765
+ endDate.setFullYear(endDate.getFullYear() + 10);
16766
+ start = startDate.toISOString();
16767
+ end = endDate.toISOString();
16768
+ startDateDahua = formatDahuaDate(startDate);
16769
+ endDateDahua = formatDahuaDate(endDate);
16770
+ } else {
16771
+ if (hasStart) {
16772
+ startDate = new Date(value.start);
16773
+ start = startDate.toISOString();
16774
+ startDateDahua = formatDahuaDate(startDate);
16775
+ }
16776
+ if (hasEnd) {
16777
+ endDate = new Date(value.end);
16778
+ end = endDate.toISOString();
16779
+ endDateDahua = formatDahuaDate(endDate);
16780
+ }
16781
+ }
16782
+ const owner = String(value.name ?? "").substring(0, 15);
16783
+ try {
16784
+ session.startTransaction();
16785
+ let message = "Vehicle plate number needs approval from property management.";
16786
+ if (value.status && value.status !== "pending" /* PENDING */) {
16787
+ const siteCameras = [];
16788
+ let page = 1;
16789
+ let pages = 1;
16790
+ const limit = 20;
16791
+ do {
16792
+ const siteCameraReq = await _getAllSiteCameras({
16793
+ site: value.site,
16794
+ type: "anpr",
16795
+ direction: ["both", "entry"],
16796
+ page,
16797
+ limit
16798
+ });
16799
+ pages = siteCameraReq.pages || 1;
16800
+ siteCameras.push(...siteCameraReq.items);
16801
+ page++;
16802
+ } while (page < pages);
16803
+ if (!siteCameras.length)
16804
+ throw new import_node_server_utils83.BadRequestError("No site cameras found.");
16805
+ for (const camera of siteCameras) {
16806
+ const { host, username, password } = camera;
16807
+ const dahuaPayload = {
16808
+ host,
16809
+ username,
16810
+ password,
16811
+ plateNumber: value.plateNumber,
16812
+ mode: _mode,
16813
+ owner,
16814
+ ...startDateDahua ? { start: startDateDahua } : "",
16815
+ ...endDateDahua ? { end: endDateDahua } : ""
16816
+ };
16817
+ const dahuaResponse = await _addPlateNumber(dahuaPayload);
16818
+ if (dahuaResponse?.statusCode !== 200) {
16819
+ throw new import_node_server_utils83.BadRequestError(
16820
+ `Failed to add plate number to ANPR ${_type}`
16821
+ );
16822
+ }
16823
+ const responseData = dahuaResponse?.data.toString("utf-8");
16824
+ value.recNo = responseData.split("=")[1]?.trim();
16825
+ message = `Vehicle plate number added to ${_type} successfully.`;
16826
+ }
16827
+ }
16828
+ const formattedValue = {
16829
+ ...value,
16830
+ start,
16831
+ // ISO string or ""
16832
+ end
16833
+ // ISO string or ""
16834
+ };
16835
+ await _add(formattedValue, session);
16836
+ await session.commitTransaction();
16837
+ return message;
16838
+ } catch (error) {
16839
+ import_node_server_utils83.logger.error("Error in vehicle service add:", error);
16840
+ await session.abortTransaction();
16841
+ throw error;
16842
+ } finally {
16843
+ session.endSession();
16844
+ }
16845
+ }
16846
+ async function deleteVehicle(_id, recno, site, type, bypass = false) {
16472
16847
  const session = import_node_server_utils83.useAtlas.getClient()?.startSession();
16473
16848
  if (!session) {
16474
16849
  throw new Error("Unable to start session for vehicle service.");
16475
16850
  }
16851
+ const _mode = type !== "whitelist" /* WHITELIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
16476
16852
  try {
16477
16853
  session.startTransaction();
16478
16854
  const siteCameras = [];
@@ -16481,9 +16857,8 @@ function useVehicleService() {
16481
16857
  const limit = 20;
16482
16858
  do {
16483
16859
  const siteCameraReq = await _getAllSiteCameras({
16484
- site: value.site,
16860
+ site,
16485
16861
  type: "anpr",
16486
- direction: ["both", "entry"],
16487
16862
  page,
16488
16863
  limit
16489
16864
  });
@@ -16495,55 +16870,82 @@ function useVehicleService() {
16495
16870
  throw new import_node_server_utils83.BadRequestError("No site cameras found.");
16496
16871
  }
16497
16872
  for (const camera of siteCameras) {
16498
- const { host, username, password } = camera;
16873
+ const host = camera.host;
16874
+ const username = camera.username;
16875
+ const password = camera.password;
16499
16876
  const dahuaPayload = {
16500
16877
  host,
16501
16878
  username,
16502
16879
  password,
16503
- plateNumber: value.plateNumber,
16504
- mode: "TrafficRedList",
16505
- start: value.start ? new Date(value.start).toISOString() : "",
16506
- end: value.end ? new Date(value.end).toISOString() : "",
16507
- owner: value.name
16880
+ recno,
16881
+ mode: _mode
16508
16882
  };
16509
- const dahuaResponse = await _addPlateNumber(dahuaPayload);
16510
- if (dahuaResponse?.statusCode !== 200) {
16511
- throw new import_node_server_utils83.BadRequestError("Failed to add plate number to ANPR");
16883
+ const dahuaResponse = await _removePlateNumber(dahuaPayload);
16884
+ if (!bypass && dahuaResponse?.statusCode !== 200) {
16885
+ throw new import_node_server_utils83.BadRequestError(
16886
+ `Failed to remove plate number to ANPR from ${type}`
16887
+ );
16512
16888
  }
16513
- const responseData = dahuaResponse?.data.toString("utf-8");
16514
- value.recNo = responseData.split("=")[1]?.trim();
16515
- const formattedValue = {
16516
- ...value,
16517
- start: value.start ? new Date(value.start).toISOString() : "",
16518
- end: value.end ? new Date(value.end).toISOString() : ""
16519
- };
16520
- await _add(formattedValue, session);
16521
16889
  }
16890
+ await _deleteVehicle(_id, session);
16522
16891
  await session.commitTransaction();
16523
- return "Vehicle plate number added successfully.";
16892
+ return `Vehicle plate number deleted from ${type} record successfully.`;
16524
16893
  } catch (error) {
16525
- import_node_server_utils83.logger.error("Error in vehicle service add:", error);
16526
16894
  await session.abortTransaction();
16527
- throw error;
16895
+ if (error instanceof import_node_server_utils83.AppError)
16896
+ throw error;
16897
+ throw new import_node_server_utils83.InternalServerError("Failed to delete vehicle");
16528
16898
  } finally {
16529
16899
  session.endSession();
16530
16900
  }
16531
16901
  }
16532
- async function deleteVehicle(_id, recno, site, bypass = false) {
16902
+ async function approveVehicleById(id, orgId, siteId) {
16533
16903
  const session = import_node_server_utils83.useAtlas.getClient()?.startSession();
16534
16904
  if (!session) {
16535
16905
  throw new Error("Unable to start session for vehicle service.");
16536
16906
  }
16907
+ const org = await _getById(orgId);
16908
+ if (!org)
16909
+ throw new import_node_server_utils83.BadRequestError("Org not found");
16910
+ const allowedNatures2 = ["property_management_agency"];
16911
+ if (!allowedNatures2.includes(org.nature)) {
16912
+ throw new import_node_server_utils83.BadRequestError(
16913
+ "Only property management can approve vehicles."
16914
+ );
16915
+ }
16916
+ const vehicle = await _getVehicleById(id);
16917
+ if (!vehicle) {
16918
+ throw new import_node_server_utils83.BadRequestError("Vehicle not found");
16919
+ }
16920
+ const owner = vehicle.name.substring(0, 15);
16921
+ const hasStart = typeof vehicle.start === "string" ? vehicle.start.trim() !== "" : !!vehicle.start;
16922
+ const hasEnd = typeof vehicle.end === "string" ? vehicle.end.trim() !== "" : !!vehicle.end;
16923
+ let startDate = null;
16924
+ let endDate = null;
16925
+ if (!hasStart && !hasEnd) {
16926
+ startDate = /* @__PURE__ */ new Date();
16927
+ endDate = new Date(startDate);
16928
+ endDate.setFullYear(endDate.getFullYear() + 10);
16929
+ } else {
16930
+ startDate = hasStart ? new Date(vehicle.start) : null;
16931
+ endDate = hasEnd ? new Date(vehicle.end) : null;
16932
+ }
16933
+ const startIso = startDate ? startDate.toISOString() : "";
16934
+ const endIso = endDate ? endDate.toISOString() : "";
16935
+ const startDahua = startDate ? formatDahuaDate(startDate) : "";
16936
+ const endDahua = endDate ? formatDahuaDate(endDate) : "";
16537
16937
  try {
16538
16938
  session.startTransaction();
16539
16939
  const siteCameras = [];
16540
16940
  let page = 1;
16541
16941
  let pages = 1;
16542
16942
  const limit = 20;
16943
+ let value = {};
16543
16944
  do {
16544
16945
  const siteCameraReq = await _getAllSiteCameras({
16545
- site,
16946
+ site: siteId,
16546
16947
  type: "anpr",
16948
+ direction: ["both", "entry"],
16547
16949
  page,
16548
16950
  limit
16549
16951
  });
@@ -16555,24 +16957,51 @@ function useVehicleService() {
16555
16957
  throw new import_node_server_utils83.BadRequestError("No site cameras found.");
16556
16958
  }
16557
16959
  for (const camera of siteCameras) {
16558
- const host = camera.host;
16559
- const username = camera.username;
16560
- const password = camera.password;
16960
+ const { host, username, password } = camera;
16561
16961
  const dahuaPayload = {
16562
16962
  host,
16563
16963
  username,
16564
16964
  password,
16565
- recno,
16566
- mode: "TrafficRedList"
16965
+ plateNumber: vehicle.plateNumber,
16966
+ mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
16967
+ start: startDahua,
16968
+ end: endDahua,
16969
+ owner
16567
16970
  };
16568
- const dahuaResponse = await _removePlateNumber(dahuaPayload);
16569
- if (!bypass && dahuaResponse?.statusCode !== 200) {
16570
- throw new import_node_server_utils83.BadRequestError("Failed to remove plate number to ANPR");
16971
+ const dahuaResponse = await _addPlateNumber(dahuaPayload);
16972
+ if (dahuaResponse?.statusCode !== 200) {
16973
+ throw new import_node_server_utils83.BadRequestError("Failed to add plate number to ANPR");
16571
16974
  }
16975
+ const responseData = dahuaResponse?.data.toString("utf-8");
16976
+ value.recNo = responseData.split("=")[1]?.trim();
16572
16977
  }
16573
- await _deleteVehicle(_id, session);
16978
+ value.status = "active";
16979
+ const formattedValue = {
16980
+ ...value,
16981
+ start: startIso,
16982
+ end: endIso
16983
+ };
16984
+ await _updateVehicle(id, formattedValue, session);
16985
+ await session.commitTransaction();
16986
+ return "Vehicle plate number approved and added successfully.";
16987
+ } catch (error) {
16988
+ import_node_server_utils83.logger.error("Error in vehicle service add:", error);
16989
+ await session.abortTransaction();
16990
+ throw error;
16991
+ } finally {
16992
+ session.endSession();
16993
+ }
16994
+ }
16995
+ async function processDeletingExpiredVehicles() {
16996
+ const session = import_node_server_utils83.useAtlas.getClient()?.startSession();
16997
+ if (!session) {
16998
+ throw new Error("Unable to start session for vehicle service.");
16999
+ }
17000
+ try {
17001
+ session.startTransaction();
17002
+ await _deleteExpiredVehicles();
16574
17003
  await session.commitTransaction();
16575
- return "Vehicle plate number deleted successfully.";
17004
+ return `Expired Vehicle plate numbers deleted successfully.`;
16576
17005
  } catch (error) {
16577
17006
  await session.abortTransaction();
16578
17007
  if (error instanceof import_node_server_utils83.AppError)
@@ -16584,7 +17013,9 @@ function useVehicleService() {
16584
17013
  }
16585
17014
  return {
16586
17015
  add,
16587
- deleteVehicle
17016
+ deleteVehicle,
17017
+ approveVehicleById,
17018
+ processDeletingExpiredVehicles
16588
17019
  };
16589
17020
  }
16590
17021
 
@@ -16592,12 +17023,17 @@ function useVehicleService() {
16592
17023
  var import_node_server_utils84 = require("@7365admin1/node-server-utils");
16593
17024
  var import_joi46 = __toESM(require("joi"));
16594
17025
  function useVehicleController() {
16595
- const { add: _add, deleteVehicle: _deleteVehicle } = useVehicleService();
17026
+ const {
17027
+ add: _add,
17028
+ deleteVehicle: _deleteVehicle,
17029
+ approveVehicleById: _approveVehicleById
17030
+ } = useVehicleService();
16596
17031
  const {
16597
17032
  getSeasonPassTypes: _getSeasonPassTypes,
16598
17033
  getVehicles: _getVehicles,
16599
17034
  getVehicleById: _getVehicleById,
16600
- updateVehicle: _updateVehicle
17035
+ updateVehicle: _updateVehicle,
17036
+ getVehiclesByNRIC: _getVehiclesByNRIC
16601
17037
  } = useVehicleRepo();
16602
17038
  async function add(req, res, next) {
16603
17039
  const payload = req.body;
@@ -16628,8 +17064,9 @@ function useVehicleController() {
16628
17064
  limit: import_joi46.default.number().integer().min(1).max(100).allow("", null).default(10),
16629
17065
  sort: import_joi46.default.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
16630
17066
  order: import_joi46.default.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
16631
- type: import_joi46.default.string().optional().allow("", ...allowedTypes),
16632
- category: import_joi46.default.string().optional().allow("", ...allowedCategories)
17067
+ type: import_joi46.default.string().optional().valid(...Object.values(VehicleType)),
17068
+ category: import_joi46.default.string().optional().valid(...Object.values(VehicleCategory)),
17069
+ status: import_joi46.default.string().optional().valid(...Object.values(VehicleStatus)).allow("")
16633
17070
  });
16634
17071
  const query = { ...req.query };
16635
17072
  const { error } = validation.validate(query);
@@ -16643,6 +17080,7 @@ function useVehicleController() {
16643
17080
  const limit = parseInt(req.query.limit ?? "10");
16644
17081
  const type = req.query.type ?? "";
16645
17082
  const category = req.query.category ?? "";
17083
+ const status = req.query.status ?? "";
16646
17084
  const sortObj = {};
16647
17085
  const sortFields = String(req.query.sort).split(",");
16648
17086
  const sortOrders = String(req.query.order).split(",");
@@ -16659,7 +17097,8 @@ function useVehicleController() {
16659
17097
  limit,
16660
17098
  sort: sortObj,
16661
17099
  type,
16662
- category
17100
+ category,
17101
+ status
16663
17102
  });
16664
17103
  res.json(data);
16665
17104
  return;
@@ -16715,7 +17154,8 @@ function useVehicleController() {
16715
17154
  block: import_joi46.default.number().integer().optional().allow(0, null),
16716
17155
  level: import_joi46.default.string().optional().allow("", null),
16717
17156
  unit: import_joi46.default.string().optional().allow("", null),
16718
- plateNumber: import_joi46.default.string().optional().allow("", null)
17157
+ plateNumber: import_joi46.default.string().optional().allow("", null),
17158
+ nric: import_joi46.default.string().optional().allow("", null)
16719
17159
  });
16720
17160
  const _id = req.params.id;
16721
17161
  const payload = { ...req.body };
@@ -16736,23 +17176,23 @@ function useVehicleController() {
16736
17176
  }
16737
17177
  }
16738
17178
  async function deleteVehicle(req, res, next) {
16739
- const site = req.params.site ?? "";
16740
- const _id = req.params.id ?? "";
16741
- const recno = req.params.recno ?? "";
16742
- const bypass = req.query.bypass === "true";
17179
+ const _id = req.params.id;
16743
17180
  const deleteVehicleSchema = import_joi46.default.object({
16744
- _id: import_joi46.default.string().hex().required(),
17181
+ _id: import_joi46.default.string().hex().length(24).required(),
16745
17182
  recno: import_joi46.default.string().required(),
16746
- bypass: import_joi46.default.boolean().optional()
17183
+ site: import_joi46.default.string().hex().length(24).required(),
17184
+ type: import_joi46.default.string().valid("whitelist", "blocklist").required(),
17185
+ bypass: import_joi46.default.boolean().optional().default(true)
16747
17186
  });
16748
- const { error } = deleteVehicleSchema.validate({ _id, recno, bypass });
17187
+ const { error, value } = deleteVehicleSchema.validate({ _id, ...req.body });
16749
17188
  if (error) {
16750
17189
  import_node_server_utils84.logger.log({ level: "error", message: error.message });
16751
17190
  next(new import_node_server_utils84.BadRequestError(error.message));
16752
17191
  return;
16753
17192
  }
17193
+ const { recno, site, type, bypass } = value;
16754
17194
  try {
16755
- const data = await _deleteVehicle(_id, recno, site, bypass);
17195
+ const data = await _deleteVehicle(_id, recno, site, type, bypass);
16756
17196
  res.json({
16757
17197
  message: "Vehicle deleted successfully.",
16758
17198
  data
@@ -16763,13 +17203,83 @@ function useVehicleController() {
16763
17203
  return;
16764
17204
  }
16765
17205
  }
17206
+ async function approveVehicleById(req, res, next) {
17207
+ const validation = import_joi46.default.object({
17208
+ _id: import_joi46.default.string().hex().length(24).required(),
17209
+ org: import_joi46.default.string().hex().length(24).required(),
17210
+ site: import_joi46.default.string().hex().length(24).required()
17211
+ });
17212
+ const _id = req.params.id;
17213
+ const payload = { ...req.body };
17214
+ const { error, value } = validation.validate({ _id, ...payload });
17215
+ if (error) {
17216
+ import_node_server_utils84.logger.log({ level: "error", message: error.message });
17217
+ next(new import_node_server_utils84.BadRequestError(error.message));
17218
+ return;
17219
+ }
17220
+ try {
17221
+ await _approveVehicleById(value._id, value.org, value.site);
17222
+ res.json({ message: "Successfully approved and updated vehicle." });
17223
+ return;
17224
+ } catch (error2) {
17225
+ import_node_server_utils84.logger.log({ level: "error", message: error2.message });
17226
+ next(error2);
17227
+ return;
17228
+ }
17229
+ }
17230
+ async function getVehiclesByNRIC(req, res, next) {
17231
+ const allowedFields = ["start", "end"];
17232
+ const allowedOrder = ["asc", "desc"];
17233
+ const validation = import_joi46.default.object({
17234
+ search: import_joi46.default.string().optional().allow("", null),
17235
+ page: import_joi46.default.number().integer().min(1).allow("", null).default(1),
17236
+ limit: import_joi46.default.number().integer().min(1).max(100).allow("", null).default(10),
17237
+ sort: import_joi46.default.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
17238
+ order: import_joi46.default.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder)
17239
+ });
17240
+ const query = { ...req.query };
17241
+ const { error } = validation.validate(query);
17242
+ if (error) {
17243
+ import_node_server_utils84.logger.log({ level: "error", message: error.message });
17244
+ next(new import_node_server_utils84.BadRequestError(error.message));
17245
+ return;
17246
+ }
17247
+ const search = req.query.search ?? "";
17248
+ const page = parseInt(req.query.page ?? "1");
17249
+ const limit = parseInt(req.query.limit ?? "10");
17250
+ const sortObj = {};
17251
+ const sortFields = String(req.query.sort).split(",");
17252
+ const sortOrders = String(req.query.order).split(",");
17253
+ sortFields.forEach((field, index) => {
17254
+ if (allowedFields.includes(field)) {
17255
+ const order = sortOrders[index] === "asc" ? 1 : -1;
17256
+ sortObj[field] = order;
17257
+ }
17258
+ });
17259
+ try {
17260
+ const data = await _getVehiclesByNRIC({
17261
+ search,
17262
+ page,
17263
+ limit,
17264
+ sort: sortObj
17265
+ });
17266
+ res.json(data);
17267
+ return;
17268
+ } catch (error2) {
17269
+ import_node_server_utils84.logger.log({ level: "error", message: error2.message });
17270
+ next(error2);
17271
+ return;
17272
+ }
17273
+ }
16766
17274
  return {
16767
17275
  add,
16768
17276
  getVehicles,
16769
17277
  getSeasonPassTypes,
16770
17278
  getVehicleById,
16771
17279
  updateVehicle,
16772
- deleteVehicle
17280
+ deleteVehicle,
17281
+ approveVehicleById,
17282
+ getVehiclesByNRIC
16773
17283
  };
16774
17284
  }
16775
17285
 
@@ -16868,7 +17378,7 @@ function useSiteCameraController() {
16868
17378
  }
16869
17379
  async function getAll(req, res, next) {
16870
17380
  const query = req.query;
16871
- const allowedTypes2 = ["ip", "exit", "entry", "both"];
17381
+ const allowedTypes = ["ip", "exit", "entry", "both"];
16872
17382
  const validation = import_joi47.default.object({
16873
17383
  type: import_joi47.default.string().optional(),
16874
17384
  site: import_joi47.default.string().optional(),
@@ -24228,20 +24738,19 @@ function useDocumentManagementController() {
24228
24738
  // src/models/bulletin-board.model.ts
24229
24739
  var import_joi75 = __toESM(require("joi"));
24230
24740
  var import_mongodb71 = require("mongodb");
24741
+ var BULLETIN_RECIPIENTS = [
24742
+ "resident",
24743
+ "security_agency",
24744
+ "cleaning_services",
24745
+ "mechanical_electrical",
24746
+ "property_management_agency"
24747
+ ];
24748
+ var STATUS_VALUES = ["active", "expired", "deleted"];
24231
24749
  var schemaBulletinBoard = import_joi75.default.object({
24232
24750
  _id: import_joi75.default.string().hex().optional().allow("", null),
24233
24751
  site: import_joi75.default.string().hex().optional().allow("", null),
24234
24752
  orgId: import_joi75.default.string().hex().optional().allow("", null),
24235
- recipients: import_joi75.default.array().items(
24236
- import_joi75.default.string().valid(
24237
- "admin",
24238
- "organization",
24239
- "site",
24240
- "service-provider",
24241
- "service-provider-member",
24242
- "resident"
24243
- )
24244
- ).optional(),
24753
+ recipients: import_joi75.default.array().items(import_joi75.default.string().valid(...BULLETIN_RECIPIENTS)).unique().optional(),
24245
24754
  title: import_joi75.default.string().optional().allow("", null),
24246
24755
  content: import_joi75.default.string().optional().allow("", null),
24247
24756
  file: import_joi75.default.array().items(
@@ -24254,23 +24763,14 @@ var schemaBulletinBoard = import_joi75.default.object({
24254
24763
  noExpiration: import_joi75.default.boolean().optional(),
24255
24764
  startDate: import_joi75.default.date().optional().allow("", null),
24256
24765
  endDate: import_joi75.default.date().optional().allow("", null),
24257
- status: import_joi75.default.string().optional().allow("", null),
24766
+ status: import_joi75.default.string().valid(...STATUS_VALUES).optional(),
24258
24767
  createdAt: import_joi75.default.date().optional().allow(null),
24259
24768
  updatedAt: import_joi75.default.date().optional().allow(null),
24260
24769
  deletedAt: import_joi75.default.date().optional().allow(null)
24261
24770
  });
24262
24771
  var schemaUpdateBulletinBoard = import_joi75.default.object({
24263
24772
  _id: import_joi75.default.string().hex().required(),
24264
- recipients: import_joi75.default.array().items(
24265
- import_joi75.default.string().valid(
24266
- "admin",
24267
- "organization",
24268
- "site",
24269
- "service-provider",
24270
- "service-provider-member",
24271
- "resident"
24272
- )
24273
- ).optional(),
24773
+ recipients: import_joi75.default.array().items(import_joi75.default.string().valid(...BULLETIN_RECIPIENTS)).unique().optional(),
24274
24774
  title: import_joi75.default.string().optional().allow("", null),
24275
24775
  content: import_joi75.default.string().optional().allow("", null),
24276
24776
  file: import_joi75.default.array().items(
@@ -24283,7 +24783,7 @@ var schemaUpdateBulletinBoard = import_joi75.default.object({
24283
24783
  noExpiration: import_joi75.default.boolean().optional(),
24284
24784
  startDate: import_joi75.default.date().optional().allow("", null),
24285
24785
  endDate: import_joi75.default.date().optional().allow("", null),
24286
- status: import_joi75.default.string().optional().allow("", null)
24786
+ status: import_joi75.default.string().valid(...STATUS_VALUES).optional()
24287
24787
  });
24288
24788
  function MBulletinBoard(value) {
24289
24789
  const { error } = schemaBulletinBoard.validate(value);
@@ -24374,7 +24874,8 @@ function useBulletinBoardRepo() {
24374
24874
  limit = 10,
24375
24875
  sort = {},
24376
24876
  site = "",
24377
- status = "active"
24877
+ status = "active",
24878
+ recipients = []
24378
24879
  }, session) {
24379
24880
  page = page > 0 ? page - 1 : 0;
24380
24881
  try {
@@ -24384,7 +24885,9 @@ function useBulletinBoardRepo() {
24384
24885
  }
24385
24886
  const query = {
24386
24887
  site,
24387
- status
24888
+ status,
24889
+ ...search && { $text: { $search: search } },
24890
+ ...recipients?.length && { recipients: { $in: recipients } }
24388
24891
  };
24389
24892
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
24390
24893
  const cacheOptions = {
@@ -24392,12 +24895,10 @@ function useBulletinBoardRepo() {
24392
24895
  status,
24393
24896
  sort: JSON.stringify(sort),
24394
24897
  page,
24395
- limit
24898
+ limit,
24899
+ ...search && { search },
24900
+ ...recipients?.length && { recipients: recipients.sort().join(",") }
24396
24901
  };
24397
- if (search) {
24398
- query.$text = { $search: search };
24399
- cacheOptions.search = search;
24400
- }
24401
24902
  const cacheKey = (0, import_node_server_utils131.makeCacheKey)(namespace_collection, cacheOptions);
24402
24903
  const cachedData = await getCache(cacheKey);
24403
24904
  if (cachedData) {
@@ -24489,7 +24990,7 @@ function useBulletinBoardRepo() {
24489
24990
  throw error;
24490
24991
  }
24491
24992
  }
24492
- async function deleteBulletinBoardById(_id) {
24993
+ async function deleteBulletinBoardById(_id, session) {
24493
24994
  try {
24494
24995
  _id = new import_mongodb72.ObjectId(_id);
24495
24996
  } catch (error) {
@@ -24501,7 +25002,11 @@ function useBulletinBoardRepo() {
24501
25002
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
24502
25003
  deletedAt: (/* @__PURE__ */ new Date()).toISOString()
24503
25004
  };
24504
- const res = await collection.updateOne({ _id }, { $set: updateValue });
25005
+ const res = await collection.updateOne(
25006
+ { _id },
25007
+ { $set: updateValue },
25008
+ { session }
25009
+ );
24505
25010
  if (res.modifiedCount === 0) {
24506
25011
  throw new import_node_server_utils131.InternalServerError("Unable to delete bulletin board.");
24507
25012
  }
@@ -24567,8 +25072,11 @@ function useBulletinBoardService() {
24567
25072
  const {
24568
25073
  add: _add,
24569
25074
  updateBulletinBoardById: _updateBulletinBoardById,
24570
- processExpiredBulletinBoards: _processExpiredBulletinBoards
25075
+ processExpiredBulletinBoards: _processExpiredBulletinBoards,
25076
+ deleteBulletinBoardById: _deleteBulletinBoardById,
25077
+ getBulletinBoardById: _getBulletinBoardById
24571
25078
  } = useBulletinBoardRepo();
25079
+ const { deleteFileById: _deleteFileById } = useFileRepo();
24572
25080
  async function add(value) {
24573
25081
  const session = import_node_server_utils132.useAtlas.getClient()?.startSession();
24574
25082
  session?.startTransaction();
@@ -24611,10 +25119,31 @@ function useBulletinBoardService() {
24611
25119
  session?.endSession();
24612
25120
  }
24613
25121
  }
25122
+ async function deleteBulletinBoardById(id) {
25123
+ const session = import_node_server_utils132.useAtlas.getClient()?.startSession();
25124
+ session?.startTransaction();
25125
+ try {
25126
+ const existingBulletinBoard = await _getBulletinBoardById(id);
25127
+ if (Array.isArray(existingBulletinBoard.file)) {
25128
+ for (const file of existingBulletinBoard.file) {
25129
+ await _deleteFileById(file, session);
25130
+ }
25131
+ }
25132
+ await _deleteBulletinBoardById(id, session);
25133
+ await session?.commitTransaction();
25134
+ return "Successfully deleted bulletin board.";
25135
+ } catch (error) {
25136
+ await session?.abortTransaction();
25137
+ throw error;
25138
+ } finally {
25139
+ session?.endSession();
25140
+ }
25141
+ }
24614
25142
  return {
24615
25143
  add,
24616
25144
  updateBulletinBoardById,
24617
- processExpiredBulletinBoards
25145
+ processExpiredBulletinBoards,
25146
+ deleteBulletinBoardById
24618
25147
  };
24619
25148
  }
24620
25149
 
@@ -24622,12 +25151,12 @@ function useBulletinBoardService() {
24622
25151
  var import_node_server_utils133 = require("@7365admin1/node-server-utils");
24623
25152
  var import_joi76 = __toESM(require("joi"));
24624
25153
  function useBulletinBoardController() {
24625
- const { add: _add, updateBulletinBoardById: _updateBulletinBoardById } = useBulletinBoardService();
24626
25154
  const {
24627
- getAll: _getAll,
24628
- getBulletinBoardById: _getBulletinBoardById,
25155
+ add: _add,
25156
+ updateBulletinBoardById: _updateBulletinBoardById,
24629
25157
  deleteBulletinBoardById: _deleteBulletinBoardById
24630
- } = useBulletinBoardRepo();
25158
+ } = useBulletinBoardService();
25159
+ const { getAll: _getAll, getBulletinBoardById: _getBulletinBoardById } = useBulletinBoardRepo();
24631
25160
  async function add(req, res, next) {
24632
25161
  const payload = { ...req.body };
24633
25162
  const { error } = schemaBulletinBoard.validate(payload, {
@@ -24659,12 +25188,23 @@ function useBulletinBoardController() {
24659
25188
  sort: import_joi76.default.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
24660
25189
  order: import_joi76.default.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
24661
25190
  site: import_joi76.default.string().hex().required(),
24662
- status: import_joi76.default.string().optional().allow(null, "")
25191
+ status: import_joi76.default.string().optional().allow(null, ""),
25192
+ recipients: import_joi76.default.string().optional().allow("", null).custom((value, helpers) => {
25193
+ const parsed = value.split(",").map((v) => v.trim()).filter(Boolean);
25194
+ for (const r of parsed) {
25195
+ if (!BULLETIN_RECIPIENTS.includes(r)) {
25196
+ return helpers.error("any.only");
25197
+ }
25198
+ }
25199
+ return value;
25200
+ }).messages({
25201
+ "any.only": `Recipients must be one of: ${BULLETIN_RECIPIENTS.join(
25202
+ ", "
25203
+ )}`
25204
+ })
24663
25205
  });
24664
25206
  const query = { ...req.query };
24665
- const { error } = validation.validate(query, {
24666
- abortEarly: false
24667
- });
25207
+ const { error } = validation.validate(query, { abortEarly: false });
24668
25208
  if (error) {
24669
25209
  const messages = error.details.map((d) => d.message).join(", ");
24670
25210
  import_node_server_utils133.logger.log({ level: "error", message: messages });
@@ -24676,9 +25216,15 @@ function useBulletinBoardController() {
24676
25216
  const limit = parseInt(req.query.limit ?? "10");
24677
25217
  const site = req.query.site ?? "";
24678
25218
  const status = req.query.status ?? "active";
25219
+ const recipientsRaw = req.query.recipients ?? "";
25220
+ const recipients = recipientsRaw && typeof recipientsRaw === "string" ? Array.from(
25221
+ new Set(
25222
+ recipientsRaw.split(",").map((r) => r.trim()).filter(Boolean)
25223
+ )
25224
+ ) : [];
24679
25225
  const sortObj = {};
24680
- const sortFields = String(req.query.sort).split(",");
24681
- const sortOrders = String(req.query.order).split(",");
25226
+ const sortFields = String(req.query.sort ?? "").split(",").filter(Boolean);
25227
+ const sortOrders = String(req.query.order ?? "").split(",").filter(Boolean);
24682
25228
  sortFields.forEach((field, index) => {
24683
25229
  if (allowedFields.includes(field)) {
24684
25230
  const order = sortOrders[index] === "asc" ? 1 : -1;
@@ -24692,7 +25238,8 @@ function useBulletinBoardController() {
24692
25238
  limit,
24693
25239
  sort: sortObj,
24694
25240
  site,
24695
- status
25241
+ status,
25242
+ recipients
24696
25243
  });
24697
25244
  res.status(200).json(data);
24698
25245
  return;
@@ -24775,6 +25322,16 @@ function useBulletinBoardController() {
24775
25322
  var import_node_server_utils134 = require("@7365admin1/node-server-utils");
24776
25323
  var import_mongodb73 = require("mongodb");
24777
25324
  var import_joi77 = __toESM(require("joi"));
25325
+
25326
+ // src/types/enums/billing-frequency.enum.ts
25327
+ var EBillingFrequency = /* @__PURE__ */ ((EBillingFrequency2) => {
25328
+ EBillingFrequency2["MONTHLY"] = "monthly";
25329
+ EBillingFrequency2["QAURTERLY"] = "quarterly";
25330
+ EBillingFrequency2["ANNUALLY"] = "annually";
25331
+ return EBillingFrequency2;
25332
+ })(EBillingFrequency || {});
25333
+
25334
+ // src/models/site-billing-item.model.ts
24778
25335
  var schemaUnits = import_joi77.default.object({
24779
25336
  _id: import_joi77.default.string().hex().optional(),
24780
25337
  name: import_joi77.default.string().optional()
@@ -24785,7 +25342,7 @@ var schemaBillingItem = import_joi77.default.object({
24785
25342
  org: import_joi77.default.string().hex().required(),
24786
25343
  name: import_joi77.default.string().required(),
24787
25344
  amount: import_joi77.default.number().required(),
24788
- frequency: import_joi77.default.string().valid("month", "quarter", "annual").required(),
25345
+ frequency: import_joi77.default.string().valid(...Object.values(EBillingFrequency)).required(),
24789
25346
  billingType: import_joi77.default.string().valid("recurring", "non-recurring").required(),
24790
25347
  dueInDays: import_joi77.default.number().optional().allow(null, ""),
24791
25348
  date: import_joi77.default.string().required(),
@@ -24810,7 +25367,7 @@ var schemaUpdateSiteBillingItem = import_joi77.default.object({
24810
25367
  org: import_joi77.default.string().hex().optional().allow(null, ""),
24811
25368
  name: import_joi77.default.string().optional().allow(null, ""),
24812
25369
  amount: import_joi77.default.number().optional().allow(null, ""),
24813
- frequency: import_joi77.default.string().valid("month", "quarter", "annual").optional().allow(null, ""),
25370
+ frequency: import_joi77.default.string().valid(...Object.values(EBillingFrequency)).optional().allow(null, ""),
24814
25371
  billingType: import_joi77.default.string().valid("recurring", "non-recurring").optional().allow(null, ""),
24815
25372
  dueInDays: import_joi77.default.number().optional().allow(null, ""),
24816
25373
  date: import_joi77.default.string().optional().allow(null, ""),
@@ -24955,7 +25512,16 @@ function useSiteBillingItemRepo() {
24955
25512
  ...search && {
24956
25513
  $or: [
24957
25514
  { name: { $regex: search, $options: "i" } },
24958
- { frequency: { $regex: search, $options: "i" } }
25515
+ { frequency: { $regex: search, $options: "i" } },
25516
+ {
25517
+ $expr: {
25518
+ $regexMatch: {
25519
+ input: { $toString: "$totalAmount" },
25520
+ regex: search,
25521
+ options: "i"
25522
+ }
25523
+ }
25524
+ }
24959
25525
  ]
24960
25526
  },
24961
25527
  ...import_mongodb74.ObjectId.isValid(site) && { site: new import_mongodb74.ObjectId(site) }
@@ -26105,7 +26671,8 @@ function useEventManagementRepo() {
26105
26671
  sort = {},
26106
26672
  site = "",
26107
26673
  status = "",
26108
- type = ""
26674
+ type = "",
26675
+ date = ""
26109
26676
  }, session) {
26110
26677
  page = page > 0 ? page - 1 : 0;
26111
26678
  try {
@@ -26116,7 +26683,13 @@ function useEventManagementRepo() {
26116
26683
  const baseQuery = {
26117
26684
  site,
26118
26685
  status: status ? status : { $ne: "deleted" },
26119
- ...type && { type }
26686
+ ...type && { type },
26687
+ ...date && {
26688
+ dateTime: {
26689
+ $gte: `${date}T00:00:00.000Z`,
26690
+ $lt: `${date}T23:59:59.999Z`
26691
+ }
26692
+ }
26120
26693
  };
26121
26694
  let query = { ...baseQuery };
26122
26695
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
@@ -26414,7 +26987,8 @@ function useEventManagementController() {
26414
26987
  order: import_joi82.default.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
26415
26988
  site: import_joi82.default.string().hex().required(),
26416
26989
  status: import_joi82.default.string().optional(),
26417
- type: import_joi82.default.string().optional().valid("TASK", "EVENT").allow(null, "")
26990
+ type: import_joi82.default.string().optional().valid("TASK", "EVENT").allow(null, ""),
26991
+ date: import_joi82.default.string().optional().allow(null, "")
26418
26992
  });
26419
26993
  const query = { ...req.query };
26420
26994
  const { error } = validation.validate(query, {
@@ -26432,6 +27006,7 @@ function useEventManagementController() {
26432
27006
  const site = req.query.site ?? "";
26433
27007
  const status = req.query.status ?? "";
26434
27008
  const type = req.query.type ?? "";
27009
+ const date = req.query.date ?? "";
26435
27010
  const sortObj = {};
26436
27011
  const sortFields = String(req.query.sort).split(",");
26437
27012
  const sortOrders = String(req.query.order).split(",");
@@ -26449,7 +27024,8 @@ function useEventManagementController() {
26449
27024
  sort: sortObj,
26450
27025
  site,
26451
27026
  status,
26452
- type
27027
+ type,
27028
+ date
26453
27029
  });
26454
27030
  res.status(200).json(data);
26455
27031
  return;
@@ -26766,6 +27342,7 @@ function useSiteUnitBillingRepo() {
26766
27342
  }
26767
27343
  };
26768
27344
  }
27345
+ const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
26769
27346
  const query = {
26770
27347
  paymentStatus,
26771
27348
  status,
@@ -26773,7 +27350,16 @@ function useSiteUnitBillingRepo() {
26773
27350
  $or: [
26774
27351
  { unitOwner: { $regex: search, $options: "i" } },
26775
27352
  { billName: { $regex: search, $options: "i" } },
26776
- { unit: { $regex: search, $options: "i" } }
27353
+ { unit: { $regex: unitSearchRegex, $options: "i" } },
27354
+ {
27355
+ $expr: {
27356
+ $regexMatch: {
27357
+ input: { $toString: "$amountPaid" },
27358
+ regex: search,
27359
+ options: "i"
27360
+ }
27361
+ }
27362
+ }
26777
27363
  ]
26778
27364
  },
26779
27365
  ...import_mongodb80.ObjectId.isValid(site) && { site: new import_mongodb80.ObjectId(site) },
@@ -27196,14 +27782,14 @@ function useSiteUnitBillingService() {
27196
27782
  function isBillingChecker(billing_item, todayMonth, todayDate) {
27197
27783
  const billingMonth = Number(billing_item.month);
27198
27784
  const billingDay = Number(billing_item.date);
27199
- if (billing_item.frequency === "month") {
27785
+ if (billing_item.frequency === "monthly" /* MONTHLY */) {
27200
27786
  return todayDate === billingDay;
27201
27787
  }
27202
- if (billing_item.frequency === "quarter") {
27788
+ if (billing_item.frequency === "quarterly" /* QAURTERLY */) {
27203
27789
  const monthDiff = todayMonth - billingMonth;
27204
27790
  return monthDiff % 3 === 0 && todayDate === billingDay;
27205
27791
  }
27206
- if (billing_item.frequency === "annual") {
27792
+ if (billing_item.frequency === "annually" /* ANNUALLY */) {
27207
27793
  return todayMonth === billingMonth && todayDate === billingDay;
27208
27794
  }
27209
27795
  return false;
@@ -27995,7 +28581,7 @@ function UseAccessManagementRepo() {
27995
28581
  {
27996
28582
  $match: {
27997
28583
  ...defaultQuery,
27998
- status: { $ne: "deleted" }
28584
+ status: { $eq: "active" }
27999
28585
  }
28000
28586
  },
28001
28587
  // ✅ Only project needed fields before heavy lookups
@@ -28115,7 +28701,7 @@ function UseAccessManagementRepo() {
28115
28701
  userType
28116
28702
  }
28117
28703
  },
28118
- { $project: { _id: 1, userId: 1, type: 1, cardNo: 1, isActivated: 1 } }
28704
+ { $project: { _id: 1, userId: 1, type: 1, cardNo: 1, isActivated: 1, replacementStatus: 1 } }
28119
28705
  ],
28120
28706
  as: "accessCards"
28121
28707
  }
@@ -28134,7 +28720,21 @@ function UseAccessManagementRepo() {
28134
28720
  $filter: {
28135
28721
  input: "$accessCards",
28136
28722
  as: "card",
28137
- cond: { $ne: ["$$card.userId", null] }
28723
+ cond: { $and: [{ $ne: ["$$card.userId", null] }, { $eq: ["$$card.isActivated", true] }] }
28724
+ }
28725
+ },
28726
+ f_replaced: {
28727
+ $filter: {
28728
+ input: "$accessCards",
28729
+ as: "card",
28730
+ cond: { $and: [{ $eq: ["$$card.isActivated", false] }, { $ne: ["$$card.replacementStatus", null] }] }
28731
+ }
28732
+ },
28733
+ f_deleted: {
28734
+ $filter: {
28735
+ input: "$accessCards",
28736
+ as: "card",
28737
+ cond: { $and: [{ $eq: ["$$card.isActivated", false] }, { $eq: ["$$card.replacementStatus", null] }] }
28138
28738
  }
28139
28739
  }
28140
28740
  }
@@ -28166,6 +28766,14 @@ function UseAccessManagementRepo() {
28166
28766
  non_physical: { $size: { $filter: { input: { $ifNull: ["$f_Assigned", []] }, as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } } }
28167
28767
  }
28168
28768
  },
28769
+ replaced: {
28770
+ physical: { $filter: { input: "$f_replaced", as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } },
28771
+ non_physical: { $filter: { input: "$f_replaced", as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } }
28772
+ },
28773
+ deleted: {
28774
+ physical: { $filter: { input: "$f_deleted", as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } },
28775
+ non_physical: { $filter: { input: "$f_deleted", as: "c", cond: { $eq: ["$$c.type", "QRCODE" /* QR */] } } }
28776
+ },
28169
28777
  totalCardCount: {
28170
28778
  $add: [
28171
28779
  { $size: { $filter: { input: { $ifNull: ["$f_Available", []] }, as: "c", cond: { $eq: ["$$c.type", "NFC" /* NFC */] } } } },
@@ -28622,16 +29230,26 @@ function UseAccessManagementRepo() {
28622
29230
  async function cardReplacementRepo(params) {
28623
29231
  const session = import_node_server_utils149.useAtlas.getClient()?.startSession();
28624
29232
  try {
28625
- const { cardId, remarks } = params;
29233
+ const { cardId, remarks, issuedCardId, unitId, userId } = params;
28626
29234
  const id = new import_mongodb83.ObjectId(cardId);
29235
+ const newCardId = new import_mongodb83.ObjectId(issuedCardId);
29236
+ const unit = new import_mongodb83.ObjectId(unitId);
29237
+ const user = new import_mongodb83.ObjectId(userId);
28627
29238
  session?.startTransaction();
28628
- const card = await collection().findOneAndUpdate(
28629
- { _id: id },
28630
- { $set: { remarks, replacementStatus: "Pending", requestDate: /* @__PURE__ */ new Date(), isActivated: false } },
28631
- { returnDocument: "after", session }
28632
- );
29239
+ const sessionResult = await Promise.all([
29240
+ await collection().findOneAndUpdate(
29241
+ { _id: id },
29242
+ { $set: { remarks, replacementStatus: "Complete", requestDate: /* @__PURE__ */ new Date(), isActivated: false } },
29243
+ { returnDocument: "after", session }
29244
+ ),
29245
+ await collection().findOneAndUpdate(
29246
+ { _id: newCardId },
29247
+ { $set: { updatedAt: /* @__PURE__ */ new Date(), assignedUnit: unit, userId: user } },
29248
+ { returnDocument: "after", session }
29249
+ )
29250
+ ]);
28633
29251
  await session?.commitTransaction();
28634
- return card;
29252
+ return sessionResult;
28635
29253
  } catch (error) {
28636
29254
  await session?.abortTransaction();
28637
29255
  throw new Error(error.message);
@@ -28880,7 +29498,8 @@ function UseAccessManagementRepo() {
28880
29498
  rawItems.map(async (item) => {
28881
29499
  const date = new Date(item["startDate (format MM/DD/YYYY)"]);
28882
29500
  const endDate = new Date(date.setFullYear(date.getFullYear() + 10));
28883
- const cardNumber = item["cardNo (number 0-65535 ex. 301)"].toString().padStart(10, "0");
29501
+ const cardNumber = String(Number(item["cardNo (number 0-65535 ex. 301)"] || 0)).padStart(6, "0");
29502
+ const facilityCode = String(Number(item["facilityCode (number 0-255 ex. 11)"] || 0)).padStart(4, "0");
28884
29503
  const pin = item["pin (number 6 digits only)"] ? item["pin (number 6 digits only)"].toString().padStart(6, "0") : "123456";
28885
29504
  const match = item["accessLevel (number ex. 1)"];
28886
29505
  const accessLevel = match ? match : null;
@@ -28894,9 +29513,9 @@ function UseAccessManagementRepo() {
28894
29513
  accessLevel,
28895
29514
  accessGroup,
28896
29515
  accessType: "Normal" /* NORMAL */,
28897
- cardNo: cardNumber,
29516
+ cardNo: `${facilityCode}${cardNumber}`,
28898
29517
  pin,
28899
- qrData: await createQrData({ cardNumber }),
29518
+ qrData: await createQrData({ cardNumber: `${facilityCode}${cardNumber}` }),
28900
29519
  startDate: new Date(item["startDate (format MM/DD/YYYY)"]),
28901
29520
  endDate: new Date(item["endDate (format MM/DD/YYYY)"] || endDate),
28902
29521
  isActivated: true,
@@ -28970,6 +29589,66 @@ function UseAccessManagementRepo() {
28970
29589
  session?.endSession();
28971
29590
  }
28972
29591
  }
29592
+ async function deleteCardRepo(params) {
29593
+ try {
29594
+ const { cardId, remarks } = params;
29595
+ const id = new import_mongodb83.ObjectId(cardId);
29596
+ const result = await collection().findOneAndUpdate({ _id: id }, { $set: { isActivated: false, updatedAt: /* @__PURE__ */ new Date(), remarks, requestDate: /* @__PURE__ */ new Date() } }, { returnDocument: "after" });
29597
+ return result;
29598
+ } catch (error) {
29599
+ throw new Error(error.message);
29600
+ }
29601
+ }
29602
+ async function getCardDetailsRepo(params) {
29603
+ try {
29604
+ const { siteId, cardId } = params;
29605
+ const convertedSiteId = new import_mongodb83.ObjectId(siteId);
29606
+ const convertedCardId = new import_mongodb83.ObjectId(cardId);
29607
+ const card = await collection().findOne(
29608
+ {
29609
+ _id: convertedCardId,
29610
+ site: convertedSiteId,
29611
+ type: "NFC" /* NFC */,
29612
+ userType: "Visitor/Resident" /* DEFAULT */
29613
+ },
29614
+ {
29615
+ projection: {
29616
+ userId: 1,
29617
+ site: 1,
29618
+ type: 1,
29619
+ userType: 1,
29620
+ cardNo: 1,
29621
+ isActivated: 1,
29622
+ replacementStatus: 1,
29623
+ vmsRemarks: 1,
29624
+ remarks: 1,
29625
+ requestDate: 1,
29626
+ createdAt: 1,
29627
+ updatedAt: 1
29628
+ }
29629
+ }
29630
+ );
29631
+ if (!card)
29632
+ return null;
29633
+ const site = await collectionName("sites").findOne(
29634
+ { _id: card.site },
29635
+ { projection: { name: 1, status: 1 } }
29636
+ );
29637
+ const user = card.userId ? await collectionName("users").findOne(
29638
+ { _id: card.userId },
29639
+ { projection: { name: 1, email: 1 } }
29640
+ ) : null;
29641
+ const status = card.userId === null && card.isActivated === true ? "available" : card.userId !== null && card.isActivated === true ? "assigned" : card.isActivated === false && card.replacementStatus !== null ? "replaced" : "deleted";
29642
+ return {
29643
+ ...card,
29644
+ status,
29645
+ site,
29646
+ user
29647
+ };
29648
+ } catch (error) {
29649
+ throw new Error(error.message);
29650
+ }
29651
+ }
28973
29652
  return {
28974
29653
  createIndexes,
28975
29654
  createIndexForEntrypass,
@@ -28990,7 +29669,9 @@ function UseAccessManagementRepo() {
28990
29669
  getCardReplacementRepo,
28991
29670
  getAccessManagementSettingsRepo,
28992
29671
  bulkPhysicalAccessCardRepo,
28993
- assignAccessCardToUnitRepo
29672
+ assignAccessCardToUnitRepo,
29673
+ deleteCardRepo,
29674
+ getCardDetailsRepo
28994
29675
  };
28995
29676
  }
28996
29677
 
@@ -29022,7 +29703,9 @@ function useAccessManagementSvc() {
29022
29703
  getCardReplacementRepo,
29023
29704
  getAccessManagementSettingsRepo,
29024
29705
  bulkPhysicalAccessCardRepo,
29025
- assignAccessCardToUnitRepo
29706
+ assignAccessCardToUnitRepo,
29707
+ deleteCardRepo,
29708
+ getCardDetailsRepo
29026
29709
  } = UseAccessManagementRepo();
29027
29710
  const addPhysicalCardSvc = async (payload) => {
29028
29711
  try {
@@ -29270,6 +29953,22 @@ function useAccessManagementSvc() {
29270
29953
  throw new Error(err.message);
29271
29954
  }
29272
29955
  };
29956
+ const deleteCardSvc = async (params) => {
29957
+ try {
29958
+ const response = await deleteCardRepo({ ...params });
29959
+ return response;
29960
+ } catch (err) {
29961
+ throw new Error(err.message);
29962
+ }
29963
+ };
29964
+ const getCardDetailsSvc = async (params) => {
29965
+ try {
29966
+ const response = await getCardDetailsRepo({ ...params });
29967
+ return response;
29968
+ } catch (err) {
29969
+ throw new Error(err.message);
29970
+ }
29971
+ };
29273
29972
  return {
29274
29973
  addPhysicalCardSvc,
29275
29974
  addNonPhysicalCardSvc,
@@ -29294,7 +29993,9 @@ function useAccessManagementSvc() {
29294
29993
  getAccessManagementSettingsSvc,
29295
29994
  convertBufferFile,
29296
29995
  bulkPhysicalAccessCardSvc,
29297
- assignAccessCardToUnitSvc
29996
+ assignAccessCardToUnitSvc,
29997
+ deleteCardSvc,
29998
+ getCardDetailsSvc
29298
29999
  };
29299
30000
  }
29300
30001
 
@@ -29326,7 +30027,9 @@ function useAccessManagementController() {
29326
30027
  getAccessManagementSettingsSvc,
29327
30028
  convertBufferFile,
29328
30029
  bulkPhysicalAccessCardSvc,
29329
- assignAccessCardToUnitSvc
30030
+ assignAccessCardToUnitSvc,
30031
+ deleteCardSvc,
30032
+ getCardDetailsSvc
29330
30033
  } = useAccessManagementSvc();
29331
30034
  const addPhysicalCard = async (req, res) => {
29332
30035
  try {
@@ -29555,11 +30258,24 @@ function useAccessManagementController() {
29555
30258
  userType: import_joi85.default.string().optional().allow("", null),
29556
30259
  type: import_joi85.default.string().optional().allow("", null)
29557
30260
  });
30261
+ const user = req.cookies?.sid;
29558
30262
  const { error } = schema2.validate({ site, userType, type });
29559
30263
  if (error) {
29560
30264
  return res.status(400).json({ message: error.message });
29561
30265
  }
30266
+ const key = `${namespace2}:${user}:available-access-cards`;
30267
+ const listKey = `${namespace2}:${user}:list`;
30268
+ const { redis } = (0, import_node_server_utils151.useCache)(key);
30269
+ const cachedData = await getCache({ key, redis });
30270
+ if (cachedData) {
30271
+ console.log("\u26A1 Cache hit:", key);
30272
+ redis.expire(key, 60).catch(console.error);
30273
+ redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
30274
+ return res.status(200).json({ message: "Success", data: cachedData });
30275
+ }
29562
30276
  const result = await availableAccessCardsSvc({ site, userType, type });
30277
+ await setCache({ key, data: result, ttlSeconds: 60, redis });
30278
+ redis.lrem(listKey, 0, key).then(() => redis.lpush(listKey, key)).then(() => redis.ltrim(listKey, 0, 9)).catch(console.error);
29563
30279
  return res.status(200).json({ message: "Success", data: result });
29564
30280
  } catch (error) {
29565
30281
  return res.status(400).json({
@@ -29741,16 +30457,19 @@ function useAccessManagementController() {
29741
30457
  };
29742
30458
  const cardReplacement = async (req, res) => {
29743
30459
  try {
29744
- const { cardId, remarks } = req.body;
30460
+ const { cardId, remarks, unitId, issuedCardId, userId } = req.body;
29745
30461
  const schema2 = import_joi85.default.object({
29746
30462
  cardId: import_joi85.default.string().required(),
29747
- remarks: import_joi85.default.string().optional().allow("", null)
30463
+ remarks: import_joi85.default.string().optional().allow("", null),
30464
+ unitId: import_joi85.default.string().hex().required(),
30465
+ issuedCardId: import_joi85.default.string().required(),
30466
+ userId: import_joi85.default.string().hex().required()
29748
30467
  });
29749
- const { error } = schema2.validate({ cardId, remarks });
30468
+ const { error } = schema2.validate({ cardId, remarks, unitId, issuedCardId, userId });
29750
30469
  if (error) {
29751
30470
  return res.status(400).json({ message: error.message });
29752
30471
  }
29753
- const result = await cardReplacementSvc({ cardId, remarks });
30472
+ const result = await cardReplacementSvc({ cardId, remarks, unitId, issuedCardId, userId });
29754
30473
  return res.status(200).json({ message: "Success", data: result });
29755
30474
  } catch (error) {
29756
30475
  return res.status(500).json({
@@ -29881,6 +30600,46 @@ function useAccessManagementController() {
29881
30600
  });
29882
30601
  }
29883
30602
  };
30603
+ const deleteCard = async (req, res) => {
30604
+ try {
30605
+ const { cardId, remarks } = req.body;
30606
+ const schema2 = import_joi85.default.object({
30607
+ cardId: import_joi85.default.string().hex().required(),
30608
+ remarks: import_joi85.default.string().optional().allow("", null)
30609
+ });
30610
+ const { error } = schema2.validate({ cardId, remarks });
30611
+ if (error) {
30612
+ return res.status(400).json({ message: error.message });
30613
+ }
30614
+ const result = await deleteCardSvc({ cardId, remarks });
30615
+ return res.status(200).json({ message: "Success", data: result });
30616
+ } catch (error) {
30617
+ return res.status(500).json({
30618
+ data: null,
30619
+ message: error.message
30620
+ });
30621
+ }
30622
+ };
30623
+ const getCardDetails = async (req, res) => {
30624
+ try {
30625
+ const { siteId, cardId } = req.query;
30626
+ const schema2 = import_joi85.default.object({
30627
+ siteId: import_joi85.default.string().hex().required(),
30628
+ cardId: import_joi85.default.string().hex().required()
30629
+ });
30630
+ const { error } = schema2.validate({ siteId, cardId });
30631
+ if (error) {
30632
+ return res.status(400).json({ message: error.message });
30633
+ }
30634
+ const result = await getCardDetailsSvc({ siteId, cardId });
30635
+ return res.status(200).json({ message: "Success", data: result });
30636
+ } catch (error) {
30637
+ return res.status(500).json({
30638
+ data: null,
30639
+ message: error.message
30640
+ });
30641
+ }
30642
+ };
29884
30643
  return {
29885
30644
  addPhysicalCard,
29886
30645
  addNonPhysicalCard,
@@ -29902,7 +30661,9 @@ function useAccessManagementController() {
29902
30661
  getCardReplacement,
29903
30662
  getAccessManagementSettings,
29904
30663
  bulkPhysicalAccessCard,
29905
- assignAccessCardToUnit
30664
+ assignAccessCardToUnit,
30665
+ deleteCard,
30666
+ getCardDetails
29906
30667
  };
29907
30668
  }
29908
30669
 
@@ -31619,9 +32380,9 @@ function useStatementOfAccountRepo() {
31619
32380
  page = page > 0 ? page - 1 : 0;
31620
32381
  let dateExpr = {};
31621
32382
  if (dateFrom && dateTo) {
31622
- const startDate = new Date(dateFrom);
32383
+ let startDate = new Date(dateFrom);
31623
32384
  startDate.setHours(0, 0, 0, 0);
31624
- const endDate = new Date(dateTo);
32385
+ let endDate = new Date(dateTo);
31625
32386
  endDate.setHours(23, 59, 59, 999);
31626
32387
  dateExpr = {
31627
32388
  $expr: {
@@ -31632,13 +32393,15 @@ function useStatementOfAccountRepo() {
31632
32393
  }
31633
32394
  };
31634
32395
  }
32396
+ const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
31635
32397
  const query = {
31636
32398
  ...status && status !== "all" && { status },
31637
32399
  ...search && {
31638
32400
  $or: [
31639
32401
  { unitOwner: { $regex: search, $options: "i" } },
31640
- { unit: { $regex: search, $options: "i" } },
31641
- { email: { $regex: search, $options: "i" } }
32402
+ { unit: { $regex: unitSearchRegex, $options: "i" } },
32403
+ { email: { $regex: search, $options: "i" } },
32404
+ { category: { $regex: search, $options: "i" } }
31642
32405
  ]
31643
32406
  },
31644
32407
  ...import_mongodb91.ObjectId.isValid(site) && { site: new import_mongodb91.ObjectId(site) },
@@ -33883,6 +34646,9 @@ function useIncidentReportRepo() {
33883
34646
  $regex: search,
33884
34647
  $options: "i"
33885
34648
  }
34649
+ },
34650
+ {
34651
+ approvedByName: { $regex: search, $options: "i" }
33886
34652
  }
33887
34653
  ]
33888
34654
  },
@@ -36396,7 +37162,9 @@ function useNfcPatrolLogController() {
36396
37162
  }
36397
37163
  // Annotate the CommonJS export names for ESM import in node:
36398
37164
  0 && (module.exports = {
37165
+ ANPRMode,
36399
37166
  AccessTypeProps,
37167
+ BULLETIN_RECIPIENTS,
36400
37168
  DEVICE_STATUS,
36401
37169
  EAccessCardTypes,
36402
37170
  EAccessCardUserTypes,
@@ -36455,12 +37223,15 @@ function useNfcPatrolLogController() {
36455
37223
  MVerification,
36456
37224
  MVisitorTransaction,
36457
37225
  MWorkOrder,
37226
+ OrgNature,
36458
37227
  PERSON_TYPES,
37228
+ STATUS_VALUES,
36459
37229
  UseAccessManagementRepo,
36460
- allowedCategories,
37230
+ VehicleCategory,
37231
+ VehicleStatus,
37232
+ VehicleType,
36461
37233
  allowedFieldsSite,
36462
37234
  allowedNatures,
36463
- allowedTypes,
36464
37235
  attendanceSchema,
36465
37236
  attendanceSettingsSchema,
36466
37237
  chatSchema,