@7365admin1/core 3.25.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
 
@@ -12122,19 +12148,22 @@ function useVerificationService() {
12122
12148
  orgId,
12123
12149
  siteId,
12124
12150
  siteName,
12151
+ app,
12125
12152
  inviteType
12126
12153
  }) {
12127
12154
  const schema2 = Joi11.object({
12128
12155
  email: Joi11.string().email().lowercase().required(),
12129
12156
  orgId: Joi11.string().hex().length(24).required(),
12130
12157
  siteId: Joi11.string().hex().length(24).required(),
12131
- siteName: Joi11.string().required()
12158
+ siteName: Joi11.string().required(),
12159
+ app: Joi11.string().required()
12132
12160
  });
12133
12161
  const { error } = schema2.validate({
12134
12162
  email,
12135
12163
  orgId,
12136
12164
  siteId,
12137
- siteName
12165
+ siteName,
12166
+ app
12138
12167
  });
12139
12168
  if (error) {
12140
12169
  const messages = error.details.map((d) => d.message).join(", ");
@@ -12149,7 +12178,8 @@ function useVerificationService() {
12149
12178
  metadata: {
12150
12179
  siteId,
12151
12180
  siteName,
12152
- org: orgId
12181
+ org: orgId,
12182
+ app
12153
12183
  },
12154
12184
  expireAt: new Date(
12155
12185
  (/* @__PURE__ */ new Date()).getTime() + 72 * 60 * 60 * 1e3
@@ -12169,7 +12199,7 @@ function useVerificationService() {
12169
12199
  const res = await _add(value);
12170
12200
  const dir = __dirname;
12171
12201
  const filePath = getDirectory(dir, `./public/handlebars/${value.type}`);
12172
- const link = `${APP_MAIN}/verify/${value.type}/${res}`;
12202
+ const link = `${APP_MAIN}/verify/service-provider-invite/${res}`;
12173
12203
  const emailContent = compileHandlebar({
12174
12204
  context: {
12175
12205
  email,
@@ -14763,6 +14793,7 @@ function useVerificationController() {
14763
14793
  orgId: Joi16.string().hex().required(),
14764
14794
  siteId: Joi16.string().hex().required(),
14765
14795
  siteName: Joi16.string().required(),
14796
+ app: Joi16.string().required(),
14766
14797
  inviteType: Joi16.string().valid("create-org", "organization-invite").required()
14767
14798
  });
14768
14799
  const { error } = validation.validate(payload);
@@ -14779,6 +14810,7 @@ function useVerificationController() {
14779
14810
  orgId,
14780
14811
  siteId,
14781
14812
  siteName,
14813
+ app,
14782
14814
  inviteType
14783
14815
  } = payload;
14784
14816
  try {
@@ -14787,6 +14819,7 @@ function useVerificationController() {
14787
14819
  orgId,
14788
14820
  siteId,
14789
14821
  siteName,
14822
+ app,
14790
14823
  inviteType
14791
14824
  });
14792
14825
  const cookieOptions = {
@@ -21252,7 +21285,8 @@ function MVisitorTransaction(value) {
21252
21285
  updatedBy: value.inviterId ?? ""
21253
21286
  } : null,
21254
21287
  arrivalTime: value.arrivalTime,
21255
- duration: value.duration
21288
+ duration: value.duration,
21289
+ members: value.members
21256
21290
  };
21257
21291
  }
21258
21292
 
@@ -25153,6 +25187,10 @@ function usePersonRepo() {
25153
25187
  }
25154
25188
  async function getByNRIC(value) {
25155
25189
  try {
25190
+ if (!value || value.trim() === "") {
25191
+ logger52.warn("getByNRIC called with an empty or invalid NRIC value.");
25192
+ return null;
25193
+ }
25156
25194
  const cacheKey = makeCacheKey24(site_people_namespace_collection, {
25157
25195
  nric: value
25158
25196
  });
@@ -25201,8 +25239,11 @@ function usePersonRepo() {
25201
25239
  unit
25202
25240
  }, session) {
25203
25241
  try {
25242
+ if (!unit || !ObjectId45.isValid(unit)) {
25243
+ throw new BadRequestError72("Invalid unit ID.");
25244
+ }
25204
25245
  const query = {
25205
- unit,
25246
+ unit: new ObjectId45(unit),
25206
25247
  status,
25207
25248
  ...Array.isArray(type) && type.length > 0 && {
25208
25249
  type: { $in: type }
@@ -26181,18 +26222,7 @@ function useBuildingUnitRepo() {
26181
26222
  } catch (error) {
26182
26223
  throw new BadRequestError74("Invalid ID.");
26183
26224
  }
26184
- const cacheKey = makeCacheKey25(building_units_namespace_collection, {
26185
- _id: String(_id)
26186
- });
26187
26225
  try {
26188
- const cached = await getCache(cacheKey);
26189
- if (cached) {
26190
- logger54.log({
26191
- level: "info",
26192
- message: `Cache hit for getById building unit: ${cacheKey}`
26193
- });
26194
- return cached;
26195
- }
26196
26226
  const result = await collection.findOne({
26197
26227
  _id,
26198
26228
  deletedAt: { $in: ["", null] }
@@ -26200,17 +26230,6 @@ function useBuildingUnitRepo() {
26200
26230
  if (!result) {
26201
26231
  throw new BadRequestError74("Building unit not found.");
26202
26232
  }
26203
- setCache(cacheKey, result, 300).then(() => {
26204
- logger54.log({
26205
- level: "info",
26206
- message: `Cache set for building unit by id: ${cacheKey}`
26207
- });
26208
- }).catch((err) => {
26209
- logger54.log({
26210
- level: "error",
26211
- message: `Failed to set cache for building unit by id: ${err.message}`
26212
- });
26213
- });
26214
26233
  return result;
26215
26234
  } catch (error) {
26216
26235
  if (error instanceof AppError11) {
@@ -34297,7 +34316,7 @@ var KeyRepo = class {
34297
34316
  findOptions
34298
34317
  );
34299
34318
  console.log("visitorId", visitorId.toString());
34300
- console.log("latestHistory.visitorId", latestHistory?.visitorId.toString());
34319
+ console.log("latestHistory.visitorId", latestHistory?.visitorId?.toString());
34301
34320
  if (latestHistory?.visitorId && latestHistory.visitorId?.toString() !== visitorId.toString()) {
34302
34321
  console.log("Not updating keyId", keyId);
34303
34322
  return;
@@ -34370,70 +34389,59 @@ var KeyRepo = class {
34370
34389
  return Promise.reject("Failed to delete key");
34371
34390
  }
34372
34391
  }
34373
- static async getById2(id) {
34374
- return this.collection().aggregate([
34375
- { $match: { _id: new ObjectId62(id) } },
34376
- {
34377
- $lookup: {
34378
- from: "qr-code-templates",
34379
- localField: "template",
34380
- foreignField: "_id",
34381
- as: "QRTemplateInfo"
34382
- }
34383
- },
34384
- {
34385
- $unwind: {
34386
- path: "$QRTemplateInfo",
34387
- preserveNullAndEmptyArrays: true
34388
- }
34389
- },
34390
- {
34391
- $addFields: {
34392
- prefixPass: { $toUpper: "$QRTemplateInfo.prefixPass" },
34393
- prefixKey: { $toUpper: "$QRTemplateInfo.prefixKey" },
34394
- templateName: "$QRTemplateInfo.name"
34395
- }
34396
- },
34397
- {
34398
- $addFields: {
34399
- prefixAndName: {
34400
- $cond: {
34401
- if: { $or: [{ $not: "$prefixPass" }, { $not: "$prefixKey" }] },
34402
- then: "$name",
34403
- else: {
34404
- $cond: {
34405
- if: { $eq: ["$passType", "pass-key"] },
34406
- then: { $concat: ["$prefixKey", "$name"] },
34407
- else: { $concat: ["$prefixPass", "$name"] }
34392
+ static async getById2(id, session) {
34393
+ return this.collection().aggregate(
34394
+ [
34395
+ { $match: { _id: new ObjectId62(id) } },
34396
+ {
34397
+ $lookup: {
34398
+ from: "qr-code-templates",
34399
+ localField: "template",
34400
+ foreignField: "_id",
34401
+ as: "QRTemplateInfo"
34402
+ }
34403
+ },
34404
+ {
34405
+ $unwind: {
34406
+ path: "$QRTemplateInfo",
34407
+ preserveNullAndEmptyArrays: true
34408
+ }
34409
+ },
34410
+ {
34411
+ $addFields: {
34412
+ prefixPass: { $toUpper: "$QRTemplateInfo.prefixPass" },
34413
+ prefixKey: { $toUpper: "$QRTemplateInfo.prefixKey" },
34414
+ templateName: "$QRTemplateInfo.name"
34415
+ }
34416
+ },
34417
+ {
34418
+ $addFields: {
34419
+ prefixAndName: {
34420
+ $cond: {
34421
+ if: { $or: [{ $not: "$prefixPass" }, { $not: "$prefixKey" }] },
34422
+ then: "$name",
34423
+ else: {
34424
+ $cond: {
34425
+ if: { $eq: ["$passType", "pass-key"] },
34426
+ then: { $concat: ["$prefixKey", "$name"] },
34427
+ else: { $concat: ["$prefixPass", "$name"] }
34428
+ }
34408
34429
  }
34409
34430
  }
34410
34431
  }
34411
34432
  }
34412
34433
  }
34413
- }
34414
- ]).toArray().then((results) => results.length ? results[0] : null);
34434
+ ],
34435
+ { session }
34436
+ ).toArray().then((results) => results.length ? results[0] : null);
34415
34437
  }
34416
- static async checkPassKeyAvailability(visitorPass, passKeys) {
34417
- if (Array.isArray(visitorPass) && visitorPass.length > 0) {
34418
- for (let i = 0; i < visitorPass.length; i++) {
34419
- let pass = await convertObjectIdUtil2(visitorPass[i].keyId, "Pass ID");
34420
- const getPass = await this.getById2(pass);
34421
- if (!getPass)
34422
- throw new Error("Pass not found");
34423
- if (getPass?.status && getPass?.status.toLowerCase() != "Available".toLowerCase())
34424
- throw new Error(`Pass ${getPass?.prefixAndName} is ${getPass?.status}`);
34425
- }
34426
- }
34427
- if (Array.isArray(passKeys) && passKeys.length > 0) {
34428
- for (let i = 0; i < passKeys.length; i++) {
34429
- let key = await convertObjectIdUtil2(passKeys[i].keyId, "Key ID");
34430
- const getPass = await this.getById2(key);
34431
- if (!getPass)
34432
- throw new Error(`Key not found`);
34433
- if (getPass?.status && getPass?.status.toLowerCase() != "Available".toLowerCase())
34434
- throw new Error(`Pass ${getPass?.prefixAndName} is ${getPass?.status}`);
34435
- }
34436
- }
34438
+ static async checkPassOrKeyAvailability(passOrKeyId, type, session) {
34439
+ let passOrKeyObjectId = await convertObjectIdUtil2(passOrKeyId, `${type} Id`);
34440
+ const getPassOrKey = await this.getById2(passOrKeyObjectId, session);
34441
+ if (!getPassOrKey)
34442
+ throw new Error(`${type} not found`);
34443
+ if (getPassOrKey?.status && getPassOrKey?.status.toLowerCase() != "Available".toLowerCase())
34444
+ throw new Error(`${type} ${getPassOrKey?.prefixAndName} is ${getPassOrKey?.status}`);
34437
34445
  }
34438
34446
  };
34439
34447
 
@@ -34752,7 +34760,7 @@ function useVisitorTransactionService() {
34752
34760
  const unit = await _getUnitById(value.unit);
34753
34761
  value.unitName = unit?.name;
34754
34762
  }
34755
- if (allowedPersonTypes.includes(value?.type)) {
34763
+ if (allowedPersonTypes.includes(value?.type) && value?.nric) {
34756
34764
  const nric = value?.nric || "";
34757
34765
  const person = await getByNRIC(nric);
34758
34766
  const existingCompanyName = person?.companyName?.includes(
@@ -34798,9 +34806,6 @@ function useVisitorTransactionService() {
34798
34806
  } = value;
34799
34807
  for (let i = 0; i < value.members.length; i += chunkSize) {
34800
34808
  const chunk = value.members.slice(i, i + chunkSize);
34801
- for (const member of chunk) {
34802
- await KeyRepo.checkPassKeyAvailability(member.visitorPass, member.passKeys);
34803
- }
34804
34809
  await Promise.all(
34805
34810
  chunk.map(async (member) => {
34806
34811
  const clonedMember = structuredClone(member);
@@ -34841,6 +34846,7 @@ function useVisitorTransactionService() {
34841
34846
  );
34842
34847
  console.log("visitorId service", visitorId);
34843
34848
  for (const item of preparedVisitorPass) {
34849
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
34844
34850
  await KeyRepo.updateKeyById(
34845
34851
  item.keyId,
34846
34852
  {
@@ -34856,6 +34862,7 @@ function useVisitorTransactionService() {
34856
34862
  );
34857
34863
  }
34858
34864
  for (const item of preparedPassKeys) {
34865
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
34859
34866
  await KeyRepo.updateKeyById(
34860
34867
  item.keyId,
34861
34868
  {
@@ -34898,7 +34905,6 @@ function useVisitorTransactionService() {
34898
34905
  if (found === 1)
34899
34906
  throw new BadRequestError100("This plate number is blocklisted");
34900
34907
  }
34901
- await KeyRepo.checkPassKeyAvailability(value.visitorPass, value.passKeys);
34902
34908
  if (Array.isArray(value.visitorPass)) {
34903
34909
  for (const item of value.visitorPass) {
34904
34910
  item.receivedDate = /* @__PURE__ */ new Date();
@@ -34918,6 +34924,7 @@ function useVisitorTransactionService() {
34918
34924
  const result = await _add(value, session);
34919
34925
  if (Array.isArray(value.visitorPass)) {
34920
34926
  for (const item of value.visitorPass) {
34927
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
34921
34928
  await KeyRepo.updateKeyById(
34922
34929
  item.keyId,
34923
34930
  { status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
@@ -34932,6 +34939,7 @@ function useVisitorTransactionService() {
34932
34939
  }
34933
34940
  if (Array.isArray(value.passKeys)) {
34934
34941
  for (const item of value.passKeys) {
34942
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
34935
34943
  await KeyRepo.updateKeyById(
34936
34944
  item.keyId,
34937
34945
  { status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
@@ -34984,6 +34992,103 @@ function useVisitorTransactionService() {
34984
34992
  let host;
34985
34993
  let username;
34986
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
+ }
34987
35092
  if (value.site && value.plateNumber) {
34988
35093
  try {
34989
35094
  camera = await _getVisitorsInBySite(value.site);
@@ -35017,10 +35122,6 @@ function useVisitorTransactionService() {
35017
35122
  if (found === 1)
35018
35123
  throw new BadRequestError100("This plate number is blocklisted");
35019
35124
  }
35020
- if (value.checkIn) {
35021
- const parsed = new Date(value.checkIn);
35022
- value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
35023
- }
35024
35125
  if (value.checkOut) {
35025
35126
  const parsed = new Date(value.checkOut);
35026
35127
  value.checkOut = isNaN(parsed.getTime()) ? null : parsed;
@@ -35028,6 +35129,12 @@ function useVisitorTransactionService() {
35028
35129
  if (value.site) {
35029
35130
  value.site = typeof value.site === "string" ? new ObjectId63(value.site) : value.site;
35030
35131
  }
35132
+ if (value?.block) {
35133
+ value.block = await convertObjectIdUtil2(value.block, "block Id");
35134
+ }
35135
+ if (value?.level) {
35136
+ value.level = await convertObjectIdUtil2(value.level, "level Id");
35137
+ }
35031
35138
  if (value.unit) {
35032
35139
  value.unit = typeof value.unit === "string" ? new ObjectId63(value.unit) : value.unit;
35033
35140
  const unit = await _getUnitById(value.unit);
@@ -35039,6 +35146,7 @@ function useVisitorTransactionService() {
35039
35146
  if (Array.isArray(value.visitorPass)) {
35040
35147
  for (const item of value.visitorPass) {
35041
35148
  if (item.add) {
35149
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
35042
35150
  await KeyRepo.updateKeyById(
35043
35151
  item.keyId,
35044
35152
  {
@@ -35087,6 +35195,7 @@ function useVisitorTransactionService() {
35087
35195
  if (Array.isArray(value.passKeys)) {
35088
35196
  for (const item of value.passKeys) {
35089
35197
  if (item.add) {
35198
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
35090
35199
  await KeyRepo.updateKeyById(
35091
35200
  item.keyId,
35092
35201
  {
@@ -36073,7 +36182,8 @@ function usePersonService() {
36073
36182
  getUserByEmail,
36074
36183
  updateUserFieldById: _updateUserFieldById,
36075
36184
  getUserById,
36076
- updateUserOrgById: _updateUserOrgById
36185
+ updateUserOrgById: _updateUserOrgById,
36186
+ updateUserUnitById: _updateUserUnitById
36077
36187
  } = useUserRepo();
36078
36188
  const { add: addMember } = useMemberRepo();
36079
36189
  const { getById: _getUnitById, updateById: updateUnitById } = useBuildingUnitRepo();
@@ -36220,6 +36330,10 @@ function usePersonService() {
36220
36330
  value.unit = new ObjectId66(value.unit);
36221
36331
  }
36222
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);
36223
36337
  await _updateById(_id, value, session);
36224
36338
  if (isOrgChanged && person.user) {
36225
36339
  await _updateUserOrgById(
@@ -36228,6 +36342,26 @@ function usePersonService() {
36228
36342
  session
36229
36343
  );
36230
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
+ }
36231
36365
  if (value.unit && (isNameUpdated || isOwnerChanged || value.isOwner)) {
36232
36366
  const unit = await _getUnitById(value.unit.toString());
36233
36367
  if (unit) {
@@ -36257,6 +36391,16 @@ function usePersonService() {
36257
36391
  }
36258
36392
  }
36259
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
+ }
36260
36404
  await session.commitTransaction();
36261
36405
  return "Person updated successfully.";
36262
36406
  } catch (error) {
@@ -45298,7 +45442,6 @@ function useSiteUnitBillingService() {
45298
45442
  const { getAll, updateById: _updateById } = useSiteBillingItemRepo();
45299
45443
  const { getBuildingUnitsWithOwner: _getBuildingUnitsWithOwner } = useBuildingUnitRepo();
45300
45444
  async function processBilling() {
45301
- console.log("Starting billing process...");
45302
45445
  const billing_items = await getAll({
45303
45446
  search: "",
45304
45447
  page: 1,
@@ -45341,7 +45484,7 @@ function useSiteUnitBillingService() {
45341
45484
  const buildUnitBilling = (unit) => ({
45342
45485
  site: billing_item.site.toString(),
45343
45486
  org: billing_item.org.toString(),
45344
- billItem: billing_item._id,
45487
+ billItem: billing_item._id?.toString(),
45345
45488
  billName: billing_item.name,
45346
45489
  unitId: unit._id.toString(),
45347
45490
  unit: unit.blockName + " / " + unit.levelName + " / " + unit.name,
@@ -45375,6 +45518,9 @@ function useSiteUnitBillingService() {
45375
45518
  level: "error",
45376
45519
  message: `Failed to proccess cron billing: ${error.message}`
45377
45520
  });
45521
+ if (session.inTransaction()) {
45522
+ await session.abortTransaction();
45523
+ }
45378
45524
  continue;
45379
45525
  } finally {
45380
45526
  session.endSession();
@@ -45429,6 +45575,7 @@ function useSiteUnitBillingService() {
45429
45575
  const billingMonth = Number(billing_item.month);
45430
45576
  const billingDay = Number(billing_item.date);
45431
45577
  if (billing_item.frequency === "monthly" /* MONTHLY */) {
45578
+ console.log("billing_item", billing_item.name);
45432
45579
  return todayDate === billingDay;
45433
45580
  }
45434
45581
  if (billing_item.frequency === "quarterly" /* QAURTERLY */) {
@@ -46564,7 +46711,7 @@ function UseAccessManagementRepo() {
46564
46711
  $lookup: {
46565
46712
  from: "building-levels",
46566
46713
  localField: "_id",
46567
- foreignField: "block",
46714
+ foreignField: "blockId",
46568
46715
  pipeline: [
46569
46716
  { $match: { status: { $ne: "deleted" } } },
46570
46717
  {
@@ -46585,7 +46732,7 @@ function UseAccessManagementRepo() {
46585
46732
  {
46586
46733
  $project: {
46587
46734
  _id: 1,
46588
- level: 1,
46735
+ name: 1,
46589
46736
  units: 1
46590
46737
  }
46591
46738
  }
@@ -46746,7 +46893,7 @@ function UseAccessManagementRepo() {
46746
46893
  $project: {
46747
46894
  _id: "$level.units._id",
46748
46895
  name: "$level.units.name",
46749
- level: { _id: "$level._id", level: "$level.level" },
46896
+ level: { _id: "$level._id", level: "$level.name" },
46750
46897
  block: { _id: "$_id", name: "$name", block: "$block" },
46751
46898
  site: "$site",
46752
46899
  unit_owner: { $arrayElemAt: ["$unitOwner", 0] },
@@ -48355,7 +48502,7 @@ function UseAccessManagementRepo() {
48355
48502
  $lookup: {
48356
48503
  from: "building-levels",
48357
48504
  localField: "_id",
48358
- foreignField: "block",
48505
+ foreignField: "blockId",
48359
48506
  as: "levels",
48360
48507
  pipeline: [
48361
48508
  {
@@ -50655,6 +50802,7 @@ import {
50655
50802
  InternalServerError as InternalServerError49,
50656
50803
  logger as logger126,
50657
50804
  makeCacheKey as makeCacheKey47,
50805
+ NotFoundError as NotFoundError36,
50658
50806
  paginate as paginate41,
50659
50807
  useAtlas as useAtlas79,
50660
50808
  useCache as useCache49
@@ -50849,11 +50997,24 @@ function useNfcPatrolTagRepo() {
50849
50997
  });
50850
50998
  });
50851
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
+ }
50852
51012
  return {
50853
51013
  createIndexes,
50854
51014
  add,
50855
51015
  getAll,
50856
- updateNfcPatrolTagBySite
51016
+ updateNfcPatrolTagBySite,
51017
+ getById
50857
51018
  };
50858
51019
  }
50859
51020
 
@@ -57592,7 +57753,7 @@ var schemaNfcPatrolLog = Joi110.object({
57592
57753
  checkPoints: Joi110.array().items(
57593
57754
  Joi110.object({
57594
57755
  nfcTag_id: Joi110.string().length(24).hex().required(),
57595
- nfcTag_name: Joi110.string().length(24).hex().required(),
57756
+ nfcTag_name: Joi110.string().required(),
57596
57757
  travelTime: Joi110.number().integer().min(0).required(),
57597
57758
  startDateTime: Joi110.date().required().messages({
57598
57759
  "date.base": "startDateTime must be a valid date or ISO string"
@@ -57600,7 +57761,7 @@ var schemaNfcPatrolLog = Joi110.object({
57600
57761
  endDateTime: Joi110.date().required().messages({
57601
57762
  "date.base": "endDateTime must be a valid date or ISO string"
57602
57763
  }),
57603
- status: Joi110.string().valid("Completed", "Skipped").required(),
57764
+ status: Joi110.string().valid("Pending", "Completed", "Skipped").required(),
57604
57765
  skippedRemarks: Joi110.string().required()
57605
57766
  })
57606
57767
  ).min(0).required(),
@@ -57785,19 +57946,62 @@ function useNfcPatrolLogRepo() {
57785
57946
  });
57786
57947
  });
57787
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
+ }
57788
57983
  return {
57789
57984
  createIndexes,
57790
57985
  add,
57791
- getAllBySite
57986
+ getAllBySite,
57987
+ getById,
57988
+ updateCheckpoint
57792
57989
  };
57793
57990
  }
57794
57991
 
57795
57992
  // src/services/nfc-patrol-log.service.ts
57796
57993
  import {
57994
+ BadRequestError as BadRequestError180,
57797
57995
  useAtlas as useAtlas100
57798
57996
  } from "@7365admin1/node-server-utils";
57799
57997
  function useNfcPatrolLogService() {
57800
- const { add: _add } = useNfcPatrolLogRepo();
57998
+ const {
57999
+ add: _add,
58000
+ getById,
58001
+ updateCheckpoint
58002
+ } = useNfcPatrolLogRepo();
58003
+ const routeRepo = useNfcPatrolRouteRepo();
58004
+ const tagRepo = useNfcPatrolTagRepo();
57801
58005
  async function add(value) {
57802
58006
  const session = useAtlas100.getClient()?.startSession();
57803
58007
  session?.startTransaction();
@@ -57812,8 +58016,59 @@ function useNfcPatrolLogService() {
57812
58016
  session?.endSession();
57813
58017
  }
57814
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
+ }
57815
58069
  return {
57816
- add
58070
+ add,
58071
+ completeCheckpoint
57817
58072
  };
57818
58073
  }
57819
58074
 
@@ -57824,7 +58079,7 @@ import {
57824
58079
  } from "@7365admin1/node-server-utils";
57825
58080
  import Joi111 from "joi";
57826
58081
  function useNfcPatrolLogController() {
57827
- const { add: _add } = useNfcPatrolLogService();
58082
+ const { add: _add, completeCheckpoint: _completeCheckpoint } = useNfcPatrolLogService();
57828
58083
  const { getAllBySite: _getAllBySite } = useNfcPatrolLogRepo();
57829
58084
  async function add(req, res, next) {
57830
58085
  const payload = { ...req.body };
@@ -57887,9 +58142,27 @@ function useNfcPatrolLogController() {
57887
58142
  return;
57888
58143
  }
57889
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
+ }
57890
58162
  return {
57891
58163
  add,
57892
- getAllBySite
58164
+ getAllBySite,
58165
+ completeCheckpoint
57893
58166
  };
57894
58167
  }
57895
58168
 
@@ -57951,12 +58224,9 @@ function useNewDashboardRepo() {
57951
58224
  function getDateRange(p) {
57952
58225
  const now = moment2().tz("Asia/Singapore");
57953
58226
  if (p === "thisWeek" /* THIS_WEEK */) {
57954
- const rollingStart = now.clone().subtract(6, "days").startOf("day");
57955
- const monthStart = now.clone().startOf("month");
57956
- const start = moment2.max(rollingStart, monthStart);
57957
58227
  return {
57958
- $gte: start.toDate(),
57959
- $lte: now.clone().endOf("day").toDate()
58228
+ $gte: now.clone().startOf("isoWeek").toDate(),
58229
+ $lte: now.clone().endOf("isoWeek").toDate()
57960
58230
  };
57961
58231
  }
57962
58232
  if (p === "thisMonth" /* THIS_MONTH */) {
@@ -57970,6 +58240,12 @@ function useNewDashboardRepo() {
57970
58240
  $lte: now.clone().endOf("day").toDate()
57971
58241
  };
57972
58242
  }
58243
+ function getSiteDayRange(date = moment2.tz("Asia/Singapore")) {
58244
+ return {
58245
+ $gte: date.clone().startOf("day").toDate(),
58246
+ $lte: date.clone().endOf("day").toDate()
58247
+ };
58248
+ }
57973
58249
  const calculatePercentageChange = (currentCount, previousCount) => {
57974
58250
  if (previousCount === 0) {
57975
58251
  return currentCount > 0 ? 100 : 0;
@@ -58396,24 +58672,27 @@ function useNewDashboardRepo() {
58396
58672
  const siteIdObj = toObjectId16(siteId);
58397
58673
  const startOfToday = moment2.tz("Asia/Singapore").startOf("day").toDate();
58398
58674
  const endOfToday = moment2.tz("Asia/Singapore").endOf("day").toDate();
58399
- const localTodayStr = moment2.tz("Asia/Singapore").format("YYYY-MM-DD");
58400
- const facilityTodayStart = moment2.utc(`${localTodayStr}T00:00:00.000Z`).toDate();
58401
- const facilityTodayEnd = moment2.utc(`${localTodayStr}T23:59:59.999Z`).toDate();
58402
- const localYesterdayStr = moment2.tz("Asia/Singapore").subtract(1, "day").format("YYYY-MM-DD");
58403
- const facilityYesterdayStart = moment2.utc(`${localYesterdayStr}T00:00:00.000Z`).toDate();
58404
- const facilityYesterdayEnd = moment2.utc(`${localYesterdayStr}T23:59:59.999Z`).toDate();
58405
- let facilityPeriodRange = { $gte: facilityTodayStart, $lte: facilityTodayEnd };
58675
+ const facilityTodayRange = getSiteDayRange();
58676
+ const facilityYesterdayRange = getSiteDayRange(
58677
+ moment2.tz("Asia/Singapore").subtract(1, "day")
58678
+ );
58679
+ const facilityTodayStart = facilityTodayRange.$gte;
58680
+ const facilityTodayEnd = facilityTodayRange.$lte;
58681
+ const facilityYesterdayStart = facilityYesterdayRange.$gte;
58682
+ const facilityYesterdayEnd = facilityYesterdayRange.$lte;
58683
+ let facilityPeriodRange = {
58684
+ $gte: facilityTodayStart,
58685
+ $lte: facilityTodayEnd
58686
+ };
58406
58687
  if (period === "thisWeek" /* THIS_WEEK */) {
58407
- const startStr = moment2.tz("Asia/Singapore").subtract(7, "days").format("YYYY-MM-DD");
58408
58688
  facilityPeriodRange = {
58409
- $gte: moment2.utc(`${startStr}T00:00:00.000Z`).toDate(),
58410
- $lte: facilityTodayEnd
58689
+ $gte: moment2.tz("Asia/Singapore").startOf("isoWeek").toDate(),
58690
+ $lte: moment2.tz("Asia/Singapore").endOf("isoWeek").toDate()
58411
58691
  };
58412
58692
  } else if (period === "thisMonth" /* THIS_MONTH */) {
58413
- const startStr = moment2.tz("Asia/Singapore").subtract(30, "days").format("YYYY-MM-DD");
58414
58693
  facilityPeriodRange = {
58415
- $gte: moment2.utc(`${startStr}T00:00:00.000Z`).toDate(),
58416
- $lte: facilityTodayEnd
58694
+ $gte: moment2.tz("Asia/Singapore").startOf("month").toDate(),
58695
+ $lte: moment2.tz("Asia/Singapore").endOf("month").toDate()
58417
58696
  };
58418
58697
  }
58419
58698
  const upcomingEvents = await db.collection(events_namespace_collection).find({
@@ -58497,6 +58776,18 @@ function useNewDashboardRepo() {
58497
58776
  const facilityCollection = db.collection(
58498
58777
  facility_bookings_namespace_collection2
58499
58778
  );
58779
+ const facilityBookingStatuses = [
58780
+ "Pending",
58781
+ "Approved",
58782
+ "Ongoing",
58783
+ "For Review",
58784
+ "With Balance",
58785
+ "For Refund",
58786
+ "With Refund",
58787
+ "Completed",
58788
+ "Rejected",
58789
+ "Cancelled"
58790
+ ];
58500
58791
  const [
58501
58792
  workOrderReport,
58502
58793
  yesterdayWorkOrderReport,
@@ -58647,7 +58938,7 @@ function useNewDashboardRepo() {
58647
58938
  }
58648
58939
  }
58649
58940
  ],
58650
- status: { $nin: ["deleted", "Deleted"] }
58941
+ status: { $in: facilityBookingStatuses }
58651
58942
  }
58652
58943
  },
58653
58944
  {
@@ -58672,6 +58963,25 @@ function useNewDashboardRepo() {
58672
58963
  }
58673
58964
  },
58674
58965
  { $count: "count" }
58966
+ ],
58967
+ byStatus: [
58968
+ {
58969
+ $match: {
58970
+ status: { $in: facilityBookingStatuses }
58971
+ }
58972
+ },
58973
+ {
58974
+ $group: {
58975
+ _id: {
58976
+ $cond: [
58977
+ { $eq: ["$status", "For Refund"] },
58978
+ "With Refund",
58979
+ "$status"
58980
+ ]
58981
+ },
58982
+ count: { $sum: 1 }
58983
+ }
58984
+ }
58675
58985
  ]
58676
58986
  }
58677
58987
  }
@@ -58682,7 +58992,12 @@ function useNewDashboardRepo() {
58682
58992
  site: { $in: [siteIdObj, siteId] },
58683
58993
  ...facilityMatchObj,
58684
58994
  $or: [
58685
- { date: { $gte: facilityYesterdayStart, $lte: facilityYesterdayEnd } },
58995
+ {
58996
+ date: {
58997
+ $gte: facilityYesterdayStart,
58998
+ $lte: facilityYesterdayEnd
58999
+ }
59000
+ },
58686
59001
  {
58687
59002
  date: {
58688
59003
  $gte: facilityYesterdayStart.toISOString(),
@@ -58690,7 +59005,7 @@ function useNewDashboardRepo() {
58690
59005
  }
58691
59006
  }
58692
59007
  ],
58693
- status: { $nin: ["deleted", "Deleted"] }
59008
+ status: { $in: facilityBookingStatuses }
58694
59009
  }
58695
59010
  },
58696
59011
  { $count: "count" }
@@ -58709,7 +59024,7 @@ function useNewDashboardRepo() {
58709
59024
  }
58710
59025
  }
58711
59026
  ],
58712
- status: { $nin: ["deleted", "Deleted"] }
59027
+ status: { $in: facilityBookingStatuses }
58713
59028
  }
58714
59029
  },
58715
59030
  { $count: "count" }
@@ -58733,8 +59048,25 @@ function useNewDashboardRepo() {
58733
59048
  const fFacet = facilityReport[0] ?? {
58734
59049
  total: [],
58735
59050
  ongoing: [],
58736
- waitingApproval: []
59051
+ waitingApproval: [],
59052
+ byStatus: []
59053
+ };
59054
+ const facilityBookingStatusCounts = {
59055
+ Pending: 0,
59056
+ Approved: 0,
59057
+ Ongoing: 0,
59058
+ "For Review": 0,
59059
+ "With Balance": 0,
59060
+ "With Refund": 0,
59061
+ Completed: 0,
59062
+ Rejected: 0,
59063
+ Cancelled: 0
58737
59064
  };
59065
+ for (const item of fFacet.byStatus ?? []) {
59066
+ if (typeof item?._id === "string" && item._id in facilityBookingStatusCounts) {
59067
+ facilityBookingStatusCounts[item._id] = item.count ?? 0;
59068
+ }
59069
+ }
58738
59070
  const workOrderStatus = {
58739
59071
  pending: 0,
58740
59072
  inProgress: 0,
@@ -58781,6 +59113,7 @@ function useNewDashboardRepo() {
58781
59113
  count: fFacet.total[0]?.count ?? 0,
58782
59114
  ongoing: fFacet.ongoing[0]?.count ?? 0,
58783
59115
  waitingApproval: fFacet.waitingApproval[0]?.count ?? 0,
59116
+ byStatus: facilityBookingStatusCounts,
58784
59117
  percentage: calculatePercentageChange(
58785
59118
  todayFacilityReport[0]?.count ?? 0,
58786
59119
  yesterdayFacilityReport[0]?.count ?? 0
@@ -63568,9 +63901,12 @@ var useRedDotPaymentRepo = () => {
63568
63901
  }
63569
63902
  const now = /* @__PURE__ */ new Date();
63570
63903
  const unitUpdate = {
63904
+ method: payload.method,
63905
+ message: payload.message,
63571
63906
  updatedAt: now.toISOString(),
63572
63907
  paymentStatus: payload.paymentStatus,
63573
- transaction_id: payload.transaction_id
63908
+ transaction_id: payload.transaction_id,
63909
+ paidBy: paymentInfo.paidBy
63574
63910
  };
63575
63911
  const paymentUpdate = {
63576
63912
  updatedAt: now.toISOString(),