@7365admin1/core 3.26.0 → 3.27.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
@@ -8676,6 +8676,31 @@ function useUserRepo() {
8676
8676
  throw new InternalServerError5("Failed to update user organization.");
8677
8677
  }
8678
8678
  }
8679
+ async function updateUserUnitById(id, value, session) {
8680
+ const _id = toObjectId(id);
8681
+ const update = {};
8682
+ if ("block" in value)
8683
+ update.block = value.block ?? null;
8684
+ if ("level" in value)
8685
+ update.level = value.level ?? null;
8686
+ if ("unitId" in value)
8687
+ update.unitId = value.unitId ?? null;
8688
+ if ("unitName" in value)
8689
+ update.unitName = value.unitName ?? "";
8690
+ if (Object.keys(update).length === 0) {
8691
+ return "No user unit fields to update.";
8692
+ }
8693
+ try {
8694
+ await collection.updateOne(
8695
+ { _id },
8696
+ { $set: { ...update, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
8697
+ { session }
8698
+ );
8699
+ return "Successfully updated user unit information.";
8700
+ } catch (error) {
8701
+ throw new InternalServerError5("Failed to update user unit information.");
8702
+ }
8703
+ }
8679
8704
  return {
8680
8705
  createIndex,
8681
8706
  createTextIndex,
@@ -8694,7 +8719,8 @@ function useUserRepo() {
8694
8719
  getUserByEmailStatus,
8695
8720
  updateUserSIDById,
8696
8721
  resetPassword,
8697
- updateUserOrgById
8722
+ updateUserOrgById,
8723
+ updateUserUnitById
8698
8724
  };
8699
8725
  }
8700
8726
 
@@ -25161,6 +25187,10 @@ function usePersonRepo() {
25161
25187
  }
25162
25188
  async function getByNRIC(value) {
25163
25189
  try {
25190
+ if (!value || value.trim() === "") {
25191
+ logger52.warn("getByNRIC called with an empty or invalid NRIC value.");
25192
+ return null;
25193
+ }
25164
25194
  const cacheKey = makeCacheKey24(site_people_namespace_collection, {
25165
25195
  nric: value
25166
25196
  });
@@ -25209,8 +25239,11 @@ function usePersonRepo() {
25209
25239
  unit
25210
25240
  }, session) {
25211
25241
  try {
25242
+ if (!unit || !ObjectId45.isValid(unit)) {
25243
+ throw new BadRequestError72("Invalid unit ID.");
25244
+ }
25212
25245
  const query = {
25213
- unit,
25246
+ unit: new ObjectId45(unit),
25214
25247
  status,
25215
25248
  ...Array.isArray(type) && type.length > 0 && {
25216
25249
  type: { $in: type }
@@ -26189,18 +26222,7 @@ function useBuildingUnitRepo() {
26189
26222
  } catch (error) {
26190
26223
  throw new BadRequestError74("Invalid ID.");
26191
26224
  }
26192
- const cacheKey = makeCacheKey25(building_units_namespace_collection, {
26193
- _id: String(_id)
26194
- });
26195
26225
  try {
26196
- const cached = await getCache(cacheKey);
26197
- if (cached) {
26198
- logger54.log({
26199
- level: "info",
26200
- message: `Cache hit for getById building unit: ${cacheKey}`
26201
- });
26202
- return cached;
26203
- }
26204
26226
  const result = await collection.findOne({
26205
26227
  _id,
26206
26228
  deletedAt: { $in: ["", null] }
@@ -26208,17 +26230,6 @@ function useBuildingUnitRepo() {
26208
26230
  if (!result) {
26209
26231
  throw new BadRequestError74("Building unit not found.");
26210
26232
  }
26211
- setCache(cacheKey, result, 300).then(() => {
26212
- logger54.log({
26213
- level: "info",
26214
- message: `Cache set for building unit by id: ${cacheKey}`
26215
- });
26216
- }).catch((err) => {
26217
- logger54.log({
26218
- level: "error",
26219
- message: `Failed to set cache for building unit by id: ${err.message}`
26220
- });
26221
- });
26222
26233
  return result;
26223
26234
  } catch (error) {
26224
26235
  if (error instanceof AppError11) {
@@ -34749,7 +34760,7 @@ function useVisitorTransactionService() {
34749
34760
  const unit = await _getUnitById(value.unit);
34750
34761
  value.unitName = unit?.name;
34751
34762
  }
34752
- if (allowedPersonTypes.includes(value?.type)) {
34763
+ if (allowedPersonTypes.includes(value?.type) && value?.nric) {
34753
34764
  const nric = value?.nric || "";
34754
34765
  const person = await getByNRIC(nric);
34755
34766
  const existingCompanyName = person?.companyName?.includes(
@@ -34981,6 +34992,103 @@ function useVisitorTransactionService() {
34981
34992
  let host;
34982
34993
  let username;
34983
34994
  let password;
34995
+ if (value.checkIn) {
34996
+ const parsed = new Date(value.checkIn);
34997
+ value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
34998
+ }
34999
+ if (value.isMembersAdded != true && Array.isArray(value.members) && value.members.length > 0) {
35000
+ const chunkSize = 10;
35001
+ const {
35002
+ block,
35003
+ level,
35004
+ unit,
35005
+ site,
35006
+ org,
35007
+ type,
35008
+ company,
35009
+ remarks,
35010
+ contractorType,
35011
+ unitName
35012
+ } = value;
35013
+ for (let i = 0; i < value.members.length; i += chunkSize) {
35014
+ const chunk = value.members.slice(i, i + chunkSize);
35015
+ await Promise.all(
35016
+ chunk.map(async (member) => {
35017
+ const clonedMember = structuredClone(member);
35018
+ const { visitorPass, passKeys } = clonedMember;
35019
+ const preparedVisitorPass = Array.isArray(visitorPass) ? visitorPass.map((item) => ({
35020
+ ...item,
35021
+ receivedDate: /* @__PURE__ */ new Date(),
35022
+ status: "Not Returned" /* NOT_RETURNED */,
35023
+ lastUpdate: null,
35024
+ remarks: ""
35025
+ })) : [];
35026
+ const preparedPassKeys = Array.isArray(passKeys) ? passKeys.map((item) => ({
35027
+ ...item,
35028
+ receivedDate: /* @__PURE__ */ new Date(),
35029
+ status: "Not Returned" /* NOT_RETURNED */,
35030
+ lastUpdate: null,
35031
+ remarks: ""
35032
+ })) : [];
35033
+ const visitorId = await _add(
35034
+ {
35035
+ ...clonedMember,
35036
+ block,
35037
+ level,
35038
+ unit,
35039
+ unitName,
35040
+ site,
35041
+ org,
35042
+ type,
35043
+ company,
35044
+ remarks,
35045
+ contractorType,
35046
+ checkIn: value.checkIn,
35047
+ visitorPass: preparedVisitorPass,
35048
+ passKeys: preparedPassKeys,
35049
+ status: "registered" /* REGISTERED */
35050
+ },
35051
+ session
35052
+ );
35053
+ console.log("visitorId service", visitorId);
35054
+ for (const item of preparedVisitorPass) {
35055
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
35056
+ await KeyRepo.updateKeyById(
35057
+ item.keyId,
35058
+ {
35059
+ status: "In Use" /* IN_USE */,
35060
+ updatedBy: value.createdBy
35061
+ },
35062
+ value.site,
35063
+ session,
35064
+ void 0,
35065
+ visitorId,
35066
+ void 0,
35067
+ true
35068
+ );
35069
+ }
35070
+ for (const item of preparedPassKeys) {
35071
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
35072
+ await KeyRepo.updateKeyById(
35073
+ item.keyId,
35074
+ {
35075
+ status: "In Use" /* IN_USE */,
35076
+ updatedBy: value.createdBy,
35077
+ visitorId
35078
+ },
35079
+ value.site,
35080
+ session,
35081
+ void 0,
35082
+ visitorId,
35083
+ void 0,
35084
+ true
35085
+ );
35086
+ }
35087
+ })
35088
+ );
35089
+ }
35090
+ value.isMembersAdded = true;
35091
+ }
34984
35092
  if (value.site && value.plateNumber) {
34985
35093
  try {
34986
35094
  camera = await _getVisitorsInBySite(value.site);
@@ -35014,10 +35122,6 @@ function useVisitorTransactionService() {
35014
35122
  if (found === 1)
35015
35123
  throw new BadRequestError100("This plate number is blocklisted");
35016
35124
  }
35017
- if (value.checkIn) {
35018
- const parsed = new Date(value.checkIn);
35019
- value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
35020
- }
35021
35125
  if (value.checkOut) {
35022
35126
  const parsed = new Date(value.checkOut);
35023
35127
  value.checkOut = isNaN(parsed.getTime()) ? null : parsed;
@@ -36078,7 +36182,8 @@ function usePersonService() {
36078
36182
  getUserByEmail,
36079
36183
  updateUserFieldById: _updateUserFieldById,
36080
36184
  getUserById,
36081
- updateUserOrgById: _updateUserOrgById
36185
+ updateUserOrgById: _updateUserOrgById,
36186
+ updateUserUnitById: _updateUserUnitById
36082
36187
  } = useUserRepo();
36083
36188
  const { add: addMember } = useMemberRepo();
36084
36189
  const { getById: _getUnitById, updateById: updateUnitById } = useBuildingUnitRepo();
@@ -36225,6 +36330,10 @@ function usePersonService() {
36225
36330
  value.unit = new ObjectId66(value.unit);
36226
36331
  }
36227
36332
  const isOrgChanged = value.org && person.org?.toString() !== value.org.toString();
36333
+ const toKey = (v) => v === null || v === void 0 || v === "" ? "" : v.toString();
36334
+ const isBlockChanged = "block" in value && toKey(value.block) !== toKey(person.block);
36335
+ const isLevelChanged = "level" in value && toKey(value.level) !== toKey(person.level);
36336
+ const isUnitChanged = "unit" in value && toKey(value.unit) !== toKey(person.unit);
36228
36337
  await _updateById(_id, value, session);
36229
36338
  if (isOrgChanged && person.user) {
36230
36339
  await _updateUserOrgById(
@@ -36233,6 +36342,26 @@ function usePersonService() {
36233
36342
  session
36234
36343
  );
36235
36344
  }
36345
+ if ((isBlockChanged || isLevelChanged || isUnitChanged) && person.user) {
36346
+ const userUnitPayload = {};
36347
+ if (isBlockChanged) {
36348
+ userUnitPayload.block = value.block ?? null;
36349
+ }
36350
+ if (isLevelChanged) {
36351
+ userUnitPayload.level = value.level ?? null;
36352
+ }
36353
+ if (isUnitChanged) {
36354
+ userUnitPayload.unitId = value.unit ?? null;
36355
+ if ("unitName" in value) {
36356
+ userUnitPayload.unitName = value.unitName ?? "";
36357
+ }
36358
+ }
36359
+ await _updateUserUnitById(
36360
+ person.user.toString(),
36361
+ userUnitPayload,
36362
+ session
36363
+ );
36364
+ }
36236
36365
  if (value.unit && (isNameUpdated || isOwnerChanged || value.isOwner)) {
36237
36366
  const unit = await _getUnitById(value.unit.toString());
36238
36367
  if (unit) {
@@ -36262,6 +36391,16 @@ function usePersonService() {
36262
36391
  }
36263
36392
  }
36264
36393
  }
36394
+ if (isUnitChanged && person.isOwner && person.unit) {
36395
+ const previousUnit = await _getUnitById(person.unit.toString());
36396
+ if (previousUnit && previousUnit._id && previousUnit.owner?.toString() === person?._id?.toString()) {
36397
+ await updateUnitById(
36398
+ previousUnit._id.toString(),
36399
+ { owner: "", ownerName: "" },
36400
+ session
36401
+ );
36402
+ }
36403
+ }
36265
36404
  await session.commitTransaction();
36266
36405
  return "Person updated successfully.";
36267
36406
  } catch (error) {
@@ -45303,7 +45442,6 @@ function useSiteUnitBillingService() {
45303
45442
  const { getAll, updateById: _updateById } = useSiteBillingItemRepo();
45304
45443
  const { getBuildingUnitsWithOwner: _getBuildingUnitsWithOwner } = useBuildingUnitRepo();
45305
45444
  async function processBilling() {
45306
- console.log("Starting billing process...");
45307
45445
  const billing_items = await getAll({
45308
45446
  search: "",
45309
45447
  page: 1,
@@ -45346,7 +45484,7 @@ function useSiteUnitBillingService() {
45346
45484
  const buildUnitBilling = (unit) => ({
45347
45485
  site: billing_item.site.toString(),
45348
45486
  org: billing_item.org.toString(),
45349
- billItem: billing_item._id,
45487
+ billItem: billing_item._id?.toString(),
45350
45488
  billName: billing_item.name,
45351
45489
  unitId: unit._id.toString(),
45352
45490
  unit: unit.blockName + " / " + unit.levelName + " / " + unit.name,
@@ -45380,6 +45518,9 @@ function useSiteUnitBillingService() {
45380
45518
  level: "error",
45381
45519
  message: `Failed to proccess cron billing: ${error.message}`
45382
45520
  });
45521
+ if (session.inTransaction()) {
45522
+ await session.abortTransaction();
45523
+ }
45383
45524
  continue;
45384
45525
  } finally {
45385
45526
  session.endSession();
@@ -45434,6 +45575,7 @@ function useSiteUnitBillingService() {
45434
45575
  const billingMonth = Number(billing_item.month);
45435
45576
  const billingDay = Number(billing_item.date);
45436
45577
  if (billing_item.frequency === "monthly" /* MONTHLY */) {
45578
+ console.log("billing_item", billing_item.name);
45437
45579
  return todayDate === billingDay;
45438
45580
  }
45439
45581
  if (billing_item.frequency === "quarterly" /* QAURTERLY */) {
@@ -46569,7 +46711,7 @@ function UseAccessManagementRepo() {
46569
46711
  $lookup: {
46570
46712
  from: "building-levels",
46571
46713
  localField: "_id",
46572
- foreignField: "block",
46714
+ foreignField: "blockId",
46573
46715
  pipeline: [
46574
46716
  { $match: { status: { $ne: "deleted" } } },
46575
46717
  {
@@ -46590,7 +46732,7 @@ function UseAccessManagementRepo() {
46590
46732
  {
46591
46733
  $project: {
46592
46734
  _id: 1,
46593
- level: 1,
46735
+ name: 1,
46594
46736
  units: 1
46595
46737
  }
46596
46738
  }
@@ -46751,7 +46893,7 @@ function UseAccessManagementRepo() {
46751
46893
  $project: {
46752
46894
  _id: "$level.units._id",
46753
46895
  name: "$level.units.name",
46754
- level: { _id: "$level._id", level: "$level.level" },
46896
+ level: { _id: "$level._id", level: "$level.name" },
46755
46897
  block: { _id: "$_id", name: "$name", block: "$block" },
46756
46898
  site: "$site",
46757
46899
  unit_owner: { $arrayElemAt: ["$unitOwner", 0] },
@@ -48360,7 +48502,7 @@ function UseAccessManagementRepo() {
48360
48502
  $lookup: {
48361
48503
  from: "building-levels",
48362
48504
  localField: "_id",
48363
- foreignField: "block",
48505
+ foreignField: "blockId",
48364
48506
  as: "levels",
48365
48507
  pipeline: [
48366
48508
  {
@@ -50660,6 +50802,7 @@ import {
50660
50802
  InternalServerError as InternalServerError49,
50661
50803
  logger as logger126,
50662
50804
  makeCacheKey as makeCacheKey47,
50805
+ NotFoundError as NotFoundError36,
50663
50806
  paginate as paginate41,
50664
50807
  useAtlas as useAtlas79,
50665
50808
  useCache as useCache49
@@ -50854,11 +50997,24 @@ function useNfcPatrolTagRepo() {
50854
50997
  });
50855
50998
  });
50856
50999
  }
51000
+ async function getById(_id, session) {
51001
+ try {
51002
+ _id = typeof _id === "string" ? new ObjectId99(_id) : _id;
51003
+ } catch {
51004
+ throw new BadRequestError147("Invalid NFC Patrol Tag ID.");
51005
+ }
51006
+ const tag = await collection.findOne({ _id }, { session });
51007
+ if (!tag) {
51008
+ throw new NotFoundError36("NFC Patrol Tag not found.");
51009
+ }
51010
+ return tag;
51011
+ }
50857
51012
  return {
50858
51013
  createIndexes,
50859
51014
  add,
50860
51015
  getAll,
50861
- updateNfcPatrolTagBySite
51016
+ updateNfcPatrolTagBySite,
51017
+ getById
50862
51018
  };
50863
51019
  }
50864
51020
 
@@ -57597,7 +57753,7 @@ var schemaNfcPatrolLog = Joi110.object({
57597
57753
  checkPoints: Joi110.array().items(
57598
57754
  Joi110.object({
57599
57755
  nfcTag_id: Joi110.string().length(24).hex().required(),
57600
- nfcTag_name: Joi110.string().length(24).hex().required(),
57756
+ nfcTag_name: Joi110.string().required(),
57601
57757
  travelTime: Joi110.number().integer().min(0).required(),
57602
57758
  startDateTime: Joi110.date().required().messages({
57603
57759
  "date.base": "startDateTime must be a valid date or ISO string"
@@ -57605,7 +57761,7 @@ var schemaNfcPatrolLog = Joi110.object({
57605
57761
  endDateTime: Joi110.date().required().messages({
57606
57762
  "date.base": "endDateTime must be a valid date or ISO string"
57607
57763
  }),
57608
- status: Joi110.string().valid("Completed", "Skipped").required(),
57764
+ status: Joi110.string().valid("Pending", "Completed", "Skipped").required(),
57609
57765
  skippedRemarks: Joi110.string().required()
57610
57766
  })
57611
57767
  ).min(0).required(),
@@ -57790,19 +57946,62 @@ function useNfcPatrolLogRepo() {
57790
57946
  });
57791
57947
  });
57792
57948
  }
57949
+ async function getById(id, session) {
57950
+ try {
57951
+ id = new ObjectId121(id);
57952
+ } catch {
57953
+ throw new BadRequestError179("Invalid Log ID.");
57954
+ }
57955
+ return collection.findOne(
57956
+ { _id: id },
57957
+ { session }
57958
+ );
57959
+ }
57960
+ async function updateCheckpoint(id, checkpointIndex, payload, session) {
57961
+ try {
57962
+ id = new ObjectId121(id);
57963
+ } catch {
57964
+ throw new BadRequestError179("Invalid Log ID.");
57965
+ }
57966
+ const setData = {
57967
+ [`checkPoints.${checkpointIndex}.status`]: payload.action === "complete" ? "Completed" : "Skipped",
57968
+ [`checkPoints.${checkpointIndex}.endDateTime`]: /* @__PURE__ */ new Date()
57969
+ };
57970
+ if (payload.action === "skip") {
57971
+ setData[`checkPoints.${checkpointIndex}.skippedRemarks`] = payload.skippedRemarks;
57972
+ }
57973
+ const res = await collection.updateOne(
57974
+ { _id: id },
57975
+ {
57976
+ $set: setData
57977
+ },
57978
+ { session }
57979
+ );
57980
+ delCachedData();
57981
+ return res;
57982
+ }
57793
57983
  return {
57794
57984
  createIndexes,
57795
57985
  add,
57796
- getAllBySite
57986
+ getAllBySite,
57987
+ getById,
57988
+ updateCheckpoint
57797
57989
  };
57798
57990
  }
57799
57991
 
57800
57992
  // src/services/nfc-patrol-log.service.ts
57801
57993
  import {
57994
+ BadRequestError as BadRequestError180,
57802
57995
  useAtlas as useAtlas100
57803
57996
  } from "@7365admin1/node-server-utils";
57804
57997
  function useNfcPatrolLogService() {
57805
- const { add: _add } = useNfcPatrolLogRepo();
57998
+ const {
57999
+ add: _add,
58000
+ getById,
58001
+ updateCheckpoint
58002
+ } = useNfcPatrolLogRepo();
58003
+ const routeRepo = useNfcPatrolRouteRepo();
58004
+ const tagRepo = useNfcPatrolTagRepo();
57806
58005
  async function add(value) {
57807
58006
  const session = useAtlas100.getClient()?.startSession();
57808
58007
  session?.startTransaction();
@@ -57817,8 +58016,59 @@ function useNfcPatrolLogService() {
57817
58016
  session?.endSession();
57818
58017
  }
57819
58018
  }
58019
+ async function completeCheckpoint(logId, payload) {
58020
+ const session = useAtlas100.getClient()?.startSession();
58021
+ session?.startTransaction();
58022
+ try {
58023
+ const log = await getById(logId, session);
58024
+ if (!log) {
58025
+ throw new BadRequestError180("Patrol Log not found.");
58026
+ }
58027
+ const route = await routeRepo.getById(log.route._id);
58028
+ if (!route) {
58029
+ throw new BadRequestError180("Route not found.");
58030
+ }
58031
+ const checkpoint = route.checkPoints[payload.checkpointIndex];
58032
+ if (!checkpoint) {
58033
+ throw new BadRequestError180("Checkpoint not found.");
58034
+ }
58035
+ const logCheckpoint = log.checkPoints[payload.checkpointIndex];
58036
+ if (!logCheckpoint) {
58037
+ throw new BadRequestError180("Checkpoint log not found.");
58038
+ }
58039
+ if (logCheckpoint.status === "Completed") {
58040
+ throw new BadRequestError180("Checkpoint already completed.");
58041
+ }
58042
+ if (payload.action === "skip" && !payload.skippedRemarks?.trim()) {
58043
+ throw new BadRequestError180("Skipped remarks are required.");
58044
+ }
58045
+ if (payload.action === "complete") {
58046
+ const tag = await tagRepo.getById(checkpoint.nfcTag_id, session);
58047
+ if (!tag.tagUID) {
58048
+ throw new BadRequestError180("NFC Tag has not been configured.");
58049
+ }
58050
+ if (tag.tagUID !== payload.uid) {
58051
+ throw new BadRequestError180("Invalid NFC Tag.");
58052
+ }
58053
+ }
58054
+ await updateCheckpoint(
58055
+ logId,
58056
+ payload.checkpointIndex,
58057
+ payload,
58058
+ session
58059
+ );
58060
+ await session?.commitTransaction();
58061
+ return "Checkpoint updated.";
58062
+ } catch (error) {
58063
+ await session?.abortTransaction();
58064
+ throw error;
58065
+ } finally {
58066
+ session?.endSession();
58067
+ }
58068
+ }
57820
58069
  return {
57821
- add
58070
+ add,
58071
+ completeCheckpoint
57822
58072
  };
57823
58073
  }
57824
58074
 
@@ -57829,7 +58079,7 @@ import {
57829
58079
  } from "@7365admin1/node-server-utils";
57830
58080
  import Joi111 from "joi";
57831
58081
  function useNfcPatrolLogController() {
57832
- const { add: _add } = useNfcPatrolLogService();
58082
+ const { add: _add, completeCheckpoint: _completeCheckpoint } = useNfcPatrolLogService();
57833
58083
  const { getAllBySite: _getAllBySite } = useNfcPatrolLogRepo();
57834
58084
  async function add(req, res, next) {
57835
58085
  const payload = { ...req.body };
@@ -57892,9 +58142,27 @@ function useNfcPatrolLogController() {
57892
58142
  return;
57893
58143
  }
57894
58144
  }
58145
+ async function completeCheckpoint(req, res, next) {
58146
+ const { id } = req.params;
58147
+ const payload = {
58148
+ checkpointIndex: req.body.checkpointIndex,
58149
+ uid: req.body.uid,
58150
+ action: req.body.action,
58151
+ skippedRemarks: req.body.skippedRemarks
58152
+ };
58153
+ try {
58154
+ const data = await _completeCheckpoint(id, payload);
58155
+ res.status(200).json(data);
58156
+ return;
58157
+ } catch (error) {
58158
+ logger159.error(error.message);
58159
+ next(error);
58160
+ }
58161
+ }
57895
58162
  return {
57896
58163
  add,
57897
- getAllBySite
58164
+ getAllBySite,
58165
+ completeCheckpoint
57898
58166
  };
57899
58167
  }
57900
58168