@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.js CHANGED
@@ -9141,6 +9141,31 @@ function useUserRepo() {
9141
9141
  throw new import_node_server_utils10.InternalServerError("Failed to update user organization.");
9142
9142
  }
9143
9143
  }
9144
+ async function updateUserUnitById(id, value, session) {
9145
+ const _id = (0, import_node_server_utils10.toObjectId)(id);
9146
+ const update = {};
9147
+ if ("block" in value)
9148
+ update.block = value.block ?? null;
9149
+ if ("level" in value)
9150
+ update.level = value.level ?? null;
9151
+ if ("unitId" in value)
9152
+ update.unitId = value.unitId ?? null;
9153
+ if ("unitName" in value)
9154
+ update.unitName = value.unitName ?? "";
9155
+ if (Object.keys(update).length === 0) {
9156
+ return "No user unit fields to update.";
9157
+ }
9158
+ try {
9159
+ await collection.updateOne(
9160
+ { _id },
9161
+ { $set: { ...update, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
9162
+ { session }
9163
+ );
9164
+ return "Successfully updated user unit information.";
9165
+ } catch (error) {
9166
+ throw new import_node_server_utils10.InternalServerError("Failed to update user unit information.");
9167
+ }
9168
+ }
9144
9169
  return {
9145
9170
  createIndex,
9146
9171
  createTextIndex,
@@ -9159,7 +9184,8 @@ function useUserRepo() {
9159
9184
  getUserByEmailStatus,
9160
9185
  updateUserSIDById,
9161
9186
  resetPassword,
9162
- updateUserOrgById
9187
+ updateUserOrgById,
9188
+ updateUserUnitById
9163
9189
  };
9164
9190
  }
9165
9191
 
@@ -12520,19 +12546,22 @@ function useVerificationService() {
12520
12546
  orgId,
12521
12547
  siteId,
12522
12548
  siteName,
12549
+ app,
12523
12550
  inviteType
12524
12551
  }) {
12525
12552
  const schema2 = import_joi11.default.object({
12526
12553
  email: import_joi11.default.string().email().lowercase().required(),
12527
12554
  orgId: import_joi11.default.string().hex().length(24).required(),
12528
12555
  siteId: import_joi11.default.string().hex().length(24).required(),
12529
- siteName: import_joi11.default.string().required()
12556
+ siteName: import_joi11.default.string().required(),
12557
+ app: import_joi11.default.string().required()
12530
12558
  });
12531
12559
  const { error } = schema2.validate({
12532
12560
  email,
12533
12561
  orgId,
12534
12562
  siteId,
12535
- siteName
12563
+ siteName,
12564
+ app
12536
12565
  });
12537
12566
  if (error) {
12538
12567
  const messages = error.details.map((d) => d.message).join(", ");
@@ -12547,7 +12576,8 @@ function useVerificationService() {
12547
12576
  metadata: {
12548
12577
  siteId,
12549
12578
  siteName,
12550
- org: orgId
12579
+ org: orgId,
12580
+ app
12551
12581
  },
12552
12582
  expireAt: new Date(
12553
12583
  (/* @__PURE__ */ new Date()).getTime() + 72 * 60 * 60 * 1e3
@@ -12567,7 +12597,7 @@ function useVerificationService() {
12567
12597
  const res = await _add(value);
12568
12598
  const dir = __dirname;
12569
12599
  const filePath = (0, import_node_server_utils21.getDirectory)(dir, `./public/handlebars/${value.type}`);
12570
- const link = `${APP_MAIN}/verify/${value.type}/${res}`;
12600
+ const link = `${APP_MAIN}/verify/service-provider-invite/${res}`;
12571
12601
  const emailContent = (0, import_node_server_utils21.compileHandlebar)({
12572
12602
  context: {
12573
12603
  email,
@@ -15136,6 +15166,7 @@ function useVerificationController() {
15136
15166
  orgId: import_joi16.default.string().hex().required(),
15137
15167
  siteId: import_joi16.default.string().hex().required(),
15138
15168
  siteName: import_joi16.default.string().required(),
15169
+ app: import_joi16.default.string().required(),
15139
15170
  inviteType: import_joi16.default.string().valid("create-org", "organization-invite").required()
15140
15171
  });
15141
15172
  const { error } = validation.validate(payload);
@@ -15152,6 +15183,7 @@ function useVerificationController() {
15152
15183
  orgId,
15153
15184
  siteId,
15154
15185
  siteName,
15186
+ app,
15155
15187
  inviteType
15156
15188
  } = payload;
15157
15189
  try {
@@ -15160,6 +15192,7 @@ function useVerificationController() {
15160
15192
  orgId,
15161
15193
  siteId,
15162
15194
  siteName,
15195
+ app,
15163
15196
  inviteType
15164
15197
  });
15165
15198
  const cookieOptions = {
@@ -21515,7 +21548,8 @@ function MVisitorTransaction(value) {
21515
21548
  updatedBy: value.inviterId ?? ""
21516
21549
  } : null,
21517
21550
  arrivalTime: value.arrivalTime,
21518
- duration: value.duration
21551
+ duration: value.duration,
21552
+ members: value.members
21519
21553
  };
21520
21554
  }
21521
21555
 
@@ -25393,6 +25427,10 @@ function usePersonRepo() {
25393
25427
  }
25394
25428
  async function getByNRIC(value) {
25395
25429
  try {
25430
+ if (!value || value.trim() === "") {
25431
+ import_node_server_utils74.logger.warn("getByNRIC called with an empty or invalid NRIC value.");
25432
+ return null;
25433
+ }
25396
25434
  const cacheKey = (0, import_node_server_utils74.makeCacheKey)(site_people_namespace_collection, {
25397
25435
  nric: value
25398
25436
  });
@@ -25441,8 +25479,11 @@ function usePersonRepo() {
25441
25479
  unit
25442
25480
  }, session) {
25443
25481
  try {
25482
+ if (!unit || !import_mongodb45.ObjectId.isValid(unit)) {
25483
+ throw new import_node_server_utils74.BadRequestError("Invalid unit ID.");
25484
+ }
25444
25485
  const query = {
25445
- unit,
25486
+ unit: new import_mongodb45.ObjectId(unit),
25446
25487
  status,
25447
25488
  ...Array.isArray(type) && type.length > 0 && {
25448
25489
  type: { $in: type }
@@ -26411,18 +26452,7 @@ function useBuildingUnitRepo() {
26411
26452
  } catch (error) {
26412
26453
  throw new import_node_server_utils76.BadRequestError("Invalid ID.");
26413
26454
  }
26414
- const cacheKey = (0, import_node_server_utils76.makeCacheKey)(building_units_namespace_collection, {
26415
- _id: String(_id)
26416
- });
26417
26455
  try {
26418
- const cached = await getCache(cacheKey);
26419
- if (cached) {
26420
- import_node_server_utils76.logger.log({
26421
- level: "info",
26422
- message: `Cache hit for getById building unit: ${cacheKey}`
26423
- });
26424
- return cached;
26425
- }
26426
26456
  const result = await collection.findOne({
26427
26457
  _id,
26428
26458
  deletedAt: { $in: ["", null] }
@@ -26430,17 +26460,6 @@ function useBuildingUnitRepo() {
26430
26460
  if (!result) {
26431
26461
  throw new import_node_server_utils76.BadRequestError("Building unit not found.");
26432
26462
  }
26433
- setCache(cacheKey, result, 300).then(() => {
26434
- import_node_server_utils76.logger.log({
26435
- level: "info",
26436
- message: `Cache set for building unit by id: ${cacheKey}`
26437
- });
26438
- }).catch((err) => {
26439
- import_node_server_utils76.logger.log({
26440
- level: "error",
26441
- message: `Failed to set cache for building unit by id: ${err.message}`
26442
- });
26443
- });
26444
26463
  return result;
26445
26464
  } catch (error) {
26446
26465
  if (error instanceof import_node_server_utils76.AppError) {
@@ -34452,7 +34471,7 @@ var KeyRepo = class {
34452
34471
  findOptions
34453
34472
  );
34454
34473
  console.log("visitorId", visitorId.toString());
34455
- console.log("latestHistory.visitorId", latestHistory?.visitorId.toString());
34474
+ console.log("latestHistory.visitorId", latestHistory?.visitorId?.toString());
34456
34475
  if (latestHistory?.visitorId && latestHistory.visitorId?.toString() !== visitorId.toString()) {
34457
34476
  console.log("Not updating keyId", keyId);
34458
34477
  return;
@@ -34525,70 +34544,59 @@ var KeyRepo = class {
34525
34544
  return Promise.reject("Failed to delete key");
34526
34545
  }
34527
34546
  }
34528
- static async getById2(id) {
34529
- return this.collection().aggregate([
34530
- { $match: { _id: new import_mongodb62.ObjectId(id) } },
34531
- {
34532
- $lookup: {
34533
- from: "qr-code-templates",
34534
- localField: "template",
34535
- foreignField: "_id",
34536
- as: "QRTemplateInfo"
34537
- }
34538
- },
34539
- {
34540
- $unwind: {
34541
- path: "$QRTemplateInfo",
34542
- preserveNullAndEmptyArrays: true
34543
- }
34544
- },
34545
- {
34546
- $addFields: {
34547
- prefixPass: { $toUpper: "$QRTemplateInfo.prefixPass" },
34548
- prefixKey: { $toUpper: "$QRTemplateInfo.prefixKey" },
34549
- templateName: "$QRTemplateInfo.name"
34550
- }
34551
- },
34552
- {
34553
- $addFields: {
34554
- prefixAndName: {
34555
- $cond: {
34556
- if: { $or: [{ $not: "$prefixPass" }, { $not: "$prefixKey" }] },
34557
- then: "$name",
34558
- else: {
34559
- $cond: {
34560
- if: { $eq: ["$passType", "pass-key"] },
34561
- then: { $concat: ["$prefixKey", "$name"] },
34562
- else: { $concat: ["$prefixPass", "$name"] }
34547
+ static async getById2(id, session) {
34548
+ return this.collection().aggregate(
34549
+ [
34550
+ { $match: { _id: new import_mongodb62.ObjectId(id) } },
34551
+ {
34552
+ $lookup: {
34553
+ from: "qr-code-templates",
34554
+ localField: "template",
34555
+ foreignField: "_id",
34556
+ as: "QRTemplateInfo"
34557
+ }
34558
+ },
34559
+ {
34560
+ $unwind: {
34561
+ path: "$QRTemplateInfo",
34562
+ preserveNullAndEmptyArrays: true
34563
+ }
34564
+ },
34565
+ {
34566
+ $addFields: {
34567
+ prefixPass: { $toUpper: "$QRTemplateInfo.prefixPass" },
34568
+ prefixKey: { $toUpper: "$QRTemplateInfo.prefixKey" },
34569
+ templateName: "$QRTemplateInfo.name"
34570
+ }
34571
+ },
34572
+ {
34573
+ $addFields: {
34574
+ prefixAndName: {
34575
+ $cond: {
34576
+ if: { $or: [{ $not: "$prefixPass" }, { $not: "$prefixKey" }] },
34577
+ then: "$name",
34578
+ else: {
34579
+ $cond: {
34580
+ if: { $eq: ["$passType", "pass-key"] },
34581
+ then: { $concat: ["$prefixKey", "$name"] },
34582
+ else: { $concat: ["$prefixPass", "$name"] }
34583
+ }
34563
34584
  }
34564
34585
  }
34565
34586
  }
34566
34587
  }
34567
34588
  }
34568
- }
34569
- ]).toArray().then((results) => results.length ? results[0] : null);
34589
+ ],
34590
+ { session }
34591
+ ).toArray().then((results) => results.length ? results[0] : null);
34570
34592
  }
34571
- static async checkPassKeyAvailability(visitorPass, passKeys) {
34572
- if (Array.isArray(visitorPass) && visitorPass.length > 0) {
34573
- for (let i = 0; i < visitorPass.length; i++) {
34574
- let pass = await convertObjectIdUtil2(visitorPass[i].keyId, "Pass ID");
34575
- const getPass = await this.getById2(pass);
34576
- if (!getPass)
34577
- throw new Error("Pass not found");
34578
- if (getPass?.status && getPass?.status.toLowerCase() != "Available".toLowerCase())
34579
- throw new Error(`Pass ${getPass?.prefixAndName} is ${getPass?.status}`);
34580
- }
34581
- }
34582
- if (Array.isArray(passKeys) && passKeys.length > 0) {
34583
- for (let i = 0; i < passKeys.length; i++) {
34584
- let key = await convertObjectIdUtil2(passKeys[i].keyId, "Key ID");
34585
- const getPass = await this.getById2(key);
34586
- if (!getPass)
34587
- throw new Error(`Key not found`);
34588
- if (getPass?.status && getPass?.status.toLowerCase() != "Available".toLowerCase())
34589
- throw new Error(`Pass ${getPass?.prefixAndName} is ${getPass?.status}`);
34590
- }
34591
- }
34593
+ static async checkPassOrKeyAvailability(passOrKeyId, type, session) {
34594
+ let passOrKeyObjectId = await convertObjectIdUtil2(passOrKeyId, `${type} Id`);
34595
+ const getPassOrKey = await this.getById2(passOrKeyObjectId, session);
34596
+ if (!getPassOrKey)
34597
+ throw new Error(`${type} not found`);
34598
+ if (getPassOrKey?.status && getPassOrKey?.status.toLowerCase() != "Available".toLowerCase())
34599
+ throw new Error(`${type} ${getPassOrKey?.prefixAndName} is ${getPassOrKey?.status}`);
34592
34600
  }
34593
34601
  };
34594
34602
 
@@ -34907,7 +34915,7 @@ function useVisitorTransactionService() {
34907
34915
  const unit = await _getUnitById(value.unit);
34908
34916
  value.unitName = unit?.name;
34909
34917
  }
34910
- if (allowedPersonTypes.includes(value?.type)) {
34918
+ if (allowedPersonTypes.includes(value?.type) && value?.nric) {
34911
34919
  const nric = value?.nric || "";
34912
34920
  const person = await getByNRIC(nric);
34913
34921
  const existingCompanyName = person?.companyName?.includes(
@@ -34953,9 +34961,6 @@ function useVisitorTransactionService() {
34953
34961
  } = value;
34954
34962
  for (let i = 0; i < value.members.length; i += chunkSize) {
34955
34963
  const chunk = value.members.slice(i, i + chunkSize);
34956
- for (const member of chunk) {
34957
- await KeyRepo.checkPassKeyAvailability(member.visitorPass, member.passKeys);
34958
- }
34959
34964
  await Promise.all(
34960
34965
  chunk.map(async (member) => {
34961
34966
  const clonedMember = structuredClone(member);
@@ -34996,6 +35001,7 @@ function useVisitorTransactionService() {
34996
35001
  );
34997
35002
  console.log("visitorId service", visitorId);
34998
35003
  for (const item of preparedVisitorPass) {
35004
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
34999
35005
  await KeyRepo.updateKeyById(
35000
35006
  item.keyId,
35001
35007
  {
@@ -35011,6 +35017,7 @@ function useVisitorTransactionService() {
35011
35017
  );
35012
35018
  }
35013
35019
  for (const item of preparedPassKeys) {
35020
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
35014
35021
  await KeyRepo.updateKeyById(
35015
35022
  item.keyId,
35016
35023
  {
@@ -35053,7 +35060,6 @@ function useVisitorTransactionService() {
35053
35060
  if (found === 1)
35054
35061
  throw new import_node_server_utils107.BadRequestError("This plate number is blocklisted");
35055
35062
  }
35056
- await KeyRepo.checkPassKeyAvailability(value.visitorPass, value.passKeys);
35057
35063
  if (Array.isArray(value.visitorPass)) {
35058
35064
  for (const item of value.visitorPass) {
35059
35065
  item.receivedDate = /* @__PURE__ */ new Date();
@@ -35073,6 +35079,7 @@ function useVisitorTransactionService() {
35073
35079
  const result = await _add(value, session);
35074
35080
  if (Array.isArray(value.visitorPass)) {
35075
35081
  for (const item of value.visitorPass) {
35082
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
35076
35083
  await KeyRepo.updateKeyById(
35077
35084
  item.keyId,
35078
35085
  { status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
@@ -35087,6 +35094,7 @@ function useVisitorTransactionService() {
35087
35094
  }
35088
35095
  if (Array.isArray(value.passKeys)) {
35089
35096
  for (const item of value.passKeys) {
35097
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
35090
35098
  await KeyRepo.updateKeyById(
35091
35099
  item.keyId,
35092
35100
  { status: "In Use" /* IN_USE */, updatedBy: value.createdBy },
@@ -35139,6 +35147,103 @@ function useVisitorTransactionService() {
35139
35147
  let host;
35140
35148
  let username;
35141
35149
  let password;
35150
+ if (value.checkIn) {
35151
+ const parsed = new Date(value.checkIn);
35152
+ value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
35153
+ }
35154
+ if (value.isMembersAdded != true && Array.isArray(value.members) && value.members.length > 0) {
35155
+ const chunkSize = 10;
35156
+ const {
35157
+ block,
35158
+ level,
35159
+ unit,
35160
+ site,
35161
+ org,
35162
+ type,
35163
+ company,
35164
+ remarks,
35165
+ contractorType,
35166
+ unitName
35167
+ } = value;
35168
+ for (let i = 0; i < value.members.length; i += chunkSize) {
35169
+ const chunk = value.members.slice(i, i + chunkSize);
35170
+ await Promise.all(
35171
+ chunk.map(async (member) => {
35172
+ const clonedMember = structuredClone(member);
35173
+ const { visitorPass, passKeys } = clonedMember;
35174
+ const preparedVisitorPass = Array.isArray(visitorPass) ? visitorPass.map((item) => ({
35175
+ ...item,
35176
+ receivedDate: /* @__PURE__ */ new Date(),
35177
+ status: "Not Returned" /* NOT_RETURNED */,
35178
+ lastUpdate: null,
35179
+ remarks: ""
35180
+ })) : [];
35181
+ const preparedPassKeys = Array.isArray(passKeys) ? passKeys.map((item) => ({
35182
+ ...item,
35183
+ receivedDate: /* @__PURE__ */ new Date(),
35184
+ status: "Not Returned" /* NOT_RETURNED */,
35185
+ lastUpdate: null,
35186
+ remarks: ""
35187
+ })) : [];
35188
+ const visitorId = await _add(
35189
+ {
35190
+ ...clonedMember,
35191
+ block,
35192
+ level,
35193
+ unit,
35194
+ unitName,
35195
+ site,
35196
+ org,
35197
+ type,
35198
+ company,
35199
+ remarks,
35200
+ contractorType,
35201
+ checkIn: value.checkIn,
35202
+ visitorPass: preparedVisitorPass,
35203
+ passKeys: preparedPassKeys,
35204
+ status: "registered" /* REGISTERED */
35205
+ },
35206
+ session
35207
+ );
35208
+ console.log("visitorId service", visitorId);
35209
+ for (const item of preparedVisitorPass) {
35210
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
35211
+ await KeyRepo.updateKeyById(
35212
+ item.keyId,
35213
+ {
35214
+ status: "In Use" /* IN_USE */,
35215
+ updatedBy: value.createdBy
35216
+ },
35217
+ value.site,
35218
+ session,
35219
+ void 0,
35220
+ visitorId,
35221
+ void 0,
35222
+ true
35223
+ );
35224
+ }
35225
+ for (const item of preparedPassKeys) {
35226
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
35227
+ await KeyRepo.updateKeyById(
35228
+ item.keyId,
35229
+ {
35230
+ status: "In Use" /* IN_USE */,
35231
+ updatedBy: value.createdBy,
35232
+ visitorId
35233
+ },
35234
+ value.site,
35235
+ session,
35236
+ void 0,
35237
+ visitorId,
35238
+ void 0,
35239
+ true
35240
+ );
35241
+ }
35242
+ })
35243
+ );
35244
+ }
35245
+ value.isMembersAdded = true;
35246
+ }
35142
35247
  if (value.site && value.plateNumber) {
35143
35248
  try {
35144
35249
  camera = await _getVisitorsInBySite(value.site);
@@ -35172,10 +35277,6 @@ function useVisitorTransactionService() {
35172
35277
  if (found === 1)
35173
35278
  throw new import_node_server_utils107.BadRequestError("This plate number is blocklisted");
35174
35279
  }
35175
- if (value.checkIn) {
35176
- const parsed = new Date(value.checkIn);
35177
- value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
35178
- }
35179
35280
  if (value.checkOut) {
35180
35281
  const parsed = new Date(value.checkOut);
35181
35282
  value.checkOut = isNaN(parsed.getTime()) ? null : parsed;
@@ -35183,6 +35284,12 @@ function useVisitorTransactionService() {
35183
35284
  if (value.site) {
35184
35285
  value.site = typeof value.site === "string" ? new import_mongodb63.ObjectId(value.site) : value.site;
35185
35286
  }
35287
+ if (value?.block) {
35288
+ value.block = await convertObjectIdUtil2(value.block, "block Id");
35289
+ }
35290
+ if (value?.level) {
35291
+ value.level = await convertObjectIdUtil2(value.level, "level Id");
35292
+ }
35186
35293
  if (value.unit) {
35187
35294
  value.unit = typeof value.unit === "string" ? new import_mongodb63.ObjectId(value.unit) : value.unit;
35188
35295
  const unit = await _getUnitById(value.unit);
@@ -35194,6 +35301,7 @@ function useVisitorTransactionService() {
35194
35301
  if (Array.isArray(value.visitorPass)) {
35195
35302
  for (const item of value.visitorPass) {
35196
35303
  if (item.add) {
35304
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Pass", session);
35197
35305
  await KeyRepo.updateKeyById(
35198
35306
  item.keyId,
35199
35307
  {
@@ -35242,6 +35350,7 @@ function useVisitorTransactionService() {
35242
35350
  if (Array.isArray(value.passKeys)) {
35243
35351
  for (const item of value.passKeys) {
35244
35352
  if (item.add) {
35353
+ await KeyRepo.checkPassOrKeyAvailability(item.keyId, "Key", session);
35245
35354
  await KeyRepo.updateKeyById(
35246
35355
  item.keyId,
35247
35356
  {
@@ -36211,7 +36320,8 @@ function usePersonService() {
36211
36320
  getUserByEmail,
36212
36321
  updateUserFieldById: _updateUserFieldById,
36213
36322
  getUserById,
36214
- updateUserOrgById: _updateUserOrgById
36323
+ updateUserOrgById: _updateUserOrgById,
36324
+ updateUserUnitById: _updateUserUnitById
36215
36325
  } = useUserRepo();
36216
36326
  const { add: addMember } = useMemberRepo();
36217
36327
  const { getById: _getUnitById, updateById: updateUnitById } = useBuildingUnitRepo();
@@ -36358,6 +36468,10 @@ function usePersonService() {
36358
36468
  value.unit = new import_mongodb66.ObjectId(value.unit);
36359
36469
  }
36360
36470
  const isOrgChanged = value.org && person.org?.toString() !== value.org.toString();
36471
+ const toKey = (v) => v === null || v === void 0 || v === "" ? "" : v.toString();
36472
+ const isBlockChanged = "block" in value && toKey(value.block) !== toKey(person.block);
36473
+ const isLevelChanged = "level" in value && toKey(value.level) !== toKey(person.level);
36474
+ const isUnitChanged = "unit" in value && toKey(value.unit) !== toKey(person.unit);
36361
36475
  await _updateById(_id, value, session);
36362
36476
  if (isOrgChanged && person.user) {
36363
36477
  await _updateUserOrgById(
@@ -36366,6 +36480,26 @@ function usePersonService() {
36366
36480
  session
36367
36481
  );
36368
36482
  }
36483
+ if ((isBlockChanged || isLevelChanged || isUnitChanged) && person.user) {
36484
+ const userUnitPayload = {};
36485
+ if (isBlockChanged) {
36486
+ userUnitPayload.block = value.block ?? null;
36487
+ }
36488
+ if (isLevelChanged) {
36489
+ userUnitPayload.level = value.level ?? null;
36490
+ }
36491
+ if (isUnitChanged) {
36492
+ userUnitPayload.unitId = value.unit ?? null;
36493
+ if ("unitName" in value) {
36494
+ userUnitPayload.unitName = value.unitName ?? "";
36495
+ }
36496
+ }
36497
+ await _updateUserUnitById(
36498
+ person.user.toString(),
36499
+ userUnitPayload,
36500
+ session
36501
+ );
36502
+ }
36369
36503
  if (value.unit && (isNameUpdated || isOwnerChanged || value.isOwner)) {
36370
36504
  const unit = await _getUnitById(value.unit.toString());
36371
36505
  if (unit) {
@@ -36395,6 +36529,16 @@ function usePersonService() {
36395
36529
  }
36396
36530
  }
36397
36531
  }
36532
+ if (isUnitChanged && person.isOwner && person.unit) {
36533
+ const previousUnit = await _getUnitById(person.unit.toString());
36534
+ if (previousUnit && previousUnit._id && previousUnit.owner?.toString() === person?._id?.toString()) {
36535
+ await updateUnitById(
36536
+ previousUnit._id.toString(),
36537
+ { owner: "", ownerName: "" },
36538
+ session
36539
+ );
36540
+ }
36541
+ }
36398
36542
  await session.commitTransaction();
36399
36543
  return "Person updated successfully.";
36400
36544
  } catch (error) {
@@ -45299,7 +45443,6 @@ function useSiteUnitBillingService() {
45299
45443
  const { getAll, updateById: _updateById } = useSiteBillingItemRepo();
45300
45444
  const { getBuildingUnitsWithOwner: _getBuildingUnitsWithOwner } = useBuildingUnitRepo();
45301
45445
  async function processBilling() {
45302
- console.log("Starting billing process...");
45303
45446
  const billing_items = await getAll({
45304
45447
  search: "",
45305
45448
  page: 1,
@@ -45342,7 +45485,7 @@ function useSiteUnitBillingService() {
45342
45485
  const buildUnitBilling = (unit) => ({
45343
45486
  site: billing_item.site.toString(),
45344
45487
  org: billing_item.org.toString(),
45345
- billItem: billing_item._id,
45488
+ billItem: billing_item._id?.toString(),
45346
45489
  billName: billing_item.name,
45347
45490
  unitId: unit._id.toString(),
45348
45491
  unit: unit.blockName + " / " + unit.levelName + " / " + unit.name,
@@ -45376,6 +45519,9 @@ function useSiteUnitBillingService() {
45376
45519
  level: "error",
45377
45520
  message: `Failed to proccess cron billing: ${error.message}`
45378
45521
  });
45522
+ if (session.inTransaction()) {
45523
+ await session.abortTransaction();
45524
+ }
45379
45525
  continue;
45380
45526
  } finally {
45381
45527
  session.endSession();
@@ -45430,6 +45576,7 @@ function useSiteUnitBillingService() {
45430
45576
  const billingMonth = Number(billing_item.month);
45431
45577
  const billingDay = Number(billing_item.date);
45432
45578
  if (billing_item.frequency === "monthly" /* MONTHLY */) {
45579
+ console.log("billing_item", billing_item.name);
45433
45580
  return todayDate === billingDay;
45434
45581
  }
45435
45582
  if (billing_item.frequency === "quarterly" /* QAURTERLY */) {
@@ -46561,7 +46708,7 @@ function UseAccessManagementRepo() {
46561
46708
  $lookup: {
46562
46709
  from: "building-levels",
46563
46710
  localField: "_id",
46564
- foreignField: "block",
46711
+ foreignField: "blockId",
46565
46712
  pipeline: [
46566
46713
  { $match: { status: { $ne: "deleted" } } },
46567
46714
  {
@@ -46582,7 +46729,7 @@ function UseAccessManagementRepo() {
46582
46729
  {
46583
46730
  $project: {
46584
46731
  _id: 1,
46585
- level: 1,
46732
+ name: 1,
46586
46733
  units: 1
46587
46734
  }
46588
46735
  }
@@ -46743,7 +46890,7 @@ function UseAccessManagementRepo() {
46743
46890
  $project: {
46744
46891
  _id: "$level.units._id",
46745
46892
  name: "$level.units.name",
46746
- level: { _id: "$level._id", level: "$level.level" },
46893
+ level: { _id: "$level._id", level: "$level.name" },
46747
46894
  block: { _id: "$_id", name: "$name", block: "$block" },
46748
46895
  site: "$site",
46749
46896
  unit_owner: { $arrayElemAt: ["$unitOwner", 0] },
@@ -48352,7 +48499,7 @@ function UseAccessManagementRepo() {
48352
48499
  $lookup: {
48353
48500
  from: "building-levels",
48354
48501
  localField: "_id",
48355
- foreignField: "block",
48502
+ foreignField: "blockId",
48356
48503
  as: "levels",
48357
48504
  pipeline: [
48358
48505
  {
@@ -50838,11 +50985,24 @@ function useNfcPatrolTagRepo() {
50838
50985
  });
50839
50986
  });
50840
50987
  }
50988
+ async function getById(_id, session) {
50989
+ try {
50990
+ _id = typeof _id === "string" ? new import_mongodb99.ObjectId(_id) : _id;
50991
+ } catch {
50992
+ throw new import_node_server_utils161.BadRequestError("Invalid NFC Patrol Tag ID.");
50993
+ }
50994
+ const tag = await collection.findOne({ _id }, { session });
50995
+ if (!tag) {
50996
+ throw new import_node_server_utils161.NotFoundError("NFC Patrol Tag not found.");
50997
+ }
50998
+ return tag;
50999
+ }
50841
51000
  return {
50842
51001
  createIndexes,
50843
51002
  add,
50844
51003
  getAll,
50845
- updateNfcPatrolTagBySite
51004
+ updateNfcPatrolTagBySite,
51005
+ getById
50846
51006
  };
50847
51007
  }
50848
51008
 
@@ -57474,7 +57634,7 @@ var schemaNfcPatrolLog = import_joi110.default.object({
57474
57634
  checkPoints: import_joi110.default.array().items(
57475
57635
  import_joi110.default.object({
57476
57636
  nfcTag_id: import_joi110.default.string().length(24).hex().required(),
57477
- nfcTag_name: import_joi110.default.string().length(24).hex().required(),
57637
+ nfcTag_name: import_joi110.default.string().required(),
57478
57638
  travelTime: import_joi110.default.number().integer().min(0).required(),
57479
57639
  startDateTime: import_joi110.default.date().required().messages({
57480
57640
  "date.base": "startDateTime must be a valid date or ISO string"
@@ -57482,7 +57642,7 @@ var schemaNfcPatrolLog = import_joi110.default.object({
57482
57642
  endDateTime: import_joi110.default.date().required().messages({
57483
57643
  "date.base": "endDateTime must be a valid date or ISO string"
57484
57644
  }),
57485
- status: import_joi110.default.string().valid("Completed", "Skipped").required(),
57645
+ status: import_joi110.default.string().valid("Pending", "Completed", "Skipped").required(),
57486
57646
  skippedRemarks: import_joi110.default.string().required()
57487
57647
  })
57488
57648
  ).min(0).required(),
@@ -57659,17 +57819,59 @@ function useNfcPatrolLogRepo() {
57659
57819
  });
57660
57820
  });
57661
57821
  }
57822
+ async function getById(id, session) {
57823
+ try {
57824
+ id = new import_mongodb121.ObjectId(id);
57825
+ } catch {
57826
+ throw new import_node_server_utils198.BadRequestError("Invalid Log ID.");
57827
+ }
57828
+ return collection.findOne(
57829
+ { _id: id },
57830
+ { session }
57831
+ );
57832
+ }
57833
+ async function updateCheckpoint(id, checkpointIndex, payload, session) {
57834
+ try {
57835
+ id = new import_mongodb121.ObjectId(id);
57836
+ } catch {
57837
+ throw new import_node_server_utils198.BadRequestError("Invalid Log ID.");
57838
+ }
57839
+ const setData = {
57840
+ [`checkPoints.${checkpointIndex}.status`]: payload.action === "complete" ? "Completed" : "Skipped",
57841
+ [`checkPoints.${checkpointIndex}.endDateTime`]: /* @__PURE__ */ new Date()
57842
+ };
57843
+ if (payload.action === "skip") {
57844
+ setData[`checkPoints.${checkpointIndex}.skippedRemarks`] = payload.skippedRemarks;
57845
+ }
57846
+ const res = await collection.updateOne(
57847
+ { _id: id },
57848
+ {
57849
+ $set: setData
57850
+ },
57851
+ { session }
57852
+ );
57853
+ delCachedData();
57854
+ return res;
57855
+ }
57662
57856
  return {
57663
57857
  createIndexes,
57664
57858
  add,
57665
- getAllBySite
57859
+ getAllBySite,
57860
+ getById,
57861
+ updateCheckpoint
57666
57862
  };
57667
57863
  }
57668
57864
 
57669
57865
  // src/services/nfc-patrol-log.service.ts
57670
57866
  var import_node_server_utils199 = require("@7365admin1/node-server-utils");
57671
57867
  function useNfcPatrolLogService() {
57672
- const { add: _add } = useNfcPatrolLogRepo();
57868
+ const {
57869
+ add: _add,
57870
+ getById,
57871
+ updateCheckpoint
57872
+ } = useNfcPatrolLogRepo();
57873
+ const routeRepo = useNfcPatrolRouteRepo();
57874
+ const tagRepo = useNfcPatrolTagRepo();
57673
57875
  async function add(value) {
57674
57876
  const session = import_node_server_utils199.useAtlas.getClient()?.startSession();
57675
57877
  session?.startTransaction();
@@ -57684,8 +57886,59 @@ function useNfcPatrolLogService() {
57684
57886
  session?.endSession();
57685
57887
  }
57686
57888
  }
57889
+ async function completeCheckpoint(logId, payload) {
57890
+ const session = import_node_server_utils199.useAtlas.getClient()?.startSession();
57891
+ session?.startTransaction();
57892
+ try {
57893
+ const log = await getById(logId, session);
57894
+ if (!log) {
57895
+ throw new import_node_server_utils199.BadRequestError("Patrol Log not found.");
57896
+ }
57897
+ const route = await routeRepo.getById(log.route._id);
57898
+ if (!route) {
57899
+ throw new import_node_server_utils199.BadRequestError("Route not found.");
57900
+ }
57901
+ const checkpoint = route.checkPoints[payload.checkpointIndex];
57902
+ if (!checkpoint) {
57903
+ throw new import_node_server_utils199.BadRequestError("Checkpoint not found.");
57904
+ }
57905
+ const logCheckpoint = log.checkPoints[payload.checkpointIndex];
57906
+ if (!logCheckpoint) {
57907
+ throw new import_node_server_utils199.BadRequestError("Checkpoint log not found.");
57908
+ }
57909
+ if (logCheckpoint.status === "Completed") {
57910
+ throw new import_node_server_utils199.BadRequestError("Checkpoint already completed.");
57911
+ }
57912
+ if (payload.action === "skip" && !payload.skippedRemarks?.trim()) {
57913
+ throw new import_node_server_utils199.BadRequestError("Skipped remarks are required.");
57914
+ }
57915
+ if (payload.action === "complete") {
57916
+ const tag = await tagRepo.getById(checkpoint.nfcTag_id, session);
57917
+ if (!tag.tagUID) {
57918
+ throw new import_node_server_utils199.BadRequestError("NFC Tag has not been configured.");
57919
+ }
57920
+ if (tag.tagUID !== payload.uid) {
57921
+ throw new import_node_server_utils199.BadRequestError("Invalid NFC Tag.");
57922
+ }
57923
+ }
57924
+ await updateCheckpoint(
57925
+ logId,
57926
+ payload.checkpointIndex,
57927
+ payload,
57928
+ session
57929
+ );
57930
+ await session?.commitTransaction();
57931
+ return "Checkpoint updated.";
57932
+ } catch (error) {
57933
+ await session?.abortTransaction();
57934
+ throw error;
57935
+ } finally {
57936
+ session?.endSession();
57937
+ }
57938
+ }
57687
57939
  return {
57688
- add
57940
+ add,
57941
+ completeCheckpoint
57689
57942
  };
57690
57943
  }
57691
57944
 
@@ -57693,7 +57946,7 @@ function useNfcPatrolLogService() {
57693
57946
  var import_node_server_utils200 = require("@7365admin1/node-server-utils");
57694
57947
  var import_joi111 = __toESM(require("joi"));
57695
57948
  function useNfcPatrolLogController() {
57696
- const { add: _add } = useNfcPatrolLogService();
57949
+ const { add: _add, completeCheckpoint: _completeCheckpoint } = useNfcPatrolLogService();
57697
57950
  const { getAllBySite: _getAllBySite } = useNfcPatrolLogRepo();
57698
57951
  async function add(req, res, next) {
57699
57952
  const payload = { ...req.body };
@@ -57756,9 +58009,27 @@ function useNfcPatrolLogController() {
57756
58009
  return;
57757
58010
  }
57758
58011
  }
58012
+ async function completeCheckpoint(req, res, next) {
58013
+ const { id } = req.params;
58014
+ const payload = {
58015
+ checkpointIndex: req.body.checkpointIndex,
58016
+ uid: req.body.uid,
58017
+ action: req.body.action,
58018
+ skippedRemarks: req.body.skippedRemarks
58019
+ };
58020
+ try {
58021
+ const data = await _completeCheckpoint(id, payload);
58022
+ res.status(200).json(data);
58023
+ return;
58024
+ } catch (error) {
58025
+ import_node_server_utils200.logger.error(error.message);
58026
+ next(error);
58027
+ }
58028
+ }
57759
58029
  return {
57760
58030
  add,
57761
- getAllBySite
58031
+ getAllBySite,
58032
+ completeCheckpoint
57762
58033
  };
57763
58034
  }
57764
58035
 
@@ -57814,12 +58085,9 @@ function useNewDashboardRepo() {
57814
58085
  function getDateRange(p) {
57815
58086
  const now = (0, import_moment.default)().tz("Asia/Singapore");
57816
58087
  if (p === "thisWeek" /* THIS_WEEK */) {
57817
- const rollingStart = now.clone().subtract(6, "days").startOf("day");
57818
- const monthStart = now.clone().startOf("month");
57819
- const start = import_moment.default.max(rollingStart, monthStart);
57820
58088
  return {
57821
- $gte: start.toDate(),
57822
- $lte: now.clone().endOf("day").toDate()
58089
+ $gte: now.clone().startOf("isoWeek").toDate(),
58090
+ $lte: now.clone().endOf("isoWeek").toDate()
57823
58091
  };
57824
58092
  }
57825
58093
  if (p === "thisMonth" /* THIS_MONTH */) {
@@ -57833,6 +58101,12 @@ function useNewDashboardRepo() {
57833
58101
  $lte: now.clone().endOf("day").toDate()
57834
58102
  };
57835
58103
  }
58104
+ function getSiteDayRange(date = import_moment.default.tz("Asia/Singapore")) {
58105
+ return {
58106
+ $gte: date.clone().startOf("day").toDate(),
58107
+ $lte: date.clone().endOf("day").toDate()
58108
+ };
58109
+ }
57836
58110
  const calculatePercentageChange = (currentCount, previousCount) => {
57837
58111
  if (previousCount === 0) {
57838
58112
  return currentCount > 0 ? 100 : 0;
@@ -58259,24 +58533,27 @@ function useNewDashboardRepo() {
58259
58533
  const siteIdObj = (0, import_node_server_utils201.toObjectId)(siteId);
58260
58534
  const startOfToday = import_moment.default.tz("Asia/Singapore").startOf("day").toDate();
58261
58535
  const endOfToday = import_moment.default.tz("Asia/Singapore").endOf("day").toDate();
58262
- const localTodayStr = import_moment.default.tz("Asia/Singapore").format("YYYY-MM-DD");
58263
- const facilityTodayStart = import_moment.default.utc(`${localTodayStr}T00:00:00.000Z`).toDate();
58264
- const facilityTodayEnd = import_moment.default.utc(`${localTodayStr}T23:59:59.999Z`).toDate();
58265
- const localYesterdayStr = import_moment.default.tz("Asia/Singapore").subtract(1, "day").format("YYYY-MM-DD");
58266
- const facilityYesterdayStart = import_moment.default.utc(`${localYesterdayStr}T00:00:00.000Z`).toDate();
58267
- const facilityYesterdayEnd = import_moment.default.utc(`${localYesterdayStr}T23:59:59.999Z`).toDate();
58268
- let facilityPeriodRange = { $gte: facilityTodayStart, $lte: facilityTodayEnd };
58536
+ const facilityTodayRange = getSiteDayRange();
58537
+ const facilityYesterdayRange = getSiteDayRange(
58538
+ import_moment.default.tz("Asia/Singapore").subtract(1, "day")
58539
+ );
58540
+ const facilityTodayStart = facilityTodayRange.$gte;
58541
+ const facilityTodayEnd = facilityTodayRange.$lte;
58542
+ const facilityYesterdayStart = facilityYesterdayRange.$gte;
58543
+ const facilityYesterdayEnd = facilityYesterdayRange.$lte;
58544
+ let facilityPeriodRange = {
58545
+ $gte: facilityTodayStart,
58546
+ $lte: facilityTodayEnd
58547
+ };
58269
58548
  if (period === "thisWeek" /* THIS_WEEK */) {
58270
- const startStr = import_moment.default.tz("Asia/Singapore").subtract(7, "days").format("YYYY-MM-DD");
58271
58549
  facilityPeriodRange = {
58272
- $gte: import_moment.default.utc(`${startStr}T00:00:00.000Z`).toDate(),
58273
- $lte: facilityTodayEnd
58550
+ $gte: import_moment.default.tz("Asia/Singapore").startOf("isoWeek").toDate(),
58551
+ $lte: import_moment.default.tz("Asia/Singapore").endOf("isoWeek").toDate()
58274
58552
  };
58275
58553
  } else if (period === "thisMonth" /* THIS_MONTH */) {
58276
- const startStr = import_moment.default.tz("Asia/Singapore").subtract(30, "days").format("YYYY-MM-DD");
58277
58554
  facilityPeriodRange = {
58278
- $gte: import_moment.default.utc(`${startStr}T00:00:00.000Z`).toDate(),
58279
- $lte: facilityTodayEnd
58555
+ $gte: import_moment.default.tz("Asia/Singapore").startOf("month").toDate(),
58556
+ $lte: import_moment.default.tz("Asia/Singapore").endOf("month").toDate()
58280
58557
  };
58281
58558
  }
58282
58559
  const upcomingEvents = await db.collection(events_namespace_collection).find({
@@ -58360,6 +58637,18 @@ function useNewDashboardRepo() {
58360
58637
  const facilityCollection = db.collection(
58361
58638
  facility_bookings_namespace_collection2
58362
58639
  );
58640
+ const facilityBookingStatuses = [
58641
+ "Pending",
58642
+ "Approved",
58643
+ "Ongoing",
58644
+ "For Review",
58645
+ "With Balance",
58646
+ "For Refund",
58647
+ "With Refund",
58648
+ "Completed",
58649
+ "Rejected",
58650
+ "Cancelled"
58651
+ ];
58363
58652
  const [
58364
58653
  workOrderReport,
58365
58654
  yesterdayWorkOrderReport,
@@ -58510,7 +58799,7 @@ function useNewDashboardRepo() {
58510
58799
  }
58511
58800
  }
58512
58801
  ],
58513
- status: { $nin: ["deleted", "Deleted"] }
58802
+ status: { $in: facilityBookingStatuses }
58514
58803
  }
58515
58804
  },
58516
58805
  {
@@ -58535,6 +58824,25 @@ function useNewDashboardRepo() {
58535
58824
  }
58536
58825
  },
58537
58826
  { $count: "count" }
58827
+ ],
58828
+ byStatus: [
58829
+ {
58830
+ $match: {
58831
+ status: { $in: facilityBookingStatuses }
58832
+ }
58833
+ },
58834
+ {
58835
+ $group: {
58836
+ _id: {
58837
+ $cond: [
58838
+ { $eq: ["$status", "For Refund"] },
58839
+ "With Refund",
58840
+ "$status"
58841
+ ]
58842
+ },
58843
+ count: { $sum: 1 }
58844
+ }
58845
+ }
58538
58846
  ]
58539
58847
  }
58540
58848
  }
@@ -58545,7 +58853,12 @@ function useNewDashboardRepo() {
58545
58853
  site: { $in: [siteIdObj, siteId] },
58546
58854
  ...facilityMatchObj,
58547
58855
  $or: [
58548
- { date: { $gte: facilityYesterdayStart, $lte: facilityYesterdayEnd } },
58856
+ {
58857
+ date: {
58858
+ $gte: facilityYesterdayStart,
58859
+ $lte: facilityYesterdayEnd
58860
+ }
58861
+ },
58549
58862
  {
58550
58863
  date: {
58551
58864
  $gte: facilityYesterdayStart.toISOString(),
@@ -58553,7 +58866,7 @@ function useNewDashboardRepo() {
58553
58866
  }
58554
58867
  }
58555
58868
  ],
58556
- status: { $nin: ["deleted", "Deleted"] }
58869
+ status: { $in: facilityBookingStatuses }
58557
58870
  }
58558
58871
  },
58559
58872
  { $count: "count" }
@@ -58572,7 +58885,7 @@ function useNewDashboardRepo() {
58572
58885
  }
58573
58886
  }
58574
58887
  ],
58575
- status: { $nin: ["deleted", "Deleted"] }
58888
+ status: { $in: facilityBookingStatuses }
58576
58889
  }
58577
58890
  },
58578
58891
  { $count: "count" }
@@ -58596,8 +58909,25 @@ function useNewDashboardRepo() {
58596
58909
  const fFacet = facilityReport[0] ?? {
58597
58910
  total: [],
58598
58911
  ongoing: [],
58599
- waitingApproval: []
58912
+ waitingApproval: [],
58913
+ byStatus: []
58914
+ };
58915
+ const facilityBookingStatusCounts = {
58916
+ Pending: 0,
58917
+ Approved: 0,
58918
+ Ongoing: 0,
58919
+ "For Review": 0,
58920
+ "With Balance": 0,
58921
+ "With Refund": 0,
58922
+ Completed: 0,
58923
+ Rejected: 0,
58924
+ Cancelled: 0
58600
58925
  };
58926
+ for (const item of fFacet.byStatus ?? []) {
58927
+ if (typeof item?._id === "string" && item._id in facilityBookingStatusCounts) {
58928
+ facilityBookingStatusCounts[item._id] = item.count ?? 0;
58929
+ }
58930
+ }
58601
58931
  const workOrderStatus = {
58602
58932
  pending: 0,
58603
58933
  inProgress: 0,
@@ -58644,6 +58974,7 @@ function useNewDashboardRepo() {
58644
58974
  count: fFacet.total[0]?.count ?? 0,
58645
58975
  ongoing: fFacet.ongoing[0]?.count ?? 0,
58646
58976
  waitingApproval: fFacet.waitingApproval[0]?.count ?? 0,
58977
+ byStatus: facilityBookingStatusCounts,
58647
58978
  percentage: calculatePercentageChange(
58648
58979
  todayFacilityReport[0]?.count ?? 0,
58649
58980
  yesterdayFacilityReport[0]?.count ?? 0
@@ -63374,9 +63705,12 @@ var useRedDotPaymentRepo = () => {
63374
63705
  }
63375
63706
  const now = /* @__PURE__ */ new Date();
63376
63707
  const unitUpdate = {
63708
+ method: payload.method,
63709
+ message: payload.message,
63377
63710
  updatedAt: now.toISOString(),
63378
63711
  paymentStatus: payload.paymentStatus,
63379
- transaction_id: payload.transaction_id
63712
+ transaction_id: payload.transaction_id,
63713
+ paidBy: paymentInfo.paidBy
63380
63714
  };
63381
63715
  const paymentUpdate = {
63382
63716
  updatedAt: now.toISOString(),