@7365admin1/core 2.15.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -10906,7 +10906,7 @@ function useFeedbackController() {
10906
10906
  }
10907
10907
  async function deleteFeedback(req, res, next) {
10908
10908
  const validation = Joi28.string().hex().required();
10909
- const _id = req.query.id;
10909
+ const _id = req.params.id;
10910
10910
  const { error } = validation.validate(_id);
10911
10911
  if (error) {
10912
10912
  logger39.log({ level: "error", message: error.message });
@@ -11270,8 +11270,7 @@ function useWorkOrderController() {
11270
11270
  }
11271
11271
  async function deleteWorkOrder(req, res, next) {
11272
11272
  const validation = Joi29.string().hex().required();
11273
- const _id = req.query.id;
11274
- console.log(_id);
11273
+ const _id = req.params.id;
11275
11274
  const { error } = validation.validate(_id);
11276
11275
  if (error) {
11277
11276
  logger40.log({ level: "error", message: error.message });
@@ -15071,11 +15070,14 @@ function usePersonRepo() {
15071
15070
  const namespace_collection = "site.people";
15072
15071
  const collection = db.collection(namespace_collection);
15073
15072
  const { delNamespace, getCache, setCache } = useCache27(namespace_collection);
15074
- async function createIndex() {
15073
+ async function createIndexes() {
15075
15074
  try {
15076
- await collection.createIndex([{ nric: 1 }, { contact: 1 }]);
15075
+ await collection.createIndexes([
15076
+ { key: { contact: 1 } },
15077
+ { key: { nric: 1 } }
15078
+ ]);
15077
15079
  } catch (error) {
15078
- throw new InternalServerError27("Failed to create index.");
15080
+ throw new InternalServerError27("Failed to create index on site people.");
15079
15081
  }
15080
15082
  }
15081
15083
  async function createTextIndex() {
@@ -15465,6 +15467,47 @@ function usePersonRepo() {
15465
15467
  throw new InternalServerError27("Failed to fetch people by plate number.");
15466
15468
  }
15467
15469
  }
15470
+ async function getPeopleByNRIC({
15471
+ page = 1,
15472
+ limit = 10,
15473
+ nric = "",
15474
+ sort = {},
15475
+ site = ""
15476
+ }) {
15477
+ page = page > 0 ? page - 1 : 0;
15478
+ const normalizedNric = nric.trim();
15479
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15480
+ const query = {
15481
+ ...nric && { nric: normalizedNric },
15482
+ site: typeof site === "string" ? new ObjectId44(site) : site
15483
+ };
15484
+ const cacheOptions = {
15485
+ site,
15486
+ ...nric && { nric: normalizedNric },
15487
+ page: String(page),
15488
+ limit: String(limit),
15489
+ sort: JSON.stringify(sort)
15490
+ };
15491
+ try {
15492
+ const cacheKey = makeCacheKey25(namespace_collection, cacheOptions);
15493
+ const cachedData = await getCache(cacheKey);
15494
+ if (cachedData) {
15495
+ logger60.info(`Cache hit for key: ${cacheKey}`);
15496
+ return cachedData;
15497
+ }
15498
+ const items = await collection.find(query).sort(sort).skip(page * limit).limit(limit).toArray();
15499
+ const length = await collection.countDocuments(query);
15500
+ const data = paginate20(items, page, limit, length);
15501
+ setCache(cacheKey, data, 15 * 60).then(() => {
15502
+ logger60.info(`Cache set for key: ${cacheKey}`);
15503
+ }).catch((err) => {
15504
+ logger60.error(`Failed to set cache for key: ${cacheKey}`, err);
15505
+ });
15506
+ return data;
15507
+ } catch (error) {
15508
+ throw new InternalServerError27("Failed to retrieve person by NRIC.");
15509
+ }
15510
+ }
15468
15511
  return {
15469
15512
  add,
15470
15513
  getAll,
@@ -15474,11 +15517,12 @@ function usePersonRepo() {
15474
15517
  createTextIndex,
15475
15518
  getPersonByPlateNumber,
15476
15519
  getByNRIC,
15477
- createIndex,
15520
+ createIndexes,
15478
15521
  getPersonByPhoneNumber,
15479
15522
  getPeopleByUnit,
15480
15523
  getCompany,
15481
- getPeopleByPlateNumber
15524
+ getPeopleByPlateNumber,
15525
+ getPeopleByNRIC
15482
15526
  };
15483
15527
  }
15484
15528
 
@@ -15527,7 +15571,15 @@ var ANPRMode = /* @__PURE__ */ ((ANPRMode2) => {
15527
15571
  return ANPRMode2;
15528
15572
  })(ANPRMode || {});
15529
15573
  var vehicleSchema = Joi43.object({
15530
- plateNumber: Joi43.string().required(),
15574
+ plateNumber: Joi43.alternatives().try(
15575
+ Joi43.array().items(Joi43.string().trim().min(1)).min(1),
15576
+ Joi43.string().trim().min(1)
15577
+ ).custom((value) => {
15578
+ if (typeof value === "string") {
15579
+ return value.split(",").map((p) => p.trim()).filter(Boolean);
15580
+ }
15581
+ return value;
15582
+ }).required(),
15531
15583
  type: Joi43.string().required().valid(...Object.values(VehicleType)),
15532
15584
  category: Joi43.string().required().valid(...Object.values(VehicleCategory)),
15533
15585
  name: Joi43.string().required(),
@@ -15661,19 +15713,9 @@ function useVehicleRepo() {
15661
15713
  await collection.createIndexes([
15662
15714
  { key: { status: 1 } },
15663
15715
  { key: { type: 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
- }
15716
+ { key: { category: 1 } }
15674
15717
  ]);
15675
15718
  } catch (error) {
15676
- console.error("createIndexes error:", error);
15677
15719
  throw new InternalServerError28("Failed to create index on vehicle.");
15678
15720
  }
15679
15721
  }
@@ -15731,14 +15773,14 @@ function useVehicleRepo() {
15731
15773
  const baseQuery = {
15732
15774
  ...status && { status },
15733
15775
  ...type && { type },
15734
- ...type && { type }
15776
+ ...category && { category }
15735
15777
  };
15736
15778
  let query = { ...baseQuery };
15737
15779
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15738
15780
  const cacheOptions = {
15739
15781
  ...status && { status },
15740
15782
  ...type && { type },
15741
- ...type && { type },
15783
+ ...category && { category },
15742
15784
  page: String(page),
15743
15785
  limit: String(limit),
15744
15786
  sort: JSON.stringify(sort)
@@ -15754,47 +15796,130 @@ function useVehicleRepo() {
15754
15796
  return cachedData;
15755
15797
  }
15756
15798
  const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15757
- try {
15758
- let items = [];
15759
- let length = 0;
15760
- items = await collection.aggregate([
15761
- { $match: query },
15762
- { $sort: sort },
15763
- { $skip: page * limit },
15764
- { $limit: limit },
15765
- {
15766
- $lookup: {
15767
- from: "building-units",
15768
- localField: "unit",
15769
- foreignField: "_id",
15770
- as: "units",
15771
- pipeline: [{ $project: { name: 1 } }]
15799
+ const buildGroupedPipeline = (matchQuery) => [
15800
+ { $match: matchQuery },
15801
+ {
15802
+ $lookup: {
15803
+ from: "building-units",
15804
+ localField: "unit",
15805
+ foreignField: "_id",
15806
+ as: "units",
15807
+ pipeline: [{ $project: { name: 1 } }]
15808
+ }
15809
+ },
15810
+ { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15811
+ {
15812
+ $project: {
15813
+ _id: 1,
15814
+ name: 1,
15815
+ type: 1,
15816
+ category: 1,
15817
+ status: 1,
15818
+ phoneNumber: 1,
15819
+ block: 1,
15820
+ level: 1,
15821
+ unit: "$units.name",
15822
+ recNo: 1,
15823
+ nric: 1,
15824
+ plateNumber: 1
15825
+ }
15826
+ },
15827
+ {
15828
+ $group: {
15829
+ _id: {
15830
+ $cond: [
15831
+ {
15832
+ $and: [{ $ne: ["$nric", null] }, { $ne: ["$nric", ""] }]
15833
+ },
15834
+ {
15835
+ nric: "$nric",
15836
+ block: "$block",
15837
+ level: "$level",
15838
+ unit: "$unit"
15839
+ },
15840
+ "$_id"
15841
+ ]
15842
+ },
15843
+ vehicleId: { $first: "$_id" },
15844
+ name: { $first: "$name" },
15845
+ category: { $first: "$category" },
15846
+ phoneNumber: { $first: "$phoneNumber" },
15847
+ block: { $first: "$block" },
15848
+ level: { $first: "$level" },
15849
+ unit: { $first: "$unit" },
15850
+ nric: { $first: "$nric" },
15851
+ plates: {
15852
+ $addToSet: {
15853
+ plateNumber: "$plateNumber",
15854
+ recNo: "$recNo",
15855
+ status: "$status",
15856
+ type: "$type"
15857
+ }
15772
15858
  }
15773
- },
15774
- { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15775
- {
15776
- $project: {
15777
- name: 1,
15778
- type: 1,
15779
- category: 1,
15780
- status: 1,
15781
- phoneNumber: 1,
15782
- block: 1,
15783
- level: 1,
15784
- unit: "$units.name",
15785
- plateNumber: 1,
15786
- recNo: 1,
15787
- nric: 1
15859
+ }
15860
+ },
15861
+ {
15862
+ $project: {
15863
+ _id: "$vehicleId",
15864
+ name: 1,
15865
+ type: 1,
15866
+ category: 1,
15867
+ status: 1,
15868
+ phoneNumber: 1,
15869
+ block: 1,
15870
+ level: 1,
15871
+ unit: 1,
15872
+ nric: 1,
15873
+ plates: {
15874
+ $filter: {
15875
+ input: "$plates",
15876
+ as: "plate",
15877
+ cond: {
15878
+ $and: [
15879
+ { $ne: ["$$plate.plateNumber", null] },
15880
+ { $ne: ["$$plate.plateNumber", ""] }
15881
+ ]
15882
+ }
15883
+ }
15788
15884
  }
15789
15885
  }
15790
- ]).toArray();
15791
- length = await collection.countDocuments(query);
15886
+ },
15887
+ { $sort: sort },
15888
+ { $skip: page * limit },
15889
+ { $limit: limit }
15890
+ ];
15891
+ const buildGroupedCountPipeline = (matchQuery) => [
15892
+ { $match: matchQuery },
15893
+ {
15894
+ $group: {
15895
+ _id: {
15896
+ $cond: [
15897
+ {
15898
+ $and: [{ $ne: ["$nric", null] }, { $ne: ["$nric", ""] }]
15899
+ },
15900
+ {
15901
+ nric: "$nric",
15902
+ block: "$block",
15903
+ level: "$level",
15904
+ unit: "$unit"
15905
+ },
15906
+ "$_id"
15907
+ ]
15908
+ }
15909
+ }
15910
+ },
15911
+ { $count: "total" }
15912
+ ];
15913
+ try {
15914
+ let items = [];
15915
+ let length = 0;
15916
+ items = await collection.aggregate(buildGroupedPipeline(query)).toArray();
15917
+ const countResult = await collection.aggregate(buildGroupedCountPipeline(query)).toArray();
15918
+ length = countResult[0]?.total || 0;
15792
15919
  if ((!items || items.length === 0) && search) {
15793
15920
  const escaped = escapeRegex(search);
15794
15921
  const regexQuery = {
15795
15922
  ...baseQuery,
15796
- ...type && { type },
15797
- ...category && { category },
15798
15923
  $or: [
15799
15924
  { name: { $regex: escaped, $options: "i" } },
15800
15925
  { plateNumber: { $regex: escaped, $options: "i" } },
@@ -15805,38 +15930,9 @@ function useVehicleRepo() {
15805
15930
  { nric: { $regex: escaped, $options: "i" } }
15806
15931
  ]
15807
15932
  };
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);
15933
+ items = await collection.aggregate(buildGroupedPipeline(regexQuery)).toArray();
15934
+ const regexCountResult = await collection.aggregate(buildGroupedCountPipeline(regexQuery)).toArray();
15935
+ length = regexCountResult[0]?.total || 0;
15840
15936
  }
15841
15937
  const data = paginate21(items, page, limit, length);
15842
15938
  setCache(cacheKey, data, 15 * 60).then(() => logger62.info(`Cache set for key: ${cacheKey}`)).catch(
@@ -15914,43 +16010,176 @@ function useVehicleRepo() {
15914
16010
  }
15915
16011
  const data = await collection.aggregate([
15916
16012
  { $match: { _id } },
16013
+ { $limit: 1 },
16014
+ {
16015
+ $project: {
16016
+ _id: 1,
16017
+ nric: 1,
16018
+ block: 1,
16019
+ level: 1,
16020
+ unit: 1
16021
+ }
16022
+ },
15917
16023
  {
15918
16024
  $lookup: {
15919
- from: "building-units",
15920
- localField: "unit",
15921
- foreignField: "_id",
15922
- as: "units",
15923
- pipeline: [{ $project: { name: 1 } }]
16025
+ from: collection.collectionName,
16026
+ let: {
16027
+ vehicleId: "$_id",
16028
+ vehicleNric: "$nric",
16029
+ vehicleBlock: "$block",
16030
+ vehicleLevel: "$level",
16031
+ vehicleUnit: "$unit"
16032
+ },
16033
+ pipeline: [
16034
+ {
16035
+ $match: {
16036
+ $expr: {
16037
+ $cond: [
16038
+ {
16039
+ $and: [
16040
+ { $ne: ["$$vehicleNric", null] },
16041
+ { $ne: ["$$vehicleNric", ""] }
16042
+ ]
16043
+ },
16044
+ {
16045
+ $and: [
16046
+ { $eq: ["$nric", "$$vehicleNric"] },
16047
+ { $eq: ["$block", "$$vehicleBlock"] },
16048
+ { $eq: ["$level", "$$vehicleLevel"] },
16049
+ { $eq: ["$unit", "$$vehicleUnit"] }
16050
+ ]
16051
+ },
16052
+ { $eq: ["$_id", "$$vehicleId"] }
16053
+ ]
16054
+ }
16055
+ }
16056
+ },
16057
+ {
16058
+ $lookup: {
16059
+ from: "building-units",
16060
+ localField: "unit",
16061
+ foreignField: "_id",
16062
+ as: "units",
16063
+ pipeline: [{ $project: { name: 1 } }]
16064
+ }
16065
+ },
16066
+ {
16067
+ $unwind: { path: "$units", preserveNullAndEmptyArrays: true }
16068
+ },
16069
+ {
16070
+ $project: {
16071
+ _id: 1,
16072
+ name: 1,
16073
+ phoneNumber: 1,
16074
+ plateNumber: 1,
16075
+ nric: 1,
16076
+ block: 1,
16077
+ level: 1,
16078
+ rawUnit: "$unit",
16079
+ unit: "$units.name",
16080
+ type: 1,
16081
+ category: 1,
16082
+ status: 1,
16083
+ recNo: 1,
16084
+ start: 1,
16085
+ end: 1,
16086
+ seasonPassType: 1
16087
+ }
16088
+ },
16089
+ {
16090
+ $group: {
16091
+ _id: {
16092
+ $cond: [
16093
+ {
16094
+ $and: [
16095
+ { $ne: ["$nric", null] },
16096
+ { $ne: ["$nric", ""] }
16097
+ ]
16098
+ },
16099
+ {
16100
+ nric: "$nric",
16101
+ block: "$block",
16102
+ level: "$level",
16103
+ unit: "$unit"
16104
+ },
16105
+ "$_id"
16106
+ ]
16107
+ },
16108
+ vehicleId: { $first: "$_id" },
16109
+ name: { $first: "$name" },
16110
+ category: { $first: "$category" },
16111
+ phoneNumber: { $first: "$phoneNumber" },
16112
+ block: { $first: "$block" },
16113
+ level: { $first: "$level" },
16114
+ unit: { $first: "$unit" },
16115
+ nric: { $first: "$nric" },
16116
+ start: { $first: "$start" },
16117
+ end: { $first: "$end" },
16118
+ seasonPassType: { $first: "$seasonPassType" },
16119
+ plates: {
16120
+ $addToSet: {
16121
+ plateNumber: "$plateNumber",
16122
+ recNo: "$recNo",
16123
+ status: "$status",
16124
+ type: "$type"
16125
+ }
16126
+ }
16127
+ }
16128
+ },
16129
+ {
16130
+ $project: {
16131
+ _id: "$vehicleId",
16132
+ name: 1,
16133
+ phoneNumber: 1,
16134
+ nric: 1,
16135
+ block: 1,
16136
+ level: 1,
16137
+ unit: 1,
16138
+ type: 1,
16139
+ category: 1,
16140
+ status: 1,
16141
+ start: 1,
16142
+ end: 1,
16143
+ seasonPassType: 1,
16144
+ plates: {
16145
+ $filter: {
16146
+ input: "$plates",
16147
+ as: "plate",
16148
+ cond: {
16149
+ $and: [
16150
+ { $ne: ["$$plate.plateNumber", null] },
16151
+ { $ne: ["$$plate.plateNumber", ""] }
16152
+ ]
16153
+ }
16154
+ }
16155
+ }
16156
+ }
16157
+ }
16158
+ ],
16159
+ as: "vehicle"
15924
16160
  }
15925
16161
  },
15926
- { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15927
16162
  {
15928
16163
  $project: {
15929
- name: 1,
15930
- phoneNumber: 1,
15931
- plateNumber: 1,
15932
- block: 1,
15933
- level: 1,
15934
- unit: "$units.name",
15935
- type: 1,
15936
- category: 1,
15937
- status: 1,
15938
- recNo: 1,
15939
- start: 1,
15940
- end: 1,
15941
- seasonPassType: 1
16164
+ vehicle: { $arrayElemAt: ["$vehicle", 0] }
16165
+ }
16166
+ },
16167
+ {
16168
+ $replaceRoot: {
16169
+ newRoot: "$vehicle"
15942
16170
  }
15943
16171
  }
15944
16172
  ]).toArray();
15945
16173
  if (!data || !data.length) {
15946
16174
  throw new NotFoundError19("Vehicle not found.");
15947
16175
  }
15948
- setCache(cacheKey, data[0], 15 * 60).then(() => {
16176
+ const result = data[0];
16177
+ setCache(cacheKey, result, 15 * 60).then(() => {
15949
16178
  logger62.info(`Cache set for key: ${cacheKey}`);
15950
16179
  }).catch((err) => {
15951
16180
  logger62.error(`Failed to set cache for key: ${cacheKey}`, err);
15952
16181
  });
15953
- return data[0];
16182
+ return result;
15954
16183
  } catch (error) {
15955
16184
  throw error;
15956
16185
  }
@@ -16084,36 +16313,34 @@ function useVehicleRepo() {
16084
16313
  async function getVehiclesByNRIC({
16085
16314
  page = 1,
16086
16315
  limit = 10,
16087
- search = "",
16316
+ nric = "",
16088
16317
  sort = {}
16089
16318
  }) {
16090
16319
  page = page > 0 ? page - 1 : 0;
16091
- const baseQuery = {
16092
- deletedAt: ""
16320
+ if (!nric) {
16321
+ throw new BadRequestError78("NRIC is required.");
16322
+ }
16323
+ const _nric = nric.trim();
16324
+ const query = {
16325
+ deletedAt: "",
16326
+ nric: _nric
16093
16327
  };
16094
- let query = { ...baseQuery };
16095
16328
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
16096
16329
  const cacheOptions = {
16097
16330
  deletedAt: "",
16331
+ nric: _nric,
16098
16332
  page: String(page),
16099
16333
  limit: String(limit),
16100
16334
  sort: JSON.stringify(sort)
16101
16335
  };
16102
- if (search) {
16103
- query.$text = { $search: search };
16104
- cacheOptions.search = search;
16105
- }
16106
16336
  const cacheKey = makeCacheKey26(namespace_collection, cacheOptions);
16107
16337
  const cachedData = await getCache(cacheKey);
16108
16338
  if (cachedData) {
16109
16339
  logger62.info(`Cache hit for key: ${cacheKey}`);
16110
16340
  return cachedData;
16111
16341
  }
16112
- const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16113
16342
  try {
16114
- let items = [];
16115
- let length = 0;
16116
- items = await collection.aggregate([
16343
+ const items = await collection.aggregate([
16117
16344
  { $match: query },
16118
16345
  { $sort: sort },
16119
16346
  { $skip: page * limit },
@@ -16130,37 +16357,7 @@ function useVehicleRepo() {
16130
16357
  }
16131
16358
  }
16132
16359
  ]).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
- }
16360
+ const length = await collection.countDocuments(query);
16164
16361
  const data = paginate21(items, page, limit, length);
16165
16362
  setCache(cacheKey, data, 15 * 60).then(() => logger62.info(`Cache set for key: ${cacheKey}`)).catch(
16166
16363
  (err) => logger62.error(`Failed to set cache for key: ${cacheKey}`, err)
@@ -16620,7 +16817,7 @@ function useDahuaService() {
16620
16817
  host: value.host,
16621
16818
  username: value.username,
16622
16819
  password: value.password,
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}`
16820
+ endpoint: `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&BeginTime=${value.start}&CancelTime=${value.end}&+OpenGate=true&MasterOfCar=${value.owner}`
16624
16821
  });
16625
16822
  return response;
16626
16823
  } catch (error2) {
@@ -16692,8 +16889,9 @@ function useVehicleService() {
16692
16889
  const { getById: _getById } = useOrgRepo();
16693
16890
  async function add(value) {
16694
16891
  const session = useAtlas39.getClient()?.startSession();
16695
- if (!session)
16892
+ if (!session) {
16696
16893
  throw new Error("Unable to start session for vehicle service.");
16894
+ }
16697
16895
  const org = await _getById(value.org);
16698
16896
  if (!org)
16699
16897
  throw new BadRequestError80("Org not found");
@@ -16741,11 +16939,12 @@ function useVehicleService() {
16741
16939
  }
16742
16940
  }
16743
16941
  const owner = String(value.name ?? "").substring(0, 15);
16942
+ const plateNumbers = value.plateNumber;
16744
16943
  try {
16745
16944
  session.startTransaction();
16746
16945
  let message = "Vehicle plate number needs approval from property management.";
16946
+ let siteCameras = [];
16747
16947
  if (value.status && value.status !== "pending" /* PENDING */) {
16748
- const siteCameras = [];
16749
16948
  let page = 1;
16750
16949
  let pages = 1;
16751
16950
  const limit = 20;
@@ -16760,40 +16959,44 @@ function useVehicleService() {
16760
16959
  pages = siteCameraReq.pages || 1;
16761
16960
  siteCameras.push(...siteCameraReq.items);
16762
16961
  page++;
16763
- } while (page < pages);
16764
- if (!siteCameras.length)
16962
+ } while (page <= pages);
16963
+ if (!siteCameras.length) {
16765
16964
  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
- );
16965
+ }
16966
+ }
16967
+ for (const plateNumber of plateNumbers) {
16968
+ const vehicleValue = {
16969
+ ...value,
16970
+ plateNumber,
16971
+ start,
16972
+ end
16973
+ };
16974
+ if (vehicleValue.status && vehicleValue.status !== "pending" /* PENDING */) {
16975
+ for (const camera of siteCameras) {
16976
+ const { host, username, password } = camera;
16977
+ const dahuaPayload = {
16978
+ host,
16979
+ username,
16980
+ password,
16981
+ plateNumber,
16982
+ mode: _mode,
16983
+ owner,
16984
+ ...startDateDahua ? { start: startDateDahua } : {},
16985
+ ...endDateDahua ? { end: endDateDahua } : {}
16986
+ };
16987
+ const dahuaResponse = await _addPlateNumber(dahuaPayload);
16988
+ if (dahuaResponse?.statusCode !== 200) {
16989
+ throw new BadRequestError80(
16990
+ `Failed to add plate number to ANPR ${_type}`
16991
+ );
16992
+ }
16993
+ const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
16994
+ vehicleValue.recNo = responseData.split("=")[1]?.trim();
16783
16995
  }
16784
- const responseData = dahuaResponse?.data.toString("utf-8");
16785
- value.recNo = responseData.split("=")[1]?.trim();
16786
16996
  message = `Vehicle plate number added to ${_type} successfully.`;
16787
16997
  }
16998
+ await _add(vehicleValue, session);
16788
16999
  }
16789
- const formattedValue = {
16790
- ...value,
16791
- start,
16792
- // ISO string or ""
16793
- end
16794
- // ISO string or ""
16795
- };
16796
- await _add(formattedValue, session);
16797
17000
  await session.commitTransaction();
16798
17001
  return message;
16799
17002
  } catch (error) {
@@ -16998,14 +17201,14 @@ function useVehicleController() {
16998
17201
  } = useVehicleRepo();
16999
17202
  async function add(req, res, next) {
17000
17203
  const payload = req.body;
17001
- const { error } = vehicleSchema.validate(payload);
17204
+ const { error, value } = vehicleSchema.validate(payload);
17002
17205
  if (error) {
17003
17206
  logger65.log({ level: "error", message: error.message });
17004
17207
  next(new BadRequestError81(error.message));
17005
17208
  return;
17006
17209
  }
17007
17210
  try {
17008
- const data = await _add(payload);
17211
+ const data = await _add(value);
17009
17212
  res.status(201).json({
17010
17213
  message: "Vehicle added successfully.",
17011
17214
  data
@@ -17025,9 +17228,9 @@ function useVehicleController() {
17025
17228
  limit: Joi46.number().integer().min(1).max(100).allow("", null).default(10),
17026
17229
  sort: Joi46.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
17027
17230
  order: Joi46.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
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("")
17231
+ type: Joi46.string().optional().valid(...Object.values(VehicleType)).allow(null, ""),
17232
+ category: Joi46.string().optional().valid(...Object.values(VehicleCategory)).allow(null, ""),
17233
+ status: Joi46.string().optional().valid(...Object.values(VehicleStatus)).allow(null, "")
17031
17234
  });
17032
17235
  const query = { ...req.query };
17033
17236
  const { error } = validation.validate(query);
@@ -17192,7 +17395,7 @@ function useVehicleController() {
17192
17395
  const allowedFields = ["start", "end"];
17193
17396
  const allowedOrder = ["asc", "desc"];
17194
17397
  const validation = Joi46.object({
17195
- search: Joi46.string().optional().allow("", null),
17398
+ nric: Joi46.string().optional().allow("", null),
17196
17399
  page: Joi46.number().integer().min(1).allow("", null).default(1),
17197
17400
  limit: Joi46.number().integer().min(1).max(100).allow("", null).default(10),
17198
17401
  sort: Joi46.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
@@ -17205,7 +17408,7 @@ function useVehicleController() {
17205
17408
  next(new BadRequestError81(error.message));
17206
17409
  return;
17207
17410
  }
17208
- const search = req.query.search ?? "";
17411
+ const nric = req.query.nric ?? "";
17209
17412
  const page = parseInt(req.query.page ?? "1");
17210
17413
  const limit = parseInt(req.query.limit ?? "10");
17211
17414
  const sortObj = {};
@@ -17219,7 +17422,7 @@ function useVehicleController() {
17219
17422
  });
17220
17423
  try {
17221
17424
  const data = await _getVehiclesByNRIC({
17222
- search,
17425
+ nric,
17223
17426
  page,
17224
17427
  limit,
17225
17428
  sort: sortObj
@@ -19868,25 +20071,15 @@ function usePersonService() {
19868
20071
  }
19869
20072
  async function plateValidity(value) {
19870
20073
  const now = /* @__PURE__ */ new Date();
19871
- if (value.type !== "resident" && value.type !== "tenant") {
19872
- return { start: "", end: "" };
19873
- }
19874
- if (value.type === "resident") {
20074
+ if (["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
20075
+ value?.type
20076
+ )) {
19875
20077
  const end = new Date(now);
19876
20078
  end.setFullYear(end.getFullYear() + 10);
19877
- return { start: now.toISOString(), end: end.toISOString() };
19878
- }
19879
- if (value.type == "tenant") {
19880
- const unit = await _getUnitById(value.unit?.toString() || "");
19881
- if (!unit) {
19882
- throw new BadRequestError97("Building unit not found for tenant.");
19883
- }
19884
- if (unit.leaseStart && unit.leaseEnd) {
19885
- return {
19886
- start: new Date(unit.leaseStart).toISOString(),
19887
- end: new Date(unit.leaseEnd).toISOString()
19888
- };
19889
- }
20079
+ return {
20080
+ start: now.toISOString(),
20081
+ end: end.toISOString()
20082
+ };
19890
20083
  }
19891
20084
  return { start: "", end: "" };
19892
20085
  }
@@ -19905,7 +20098,8 @@ function usePersonController() {
19905
20098
  getPersonByPhoneNumber: _getPersonByPhoneNumber,
19906
20099
  getPeopleByUnit: _getPeopleByUnit,
19907
20100
  getCompany: _getCompany,
19908
- getPeopleByPlateNumber: _getPeopleByPlateNumber
20101
+ getPeopleByPlateNumber: _getPeopleByPlateNumber,
20102
+ getPeopleByNRIC: _getPeopleByNRIC
19909
20103
  } = usePersonRepo();
19910
20104
  const { add: _add, updateById: _updateById } = usePersonService();
19911
20105
  async function add(req, res, next) {
@@ -20140,6 +20334,53 @@ function usePersonController() {
20140
20334
  return;
20141
20335
  }
20142
20336
  }
20337
+ async function getPeopleByNRIC(req, res, next) {
20338
+ const allowedFields = ["start", "end"];
20339
+ const allowedOrder = ["asc", "desc"];
20340
+ const validation = Joi58.object({
20341
+ nric: Joi58.string().optional().allow("", null),
20342
+ page: Joi58.number().integer().min(1).allow("", null).default(1),
20343
+ limit: Joi58.number().integer().min(1).max(100).allow("", null).default(10),
20344
+ sort: Joi58.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
20345
+ order: Joi58.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
20346
+ site: Joi58.string().hex().length(24).required()
20347
+ });
20348
+ const query = { ...req.query };
20349
+ const { error } = validation.validate(query);
20350
+ if (error) {
20351
+ logger81.log({ level: "error", message: error.message });
20352
+ next(new BadRequestError98(error.message));
20353
+ return;
20354
+ }
20355
+ const nric = req.query.nric ?? "";
20356
+ const page = parseInt(req.query.page ?? "1");
20357
+ const limit = parseInt(req.query.limit ?? "10");
20358
+ const site = req.query.site ?? "";
20359
+ const sortObj = {};
20360
+ const sortFields = String(req.query.sort).split(",");
20361
+ const sortOrders = String(req.query.order).split(",");
20362
+ sortFields.forEach((field, index) => {
20363
+ if (allowedFields.includes(field)) {
20364
+ const order = sortOrders[index] === "asc" ? 1 : -1;
20365
+ sortObj[field] = order;
20366
+ }
20367
+ });
20368
+ try {
20369
+ const data = await _getPeopleByNRIC({
20370
+ nric,
20371
+ page,
20372
+ limit,
20373
+ sort: sortObj,
20374
+ site
20375
+ });
20376
+ res.json(data);
20377
+ return;
20378
+ } catch (error2) {
20379
+ logger81.log({ level: "error", message: error2.message });
20380
+ next(error2);
20381
+ return;
20382
+ }
20383
+ }
20143
20384
  return {
20144
20385
  add,
20145
20386
  getAll,
@@ -20149,7 +20390,8 @@ function usePersonController() {
20149
20390
  getPersonByPhoneNumber,
20150
20391
  getPeopleByUnit,
20151
20392
  getCompany,
20152
- getPeopleByPlateNumber
20393
+ getPeopleByPlateNumber,
20394
+ getPeopleByNRIC
20153
20395
  };
20154
20396
  }
20155
20397
 
@@ -29417,7 +29659,8 @@ function UseAccessManagementRepo() {
29417
29659
  const query = {
29418
29660
  site: siteId,
29419
29661
  assignedUnit: { $ne: null },
29420
- type: "NFC" /* NFC */
29662
+ type: "NFC" /* NFC */,
29663
+ isActivated: true
29421
29664
  };
29422
29665
  if (search) {
29423
29666
  query.$or = [{ accessLevel: { $regex: search, $options: "i" } }, { cardNo: { $regex: search, $options: "i" } }];
@@ -29425,7 +29668,7 @@ function UseAccessManagementRepo() {
29425
29668
  if (type) {
29426
29669
  query.userType = type;
29427
29670
  } else {
29428
- query.userType = { $ne: "Resident/Tenant" /* RESIDENT */ };
29671
+ query.userType = { $ne: "Visitor/Resident" /* DEFAULT */ };
29429
29672
  }
29430
29673
  const res = await collection().aggregate([
29431
29674
  {
@@ -29799,6 +30042,52 @@ function UseAccessManagementRepo() {
29799
30042
  throw new Error(error.message);
29800
30043
  }
29801
30044
  }
30045
+ async function addQrTagRepo(params) {
30046
+ try {
30047
+ const { site, payload } = params;
30048
+ const id = new ObjectId83(site);
30049
+ const highestCardNo = await collection().aggregate([
30050
+ { $match: { site: id, type: "NFC" /* NFC */, userType: "Visitor/Resident" /* DEFAULT */ } },
30051
+ {
30052
+ $addFields: {
30053
+ qrTagCardNoNumeric: { $toInt: "$qrTagCardNo" }
30054
+ // Convert string to integer
30055
+ }
30056
+ },
30057
+ {
30058
+ $sort: { qrTagCardNoNumeric: -1 }
30059
+ // Sort in descending order
30060
+ },
30061
+ {
30062
+ $limit: 1
30063
+ // Get the highest
30064
+ }
30065
+ ]).toArray();
30066
+ let start = 0;
30067
+ if (highestCardNo.length > 0) {
30068
+ start = highestCardNo[0].qrTagCardNoNumeric || 0;
30069
+ }
30070
+ const nextCardNumbers = Array.from({ length: payload.length }, (_, i) => String(start + i + 1).padStart(5, "0"));
30071
+ const bulkOps = await Promise.all(
30072
+ payload.map(async (doc, index) => {
30073
+ const id2 = new ObjectId83(doc._id);
30074
+ return {
30075
+ updateOne: {
30076
+ filter: { _id: id2 },
30077
+ update: { $set: { qrTag: doc.qrTag, qrTagCardNo: nextCardNumbers[index] } }
30078
+ }
30079
+ };
30080
+ })
30081
+ );
30082
+ let result;
30083
+ if (bulkOps.length > 0) {
30084
+ result = await collection().bulkWrite(bulkOps);
30085
+ }
30086
+ return result;
30087
+ } catch (error) {
30088
+ throw new Error(error.message);
30089
+ }
30090
+ }
29802
30091
  return {
29803
30092
  createIndexes,
29804
30093
  createIndexForEntrypass,
@@ -29821,7 +30110,8 @@ function UseAccessManagementRepo() {
29821
30110
  bulkPhysicalAccessCardRepo,
29822
30111
  assignAccessCardToUnitRepo,
29823
30112
  deleteCardRepo,
29824
- getCardDetailsRepo
30113
+ getCardDetailsRepo,
30114
+ addQrTagRepo
29825
30115
  };
29826
30116
  }
29827
30117
 
@@ -29855,7 +30145,8 @@ function useAccessManagementSvc() {
29855
30145
  bulkPhysicalAccessCardRepo,
29856
30146
  assignAccessCardToUnitRepo,
29857
30147
  deleteCardRepo,
29858
- getCardDetailsRepo
30148
+ getCardDetailsRepo,
30149
+ addQrTagRepo
29859
30150
  } = UseAccessManagementRepo();
29860
30151
  const addPhysicalCardSvc = async (payload) => {
29861
30152
  try {
@@ -30119,6 +30410,14 @@ function useAccessManagementSvc() {
30119
30410
  throw new Error(err.message);
30120
30411
  }
30121
30412
  };
30413
+ const addQrTagSvc = async (params) => {
30414
+ try {
30415
+ const response = await addQrTagRepo({ ...params });
30416
+ return response;
30417
+ } catch (err) {
30418
+ throw new Error(err.message);
30419
+ }
30420
+ };
30122
30421
  return {
30123
30422
  addPhysicalCardSvc,
30124
30423
  addNonPhysicalCardSvc,
@@ -30145,7 +30444,8 @@ function useAccessManagementSvc() {
30145
30444
  bulkPhysicalAccessCardSvc,
30146
30445
  assignAccessCardToUnitSvc,
30147
30446
  deleteCardSvc,
30148
- getCardDetailsSvc
30447
+ getCardDetailsSvc,
30448
+ addQrTagSvc
30149
30449
  };
30150
30450
  }
30151
30451
 
@@ -30179,7 +30479,8 @@ function useAccessManagementController() {
30179
30479
  bulkPhysicalAccessCardSvc,
30180
30480
  assignAccessCardToUnitSvc,
30181
30481
  deleteCardSvc,
30182
- getCardDetailsSvc
30482
+ getCardDetailsSvc,
30483
+ addQrTagSvc
30183
30484
  } = useAccessManagementSvc();
30184
30485
  const addPhysicalCard = async (req, res) => {
30185
30486
  try {
@@ -30790,6 +31091,34 @@ function useAccessManagementController() {
30790
31091
  });
30791
31092
  }
30792
31093
  };
31094
+ const addQrTag = async (req, res) => {
31095
+ try {
31096
+ const payload = req.body;
31097
+ const site = req.params.site;
31098
+ const schema2 = Joi85.object({
31099
+ site: Joi85.string().hex().required(),
31100
+ payload: Joi85.array().items(
31101
+ Joi85.object({
31102
+ _id: Joi85.string().hex().required(),
31103
+ cardNo: Joi85.string().required(),
31104
+ qrTag: Joi85.string().required(),
31105
+ qrTagCardNo: Joi85.string().required()
31106
+ })
31107
+ ).required()
31108
+ });
31109
+ const { error } = schema2.validate({ site, payload });
31110
+ if (error) {
31111
+ return res.status(400).json({ message: error.message });
31112
+ }
31113
+ const result = await addQrTagSvc({ payload, site });
31114
+ return res.status(200).json({ message: "Success", data: result });
31115
+ } catch (error) {
31116
+ return res.status(500).json({
31117
+ data: null,
31118
+ message: error.message
31119
+ });
31120
+ }
31121
+ };
30793
31122
  return {
30794
31123
  addPhysicalCard,
30795
31124
  addNonPhysicalCard,
@@ -30813,7 +31142,8 @@ function useAccessManagementController() {
30813
31142
  bulkPhysicalAccessCard,
30814
31143
  assignAccessCardToUnit,
30815
31144
  deleteCard,
30816
- getCardDetails
31145
+ getCardDetails,
31146
+ addQrTag
30817
31147
  };
30818
31148
  }
30819
31149