@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.js CHANGED
@@ -11040,7 +11040,7 @@ function useFeedbackController() {
11040
11040
  }
11041
11041
  async function deleteFeedback(req, res, next) {
11042
11042
  const validation = import_joi28.default.string().hex().required();
11043
- const _id = req.query.id;
11043
+ const _id = req.params.id;
11044
11044
  const { error } = validation.validate(_id);
11045
11045
  if (error) {
11046
11046
  import_node_server_utils55.logger.log({ level: "error", message: error.message });
@@ -11404,8 +11404,7 @@ function useWorkOrderController() {
11404
11404
  }
11405
11405
  async function deleteWorkOrder(req, res, next) {
11406
11406
  const validation = import_joi29.default.string().hex().required();
11407
- const _id = req.query.id;
11408
- console.log(_id);
11407
+ const _id = req.params.id;
11409
11408
  const { error } = validation.validate(_id);
11410
11409
  if (error) {
11411
11410
  import_node_server_utils57.logger.log({ level: "error", message: error.message });
@@ -15126,11 +15125,14 @@ function usePersonRepo() {
15126
15125
  const namespace_collection = "site.people";
15127
15126
  const collection = db.collection(namespace_collection);
15128
15127
  const { delNamespace, getCache, setCache } = (0, import_node_server_utils79.useCache)(namespace_collection);
15129
- async function createIndex() {
15128
+ async function createIndexes() {
15130
15129
  try {
15131
- await collection.createIndex([{ nric: 1 }, { contact: 1 }]);
15130
+ await collection.createIndexes([
15131
+ { key: { contact: 1 } },
15132
+ { key: { nric: 1 } }
15133
+ ]);
15132
15134
  } catch (error) {
15133
- throw new import_node_server_utils79.InternalServerError("Failed to create index.");
15135
+ throw new import_node_server_utils79.InternalServerError("Failed to create index on site people.");
15134
15136
  }
15135
15137
  }
15136
15138
  async function createTextIndex() {
@@ -15520,6 +15522,47 @@ function usePersonRepo() {
15520
15522
  throw new import_node_server_utils79.InternalServerError("Failed to fetch people by plate number.");
15521
15523
  }
15522
15524
  }
15525
+ async function getPeopleByNRIC({
15526
+ page = 1,
15527
+ limit = 10,
15528
+ nric = "",
15529
+ sort = {},
15530
+ site = ""
15531
+ }) {
15532
+ page = page > 0 ? page - 1 : 0;
15533
+ const normalizedNric = nric.trim();
15534
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15535
+ const query = {
15536
+ ...nric && { nric: normalizedNric },
15537
+ site: typeof site === "string" ? new import_mongodb44.ObjectId(site) : site
15538
+ };
15539
+ const cacheOptions = {
15540
+ site,
15541
+ ...nric && { nric: normalizedNric },
15542
+ page: String(page),
15543
+ limit: String(limit),
15544
+ sort: JSON.stringify(sort)
15545
+ };
15546
+ try {
15547
+ const cacheKey = (0, import_node_server_utils79.makeCacheKey)(namespace_collection, cacheOptions);
15548
+ const cachedData = await getCache(cacheKey);
15549
+ if (cachedData) {
15550
+ import_node_server_utils79.logger.info(`Cache hit for key: ${cacheKey}`);
15551
+ return cachedData;
15552
+ }
15553
+ const items = await collection.find(query).sort(sort).skip(page * limit).limit(limit).toArray();
15554
+ const length = await collection.countDocuments(query);
15555
+ const data = (0, import_node_server_utils79.paginate)(items, page, limit, length);
15556
+ setCache(cacheKey, data, 15 * 60).then(() => {
15557
+ import_node_server_utils79.logger.info(`Cache set for key: ${cacheKey}`);
15558
+ }).catch((err) => {
15559
+ import_node_server_utils79.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
15560
+ });
15561
+ return data;
15562
+ } catch (error) {
15563
+ throw new import_node_server_utils79.InternalServerError("Failed to retrieve person by NRIC.");
15564
+ }
15565
+ }
15523
15566
  return {
15524
15567
  add,
15525
15568
  getAll,
@@ -15529,11 +15572,12 @@ function usePersonRepo() {
15529
15572
  createTextIndex,
15530
15573
  getPersonByPlateNumber,
15531
15574
  getByNRIC,
15532
- createIndex,
15575
+ createIndexes,
15533
15576
  getPersonByPhoneNumber,
15534
15577
  getPeopleByUnit,
15535
15578
  getCompany,
15536
- getPeopleByPlateNumber
15579
+ getPeopleByPlateNumber,
15580
+ getPeopleByNRIC
15537
15581
  };
15538
15582
  }
15539
15583
 
@@ -15572,7 +15616,15 @@ var ANPRMode = /* @__PURE__ */ ((ANPRMode2) => {
15572
15616
  return ANPRMode2;
15573
15617
  })(ANPRMode || {});
15574
15618
  var vehicleSchema = import_joi43.default.object({
15575
- plateNumber: import_joi43.default.string().required(),
15619
+ plateNumber: import_joi43.default.alternatives().try(
15620
+ import_joi43.default.array().items(import_joi43.default.string().trim().min(1)).min(1),
15621
+ import_joi43.default.string().trim().min(1)
15622
+ ).custom((value) => {
15623
+ if (typeof value === "string") {
15624
+ return value.split(",").map((p) => p.trim()).filter(Boolean);
15625
+ }
15626
+ return value;
15627
+ }).required(),
15576
15628
  type: import_joi43.default.string().required().valid(...Object.values(VehicleType)),
15577
15629
  category: import_joi43.default.string().required().valid(...Object.values(VehicleCategory)),
15578
15630
  name: import_joi43.default.string().required(),
@@ -15706,19 +15758,9 @@ function useVehicleRepo() {
15706
15758
  await collection.createIndexes([
15707
15759
  { key: { status: 1 } },
15708
15760
  { key: { type: 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
- }
15761
+ { key: { category: 1 } }
15719
15762
  ]);
15720
15763
  } catch (error) {
15721
- console.error("createIndexes error:", error);
15722
15764
  throw new import_node_server_utils81.InternalServerError("Failed to create index on vehicle.");
15723
15765
  }
15724
15766
  }
@@ -15776,14 +15818,14 @@ function useVehicleRepo() {
15776
15818
  const baseQuery = {
15777
15819
  ...status && { status },
15778
15820
  ...type && { type },
15779
- ...type && { type }
15821
+ ...category && { category }
15780
15822
  };
15781
15823
  let query = { ...baseQuery };
15782
15824
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
15783
15825
  const cacheOptions = {
15784
15826
  ...status && { status },
15785
15827
  ...type && { type },
15786
- ...type && { type },
15828
+ ...category && { category },
15787
15829
  page: String(page),
15788
15830
  limit: String(limit),
15789
15831
  sort: JSON.stringify(sort)
@@ -15799,47 +15841,130 @@ function useVehicleRepo() {
15799
15841
  return cachedData;
15800
15842
  }
15801
15843
  const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15802
- try {
15803
- let items = [];
15804
- let length = 0;
15805
- items = await collection.aggregate([
15806
- { $match: query },
15807
- { $sort: sort },
15808
- { $skip: page * limit },
15809
- { $limit: limit },
15810
- {
15811
- $lookup: {
15812
- from: "building-units",
15813
- localField: "unit",
15814
- foreignField: "_id",
15815
- as: "units",
15816
- pipeline: [{ $project: { name: 1 } }]
15844
+ const buildGroupedPipeline = (matchQuery) => [
15845
+ { $match: matchQuery },
15846
+ {
15847
+ $lookup: {
15848
+ from: "building-units",
15849
+ localField: "unit",
15850
+ foreignField: "_id",
15851
+ as: "units",
15852
+ pipeline: [{ $project: { name: 1 } }]
15853
+ }
15854
+ },
15855
+ { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15856
+ {
15857
+ $project: {
15858
+ _id: 1,
15859
+ name: 1,
15860
+ type: 1,
15861
+ category: 1,
15862
+ status: 1,
15863
+ phoneNumber: 1,
15864
+ block: 1,
15865
+ level: 1,
15866
+ unit: "$units.name",
15867
+ recNo: 1,
15868
+ nric: 1,
15869
+ plateNumber: 1
15870
+ }
15871
+ },
15872
+ {
15873
+ $group: {
15874
+ _id: {
15875
+ $cond: [
15876
+ {
15877
+ $and: [{ $ne: ["$nric", null] }, { $ne: ["$nric", ""] }]
15878
+ },
15879
+ {
15880
+ nric: "$nric",
15881
+ block: "$block",
15882
+ level: "$level",
15883
+ unit: "$unit"
15884
+ },
15885
+ "$_id"
15886
+ ]
15887
+ },
15888
+ vehicleId: { $first: "$_id" },
15889
+ name: { $first: "$name" },
15890
+ category: { $first: "$category" },
15891
+ phoneNumber: { $first: "$phoneNumber" },
15892
+ block: { $first: "$block" },
15893
+ level: { $first: "$level" },
15894
+ unit: { $first: "$unit" },
15895
+ nric: { $first: "$nric" },
15896
+ plates: {
15897
+ $addToSet: {
15898
+ plateNumber: "$plateNumber",
15899
+ recNo: "$recNo",
15900
+ status: "$status",
15901
+ type: "$type"
15902
+ }
15817
15903
  }
15818
- },
15819
- { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15820
- {
15821
- $project: {
15822
- name: 1,
15823
- type: 1,
15824
- category: 1,
15825
- status: 1,
15826
- phoneNumber: 1,
15827
- block: 1,
15828
- level: 1,
15829
- unit: "$units.name",
15830
- plateNumber: 1,
15831
- recNo: 1,
15832
- nric: 1
15904
+ }
15905
+ },
15906
+ {
15907
+ $project: {
15908
+ _id: "$vehicleId",
15909
+ name: 1,
15910
+ type: 1,
15911
+ category: 1,
15912
+ status: 1,
15913
+ phoneNumber: 1,
15914
+ block: 1,
15915
+ level: 1,
15916
+ unit: 1,
15917
+ nric: 1,
15918
+ plates: {
15919
+ $filter: {
15920
+ input: "$plates",
15921
+ as: "plate",
15922
+ cond: {
15923
+ $and: [
15924
+ { $ne: ["$$plate.plateNumber", null] },
15925
+ { $ne: ["$$plate.plateNumber", ""] }
15926
+ ]
15927
+ }
15928
+ }
15833
15929
  }
15834
15930
  }
15835
- ]).toArray();
15836
- length = await collection.countDocuments(query);
15931
+ },
15932
+ { $sort: sort },
15933
+ { $skip: page * limit },
15934
+ { $limit: limit }
15935
+ ];
15936
+ const buildGroupedCountPipeline = (matchQuery) => [
15937
+ { $match: matchQuery },
15938
+ {
15939
+ $group: {
15940
+ _id: {
15941
+ $cond: [
15942
+ {
15943
+ $and: [{ $ne: ["$nric", null] }, { $ne: ["$nric", ""] }]
15944
+ },
15945
+ {
15946
+ nric: "$nric",
15947
+ block: "$block",
15948
+ level: "$level",
15949
+ unit: "$unit"
15950
+ },
15951
+ "$_id"
15952
+ ]
15953
+ }
15954
+ }
15955
+ },
15956
+ { $count: "total" }
15957
+ ];
15958
+ try {
15959
+ let items = [];
15960
+ let length = 0;
15961
+ items = await collection.aggregate(buildGroupedPipeline(query)).toArray();
15962
+ const countResult = await collection.aggregate(buildGroupedCountPipeline(query)).toArray();
15963
+ length = countResult[0]?.total || 0;
15837
15964
  if ((!items || items.length === 0) && search) {
15838
15965
  const escaped = escapeRegex(search);
15839
15966
  const regexQuery = {
15840
15967
  ...baseQuery,
15841
- ...type && { type },
15842
- ...category && { category },
15843
15968
  $or: [
15844
15969
  { name: { $regex: escaped, $options: "i" } },
15845
15970
  { plateNumber: { $regex: escaped, $options: "i" } },
@@ -15850,38 +15975,9 @@ function useVehicleRepo() {
15850
15975
  { nric: { $regex: escaped, $options: "i" } }
15851
15976
  ]
15852
15977
  };
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);
15978
+ items = await collection.aggregate(buildGroupedPipeline(regexQuery)).toArray();
15979
+ const regexCountResult = await collection.aggregate(buildGroupedCountPipeline(regexQuery)).toArray();
15980
+ length = regexCountResult[0]?.total || 0;
15885
15981
  }
15886
15982
  const data = (0, import_node_server_utils81.paginate)(items, page, limit, length);
15887
15983
  setCache(cacheKey, data, 15 * 60).then(() => import_node_server_utils81.logger.info(`Cache set for key: ${cacheKey}`)).catch(
@@ -15959,43 +16055,176 @@ function useVehicleRepo() {
15959
16055
  }
15960
16056
  const data = await collection.aggregate([
15961
16057
  { $match: { _id } },
16058
+ { $limit: 1 },
16059
+ {
16060
+ $project: {
16061
+ _id: 1,
16062
+ nric: 1,
16063
+ block: 1,
16064
+ level: 1,
16065
+ unit: 1
16066
+ }
16067
+ },
15962
16068
  {
15963
16069
  $lookup: {
15964
- from: "building-units",
15965
- localField: "unit",
15966
- foreignField: "_id",
15967
- as: "units",
15968
- pipeline: [{ $project: { name: 1 } }]
16070
+ from: collection.collectionName,
16071
+ let: {
16072
+ vehicleId: "$_id",
16073
+ vehicleNric: "$nric",
16074
+ vehicleBlock: "$block",
16075
+ vehicleLevel: "$level",
16076
+ vehicleUnit: "$unit"
16077
+ },
16078
+ pipeline: [
16079
+ {
16080
+ $match: {
16081
+ $expr: {
16082
+ $cond: [
16083
+ {
16084
+ $and: [
16085
+ { $ne: ["$$vehicleNric", null] },
16086
+ { $ne: ["$$vehicleNric", ""] }
16087
+ ]
16088
+ },
16089
+ {
16090
+ $and: [
16091
+ { $eq: ["$nric", "$$vehicleNric"] },
16092
+ { $eq: ["$block", "$$vehicleBlock"] },
16093
+ { $eq: ["$level", "$$vehicleLevel"] },
16094
+ { $eq: ["$unit", "$$vehicleUnit"] }
16095
+ ]
16096
+ },
16097
+ { $eq: ["$_id", "$$vehicleId"] }
16098
+ ]
16099
+ }
16100
+ }
16101
+ },
16102
+ {
16103
+ $lookup: {
16104
+ from: "building-units",
16105
+ localField: "unit",
16106
+ foreignField: "_id",
16107
+ as: "units",
16108
+ pipeline: [{ $project: { name: 1 } }]
16109
+ }
16110
+ },
16111
+ {
16112
+ $unwind: { path: "$units", preserveNullAndEmptyArrays: true }
16113
+ },
16114
+ {
16115
+ $project: {
16116
+ _id: 1,
16117
+ name: 1,
16118
+ phoneNumber: 1,
16119
+ plateNumber: 1,
16120
+ nric: 1,
16121
+ block: 1,
16122
+ level: 1,
16123
+ rawUnit: "$unit",
16124
+ unit: "$units.name",
16125
+ type: 1,
16126
+ category: 1,
16127
+ status: 1,
16128
+ recNo: 1,
16129
+ start: 1,
16130
+ end: 1,
16131
+ seasonPassType: 1
16132
+ }
16133
+ },
16134
+ {
16135
+ $group: {
16136
+ _id: {
16137
+ $cond: [
16138
+ {
16139
+ $and: [
16140
+ { $ne: ["$nric", null] },
16141
+ { $ne: ["$nric", ""] }
16142
+ ]
16143
+ },
16144
+ {
16145
+ nric: "$nric",
16146
+ block: "$block",
16147
+ level: "$level",
16148
+ unit: "$unit"
16149
+ },
16150
+ "$_id"
16151
+ ]
16152
+ },
16153
+ vehicleId: { $first: "$_id" },
16154
+ name: { $first: "$name" },
16155
+ category: { $first: "$category" },
16156
+ phoneNumber: { $first: "$phoneNumber" },
16157
+ block: { $first: "$block" },
16158
+ level: { $first: "$level" },
16159
+ unit: { $first: "$unit" },
16160
+ nric: { $first: "$nric" },
16161
+ start: { $first: "$start" },
16162
+ end: { $first: "$end" },
16163
+ seasonPassType: { $first: "$seasonPassType" },
16164
+ plates: {
16165
+ $addToSet: {
16166
+ plateNumber: "$plateNumber",
16167
+ recNo: "$recNo",
16168
+ status: "$status",
16169
+ type: "$type"
16170
+ }
16171
+ }
16172
+ }
16173
+ },
16174
+ {
16175
+ $project: {
16176
+ _id: "$vehicleId",
16177
+ name: 1,
16178
+ phoneNumber: 1,
16179
+ nric: 1,
16180
+ block: 1,
16181
+ level: 1,
16182
+ unit: 1,
16183
+ type: 1,
16184
+ category: 1,
16185
+ status: 1,
16186
+ start: 1,
16187
+ end: 1,
16188
+ seasonPassType: 1,
16189
+ plates: {
16190
+ $filter: {
16191
+ input: "$plates",
16192
+ as: "plate",
16193
+ cond: {
16194
+ $and: [
16195
+ { $ne: ["$$plate.plateNumber", null] },
16196
+ { $ne: ["$$plate.plateNumber", ""] }
16197
+ ]
16198
+ }
16199
+ }
16200
+ }
16201
+ }
16202
+ }
16203
+ ],
16204
+ as: "vehicle"
15969
16205
  }
15970
16206
  },
15971
- { $unwind: { path: "$units", preserveNullAndEmptyArrays: true } },
15972
16207
  {
15973
16208
  $project: {
15974
- name: 1,
15975
- phoneNumber: 1,
15976
- plateNumber: 1,
15977
- block: 1,
15978
- level: 1,
15979
- unit: "$units.name",
15980
- type: 1,
15981
- category: 1,
15982
- status: 1,
15983
- recNo: 1,
15984
- start: 1,
15985
- end: 1,
15986
- seasonPassType: 1
16209
+ vehicle: { $arrayElemAt: ["$vehicle", 0] }
16210
+ }
16211
+ },
16212
+ {
16213
+ $replaceRoot: {
16214
+ newRoot: "$vehicle"
15987
16215
  }
15988
16216
  }
15989
16217
  ]).toArray();
15990
16218
  if (!data || !data.length) {
15991
16219
  throw new import_node_server_utils81.NotFoundError("Vehicle not found.");
15992
16220
  }
15993
- setCache(cacheKey, data[0], 15 * 60).then(() => {
16221
+ const result = data[0];
16222
+ setCache(cacheKey, result, 15 * 60).then(() => {
15994
16223
  import_node_server_utils81.logger.info(`Cache set for key: ${cacheKey}`);
15995
16224
  }).catch((err) => {
15996
16225
  import_node_server_utils81.logger.error(`Failed to set cache for key: ${cacheKey}`, err);
15997
16226
  });
15998
- return data[0];
16227
+ return result;
15999
16228
  } catch (error) {
16000
16229
  throw error;
16001
16230
  }
@@ -16129,36 +16358,34 @@ function useVehicleRepo() {
16129
16358
  async function getVehiclesByNRIC({
16130
16359
  page = 1,
16131
16360
  limit = 10,
16132
- search = "",
16361
+ nric = "",
16133
16362
  sort = {}
16134
16363
  }) {
16135
16364
  page = page > 0 ? page - 1 : 0;
16136
- const baseQuery = {
16137
- deletedAt: ""
16365
+ if (!nric) {
16366
+ throw new import_node_server_utils81.BadRequestError("NRIC is required.");
16367
+ }
16368
+ const _nric = nric.trim();
16369
+ const query = {
16370
+ deletedAt: "",
16371
+ nric: _nric
16138
16372
  };
16139
- let query = { ...baseQuery };
16140
16373
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
16141
16374
  const cacheOptions = {
16142
16375
  deletedAt: "",
16376
+ nric: _nric,
16143
16377
  page: String(page),
16144
16378
  limit: String(limit),
16145
16379
  sort: JSON.stringify(sort)
16146
16380
  };
16147
- if (search) {
16148
- query.$text = { $search: search };
16149
- cacheOptions.search = search;
16150
- }
16151
16381
  const cacheKey = (0, import_node_server_utils81.makeCacheKey)(namespace_collection, cacheOptions);
16152
16382
  const cachedData = await getCache(cacheKey);
16153
16383
  if (cachedData) {
16154
16384
  import_node_server_utils81.logger.info(`Cache hit for key: ${cacheKey}`);
16155
16385
  return cachedData;
16156
16386
  }
16157
- const escapeRegex = (input) => input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16158
16387
  try {
16159
- let items = [];
16160
- let length = 0;
16161
- items = await collection.aggregate([
16388
+ const items = await collection.aggregate([
16162
16389
  { $match: query },
16163
16390
  { $sort: sort },
16164
16391
  { $skip: page * limit },
@@ -16175,37 +16402,7 @@ function useVehicleRepo() {
16175
16402
  }
16176
16403
  }
16177
16404
  ]).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
- }
16405
+ const length = await collection.countDocuments(query);
16209
16406
  const data = (0, import_node_server_utils81.paginate)(items, page, limit, length);
16210
16407
  setCache(cacheKey, data, 15 * 60).then(() => import_node_server_utils81.logger.info(`Cache set for key: ${cacheKey}`)).catch(
16211
16408
  (err) => import_node_server_utils81.logger.error(`Failed to set cache for key: ${cacheKey}`, err)
@@ -16665,7 +16862,7 @@ function useDahuaService() {
16665
16862
  host: value.host,
16666
16863
  username: value.username,
16667
16864
  password: value.password,
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}`
16865
+ endpoint: `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&BeginTime=${value.start}&CancelTime=${value.end}&+OpenGate=true&MasterOfCar=${value.owner}`
16669
16866
  });
16670
16867
  return response;
16671
16868
  } catch (error2) {
@@ -16731,8 +16928,9 @@ function useVehicleService() {
16731
16928
  const { getById: _getById } = useOrgRepo();
16732
16929
  async function add(value) {
16733
16930
  const session = import_node_server_utils83.useAtlas.getClient()?.startSession();
16734
- if (!session)
16931
+ if (!session) {
16735
16932
  throw new Error("Unable to start session for vehicle service.");
16933
+ }
16736
16934
  const org = await _getById(value.org);
16737
16935
  if (!org)
16738
16936
  throw new import_node_server_utils83.BadRequestError("Org not found");
@@ -16780,11 +16978,12 @@ function useVehicleService() {
16780
16978
  }
16781
16979
  }
16782
16980
  const owner = String(value.name ?? "").substring(0, 15);
16981
+ const plateNumbers = value.plateNumber;
16783
16982
  try {
16784
16983
  session.startTransaction();
16785
16984
  let message = "Vehicle plate number needs approval from property management.";
16985
+ let siteCameras = [];
16786
16986
  if (value.status && value.status !== "pending" /* PENDING */) {
16787
- const siteCameras = [];
16788
16987
  let page = 1;
16789
16988
  let pages = 1;
16790
16989
  const limit = 20;
@@ -16799,40 +16998,44 @@ function useVehicleService() {
16799
16998
  pages = siteCameraReq.pages || 1;
16800
16999
  siteCameras.push(...siteCameraReq.items);
16801
17000
  page++;
16802
- } while (page < pages);
16803
- if (!siteCameras.length)
17001
+ } while (page <= pages);
17002
+ if (!siteCameras.length) {
16804
17003
  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
- );
17004
+ }
17005
+ }
17006
+ for (const plateNumber of plateNumbers) {
17007
+ const vehicleValue = {
17008
+ ...value,
17009
+ plateNumber,
17010
+ start,
17011
+ end
17012
+ };
17013
+ if (vehicleValue.status && vehicleValue.status !== "pending" /* PENDING */) {
17014
+ for (const camera of siteCameras) {
17015
+ const { host, username, password } = camera;
17016
+ const dahuaPayload = {
17017
+ host,
17018
+ username,
17019
+ password,
17020
+ plateNumber,
17021
+ mode: _mode,
17022
+ owner,
17023
+ ...startDateDahua ? { start: startDateDahua } : {},
17024
+ ...endDateDahua ? { end: endDateDahua } : {}
17025
+ };
17026
+ const dahuaResponse = await _addPlateNumber(dahuaPayload);
17027
+ if (dahuaResponse?.statusCode !== 200) {
17028
+ throw new import_node_server_utils83.BadRequestError(
17029
+ `Failed to add plate number to ANPR ${_type}`
17030
+ );
17031
+ }
17032
+ const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
17033
+ vehicleValue.recNo = responseData.split("=")[1]?.trim();
16822
17034
  }
16823
- const responseData = dahuaResponse?.data.toString("utf-8");
16824
- value.recNo = responseData.split("=")[1]?.trim();
16825
17035
  message = `Vehicle plate number added to ${_type} successfully.`;
16826
17036
  }
17037
+ await _add(vehicleValue, session);
16827
17038
  }
16828
- const formattedValue = {
16829
- ...value,
16830
- start,
16831
- // ISO string or ""
16832
- end
16833
- // ISO string or ""
16834
- };
16835
- await _add(formattedValue, session);
16836
17039
  await session.commitTransaction();
16837
17040
  return message;
16838
17041
  } catch (error) {
@@ -17037,14 +17240,14 @@ function useVehicleController() {
17037
17240
  } = useVehicleRepo();
17038
17241
  async function add(req, res, next) {
17039
17242
  const payload = req.body;
17040
- const { error } = vehicleSchema.validate(payload);
17243
+ const { error, value } = vehicleSchema.validate(payload);
17041
17244
  if (error) {
17042
17245
  import_node_server_utils84.logger.log({ level: "error", message: error.message });
17043
17246
  next(new import_node_server_utils84.BadRequestError(error.message));
17044
17247
  return;
17045
17248
  }
17046
17249
  try {
17047
- const data = await _add(payload);
17250
+ const data = await _add(value);
17048
17251
  res.status(201).json({
17049
17252
  message: "Vehicle added successfully.",
17050
17253
  data
@@ -17064,9 +17267,9 @@ function useVehicleController() {
17064
17267
  limit: import_joi46.default.number().integer().min(1).max(100).allow("", null).default(10),
17065
17268
  sort: import_joi46.default.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
17066
17269
  order: import_joi46.default.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
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("")
17270
+ type: import_joi46.default.string().optional().valid(...Object.values(VehicleType)).allow(null, ""),
17271
+ category: import_joi46.default.string().optional().valid(...Object.values(VehicleCategory)).allow(null, ""),
17272
+ status: import_joi46.default.string().optional().valid(...Object.values(VehicleStatus)).allow(null, "")
17070
17273
  });
17071
17274
  const query = { ...req.query };
17072
17275
  const { error } = validation.validate(query);
@@ -17231,7 +17434,7 @@ function useVehicleController() {
17231
17434
  const allowedFields = ["start", "end"];
17232
17435
  const allowedOrder = ["asc", "desc"];
17233
17436
  const validation = import_joi46.default.object({
17234
- search: import_joi46.default.string().optional().allow("", null),
17437
+ nric: import_joi46.default.string().optional().allow("", null),
17235
17438
  page: import_joi46.default.number().integer().min(1).allow("", null).default(1),
17236
17439
  limit: import_joi46.default.number().integer().min(1).max(100).allow("", null).default(10),
17237
17440
  sort: import_joi46.default.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
@@ -17244,7 +17447,7 @@ function useVehicleController() {
17244
17447
  next(new import_node_server_utils84.BadRequestError(error.message));
17245
17448
  return;
17246
17449
  }
17247
- const search = req.query.search ?? "";
17450
+ const nric = req.query.nric ?? "";
17248
17451
  const page = parseInt(req.query.page ?? "1");
17249
17452
  const limit = parseInt(req.query.limit ?? "10");
17250
17453
  const sortObj = {};
@@ -17258,7 +17461,7 @@ function useVehicleController() {
17258
17461
  });
17259
17462
  try {
17260
17463
  const data = await _getVehiclesByNRIC({
17261
- search,
17464
+ nric,
17262
17465
  page,
17263
17466
  limit,
17264
17467
  sort: sortObj
@@ -19859,25 +20062,15 @@ function usePersonService() {
19859
20062
  }
19860
20063
  async function plateValidity(value) {
19861
20064
  const now = /* @__PURE__ */ new Date();
19862
- if (value.type !== "resident" && value.type !== "tenant") {
19863
- return { start: "", end: "" };
19864
- }
19865
- if (value.type === "resident") {
20065
+ if (["resident" /* RESIDENT */, "tenant" /* TENANT */].includes(
20066
+ value?.type
20067
+ )) {
19866
20068
  const end = new Date(now);
19867
20069
  end.setFullYear(end.getFullYear() + 10);
19868
- return { start: now.toISOString(), end: end.toISOString() };
19869
- }
19870
- if (value.type == "tenant") {
19871
- const unit = await _getUnitById(value.unit?.toString() || "");
19872
- if (!unit) {
19873
- throw new import_node_server_utils103.BadRequestError("Building unit not found for tenant.");
19874
- }
19875
- if (unit.leaseStart && unit.leaseEnd) {
19876
- return {
19877
- start: new Date(unit.leaseStart).toISOString(),
19878
- end: new Date(unit.leaseEnd).toISOString()
19879
- };
19880
- }
20070
+ return {
20071
+ start: now.toISOString(),
20072
+ end: end.toISOString()
20073
+ };
19881
20074
  }
19882
20075
  return { start: "", end: "" };
19883
20076
  }
@@ -19896,7 +20089,8 @@ function usePersonController() {
19896
20089
  getPersonByPhoneNumber: _getPersonByPhoneNumber,
19897
20090
  getPeopleByUnit: _getPeopleByUnit,
19898
20091
  getCompany: _getCompany,
19899
- getPeopleByPlateNumber: _getPeopleByPlateNumber
20092
+ getPeopleByPlateNumber: _getPeopleByPlateNumber,
20093
+ getPeopleByNRIC: _getPeopleByNRIC
19900
20094
  } = usePersonRepo();
19901
20095
  const { add: _add, updateById: _updateById } = usePersonService();
19902
20096
  async function add(req, res, next) {
@@ -20131,6 +20325,53 @@ function usePersonController() {
20131
20325
  return;
20132
20326
  }
20133
20327
  }
20328
+ async function getPeopleByNRIC(req, res, next) {
20329
+ const allowedFields = ["start", "end"];
20330
+ const allowedOrder = ["asc", "desc"];
20331
+ const validation = import_joi58.default.object({
20332
+ nric: import_joi58.default.string().optional().allow("", null),
20333
+ page: import_joi58.default.number().integer().min(1).allow("", null).default(1),
20334
+ limit: import_joi58.default.number().integer().min(1).max(100).allow("", null).default(10),
20335
+ sort: import_joi58.default.string().pattern(/^([a-zA-Z0-9_]+)(,[a-zA-Z0-9_]+)*$/).optional().allow("", ...allowedFields),
20336
+ order: import_joi58.default.string().pattern(/^(asc|desc)(,(asc|desc))*$/).optional().allow("", ...allowedOrder),
20337
+ site: import_joi58.default.string().hex().length(24).required()
20338
+ });
20339
+ const query = { ...req.query };
20340
+ const { error } = validation.validate(query);
20341
+ if (error) {
20342
+ import_node_server_utils104.logger.log({ level: "error", message: error.message });
20343
+ next(new import_node_server_utils104.BadRequestError(error.message));
20344
+ return;
20345
+ }
20346
+ const nric = req.query.nric ?? "";
20347
+ const page = parseInt(req.query.page ?? "1");
20348
+ const limit = parseInt(req.query.limit ?? "10");
20349
+ const site = req.query.site ?? "";
20350
+ const sortObj = {};
20351
+ const sortFields = String(req.query.sort).split(",");
20352
+ const sortOrders = String(req.query.order).split(",");
20353
+ sortFields.forEach((field, index) => {
20354
+ if (allowedFields.includes(field)) {
20355
+ const order = sortOrders[index] === "asc" ? 1 : -1;
20356
+ sortObj[field] = order;
20357
+ }
20358
+ });
20359
+ try {
20360
+ const data = await _getPeopleByNRIC({
20361
+ nric,
20362
+ page,
20363
+ limit,
20364
+ sort: sortObj,
20365
+ site
20366
+ });
20367
+ res.json(data);
20368
+ return;
20369
+ } catch (error2) {
20370
+ import_node_server_utils104.logger.log({ level: "error", message: error2.message });
20371
+ next(error2);
20372
+ return;
20373
+ }
20374
+ }
20134
20375
  return {
20135
20376
  add,
20136
20377
  getAll,
@@ -20140,7 +20381,8 @@ function usePersonController() {
20140
20381
  getPersonByPhoneNumber,
20141
20382
  getPeopleByUnit,
20142
20383
  getCompany,
20143
- getPeopleByPlateNumber
20384
+ getPeopleByPlateNumber,
20385
+ getPeopleByNRIC
20144
20386
  };
20145
20387
  }
20146
20388
 
@@ -29267,7 +29509,8 @@ function UseAccessManagementRepo() {
29267
29509
  const query = {
29268
29510
  site: siteId,
29269
29511
  assignedUnit: { $ne: null },
29270
- type: "NFC" /* NFC */
29512
+ type: "NFC" /* NFC */,
29513
+ isActivated: true
29271
29514
  };
29272
29515
  if (search) {
29273
29516
  query.$or = [{ accessLevel: { $regex: search, $options: "i" } }, { cardNo: { $regex: search, $options: "i" } }];
@@ -29275,7 +29518,7 @@ function UseAccessManagementRepo() {
29275
29518
  if (type) {
29276
29519
  query.userType = type;
29277
29520
  } else {
29278
- query.userType = { $ne: "Resident/Tenant" /* RESIDENT */ };
29521
+ query.userType = { $ne: "Visitor/Resident" /* DEFAULT */ };
29279
29522
  }
29280
29523
  const res = await collection().aggregate([
29281
29524
  {
@@ -29649,6 +29892,52 @@ function UseAccessManagementRepo() {
29649
29892
  throw new Error(error.message);
29650
29893
  }
29651
29894
  }
29895
+ async function addQrTagRepo(params) {
29896
+ try {
29897
+ const { site, payload } = params;
29898
+ const id = new import_mongodb83.ObjectId(site);
29899
+ const highestCardNo = await collection().aggregate([
29900
+ { $match: { site: id, type: "NFC" /* NFC */, userType: "Visitor/Resident" /* DEFAULT */ } },
29901
+ {
29902
+ $addFields: {
29903
+ qrTagCardNoNumeric: { $toInt: "$qrTagCardNo" }
29904
+ // Convert string to integer
29905
+ }
29906
+ },
29907
+ {
29908
+ $sort: { qrTagCardNoNumeric: -1 }
29909
+ // Sort in descending order
29910
+ },
29911
+ {
29912
+ $limit: 1
29913
+ // Get the highest
29914
+ }
29915
+ ]).toArray();
29916
+ let start = 0;
29917
+ if (highestCardNo.length > 0) {
29918
+ start = highestCardNo[0].qrTagCardNoNumeric || 0;
29919
+ }
29920
+ const nextCardNumbers = Array.from({ length: payload.length }, (_, i) => String(start + i + 1).padStart(5, "0"));
29921
+ const bulkOps = await Promise.all(
29922
+ payload.map(async (doc, index) => {
29923
+ const id2 = new import_mongodb83.ObjectId(doc._id);
29924
+ return {
29925
+ updateOne: {
29926
+ filter: { _id: id2 },
29927
+ update: { $set: { qrTag: doc.qrTag, qrTagCardNo: nextCardNumbers[index] } }
29928
+ }
29929
+ };
29930
+ })
29931
+ );
29932
+ let result;
29933
+ if (bulkOps.length > 0) {
29934
+ result = await collection().bulkWrite(bulkOps);
29935
+ }
29936
+ return result;
29937
+ } catch (error) {
29938
+ throw new Error(error.message);
29939
+ }
29940
+ }
29652
29941
  return {
29653
29942
  createIndexes,
29654
29943
  createIndexForEntrypass,
@@ -29671,7 +29960,8 @@ function UseAccessManagementRepo() {
29671
29960
  bulkPhysicalAccessCardRepo,
29672
29961
  assignAccessCardToUnitRepo,
29673
29962
  deleteCardRepo,
29674
- getCardDetailsRepo
29963
+ getCardDetailsRepo,
29964
+ addQrTagRepo
29675
29965
  };
29676
29966
  }
29677
29967
 
@@ -29705,7 +29995,8 @@ function useAccessManagementSvc() {
29705
29995
  bulkPhysicalAccessCardRepo,
29706
29996
  assignAccessCardToUnitRepo,
29707
29997
  deleteCardRepo,
29708
- getCardDetailsRepo
29998
+ getCardDetailsRepo,
29999
+ addQrTagRepo
29709
30000
  } = UseAccessManagementRepo();
29710
30001
  const addPhysicalCardSvc = async (payload) => {
29711
30002
  try {
@@ -29969,6 +30260,14 @@ function useAccessManagementSvc() {
29969
30260
  throw new Error(err.message);
29970
30261
  }
29971
30262
  };
30263
+ const addQrTagSvc = async (params) => {
30264
+ try {
30265
+ const response = await addQrTagRepo({ ...params });
30266
+ return response;
30267
+ } catch (err) {
30268
+ throw new Error(err.message);
30269
+ }
30270
+ };
29972
30271
  return {
29973
30272
  addPhysicalCardSvc,
29974
30273
  addNonPhysicalCardSvc,
@@ -29995,7 +30294,8 @@ function useAccessManagementSvc() {
29995
30294
  bulkPhysicalAccessCardSvc,
29996
30295
  assignAccessCardToUnitSvc,
29997
30296
  deleteCardSvc,
29998
- getCardDetailsSvc
30297
+ getCardDetailsSvc,
30298
+ addQrTagSvc
29999
30299
  };
30000
30300
  }
30001
30301
 
@@ -30029,7 +30329,8 @@ function useAccessManagementController() {
30029
30329
  bulkPhysicalAccessCardSvc,
30030
30330
  assignAccessCardToUnitSvc,
30031
30331
  deleteCardSvc,
30032
- getCardDetailsSvc
30332
+ getCardDetailsSvc,
30333
+ addQrTagSvc
30033
30334
  } = useAccessManagementSvc();
30034
30335
  const addPhysicalCard = async (req, res) => {
30035
30336
  try {
@@ -30640,6 +30941,34 @@ function useAccessManagementController() {
30640
30941
  });
30641
30942
  }
30642
30943
  };
30944
+ const addQrTag = async (req, res) => {
30945
+ try {
30946
+ const payload = req.body;
30947
+ const site = req.params.site;
30948
+ const schema2 = import_joi85.default.object({
30949
+ site: import_joi85.default.string().hex().required(),
30950
+ payload: import_joi85.default.array().items(
30951
+ import_joi85.default.object({
30952
+ _id: import_joi85.default.string().hex().required(),
30953
+ cardNo: import_joi85.default.string().required(),
30954
+ qrTag: import_joi85.default.string().required(),
30955
+ qrTagCardNo: import_joi85.default.string().required()
30956
+ })
30957
+ ).required()
30958
+ });
30959
+ const { error } = schema2.validate({ site, payload });
30960
+ if (error) {
30961
+ return res.status(400).json({ message: error.message });
30962
+ }
30963
+ const result = await addQrTagSvc({ payload, site });
30964
+ return res.status(200).json({ message: "Success", data: result });
30965
+ } catch (error) {
30966
+ return res.status(500).json({
30967
+ data: null,
30968
+ message: error.message
30969
+ });
30970
+ }
30971
+ };
30643
30972
  return {
30644
30973
  addPhysicalCard,
30645
30974
  addNonPhysicalCard,
@@ -30663,7 +30992,8 @@ function useAccessManagementController() {
30663
30992
  bulkPhysicalAccessCard,
30664
30993
  assignAccessCardToUnit,
30665
30994
  deleteCard,
30666
- getCardDetails
30995
+ getCardDetails,
30996
+ addQrTag
30667
30997
  };
30668
30998
  }
30669
30999