@7365admin1/core 2.38.0 → 2.39.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
@@ -1,3 +1,16 @@
1
+ // src/models/base.model.ts
2
+ var AppServiceType = /* @__PURE__ */ ((AppServiceType2) => {
3
+ AppServiceType2["REAL_ESTATE_DEVELOPER"] = "real_estate_developer";
4
+ AppServiceType2["PROPERTY_MANAGEMENT_AGENCY"] = "property_management_agency";
5
+ AppServiceType2["SECURITY_AGENCY"] = "security_agency";
6
+ AppServiceType2["CLEANING_SERVICES"] = "cleaning_services";
7
+ AppServiceType2["MECHANICAL_ELECTRICAL_SERVICES"] = "mechanical_electrical_services";
8
+ AppServiceType2["LANDSCAPING_SERVICES"] = "landscaping_services";
9
+ AppServiceType2["PEST_CONTROL_SERVICES"] = "pest_control_services";
10
+ AppServiceType2["POOL_MAINTENANCE_SERVICES"] = "pool_maintenance_services";
11
+ return AppServiceType2;
12
+ })(AppServiceType || {});
13
+
1
14
  // src/models/session.model.ts
2
15
  import { BadRequestError } from "@7365admin1/node-server-utils";
3
16
  import Joi from "joi";
@@ -16556,13 +16569,53 @@ function useDahuaService() {
16556
16569
  throw error2;
16557
16570
  }
16558
16571
  }
16572
+ async function bulkInsertPlateNumber(value) {
16573
+ const validation = Joi39.object({
16574
+ host: Joi39.string().required(),
16575
+ username: Joi39.string().required(),
16576
+ password: Joi39.string().required(),
16577
+ plateNumber: Joi39.string().required(),
16578
+ mode: Joi39.string().valid(...Object.values(ANPRMode)).required(),
16579
+ start: Joi39.string().isoDate().optional().allow("", null),
16580
+ end: Joi39.string().isoDate().optional().allow("", null),
16581
+ owner: Joi39.string().optional().allow("", null),
16582
+ isOpenGate: Joi39.boolean().optional().allow(null),
16583
+ vehicleType: Joi39.string().optional().allow("", null),
16584
+ vehicleColor: Joi39.string().optional().allow("", null)
16585
+ });
16586
+ const { error } = validation.validate(value);
16587
+ if (error) {
16588
+ throw new BadRequestError71(`Validation error: ${error.message}`);
16589
+ }
16590
+ value.owner = String(value.owner ?? "").substring(0, 15) || "unknown";
16591
+ value.vehicleType = String(value.vehicleType ?? "").substring(0, 31) || "unknown";
16592
+ value.vehicleColor = String(value.vehicleColor ?? "").substring(0, 31) || "unknown";
16593
+ const _openGate = String(value.isOpenGate);
16594
+ const isOpenGateString = _openGate && _openGate !== "undefined" ? _openGate : "true";
16595
+ const endpoint = `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${value.plateNumber}&VehicleType=${value.vehicleType}&VehicleColor=${value.vehicleColor}&BeginTime=${value.start}&CancelTime=${value.end}&+OpenGate=${isOpenGateString}&MasterOfCar=${value.owner}`;
16596
+ try {
16597
+ const response = await useDahuaDigest({
16598
+ host: value.host,
16599
+ username: value.username,
16600
+ password: value.password,
16601
+ endpoint
16602
+ });
16603
+ return response;
16604
+ } catch (error2) {
16605
+ logger52.error(`[${value.host}] Error bulk add plate number:`, error2);
16606
+ throw new BadRequestError71(
16607
+ `Failed bulk adding plate number: ${error2.message}`
16608
+ );
16609
+ }
16610
+ }
16559
16611
  return {
16560
16612
  getSnapshot,
16561
16613
  getTrafficJunction,
16562
16614
  addPlateNumber,
16563
16615
  removePlateNumber,
16564
16616
  updatePlateNumber,
16565
- getPlateNumber
16617
+ getPlateNumber,
16618
+ bulkInsertPlateNumber
16566
16619
  };
16567
16620
  }
16568
16621
 
@@ -18835,6 +18888,21 @@ function useVehicleController() {
18835
18888
  next(new BadRequestError83("Only .xlsx files are allowed."));
18836
18889
  return;
18837
18890
  }
18891
+ const schema2 = Joi46.object({
18892
+ fullName: Joi46.string().trim().required(),
18893
+ userType: Joi46.string().trim().required(),
18894
+ recordType: Joi46.string().trim().required(),
18895
+ phoneNumber: Joi46.string().trim().allow("", null).optional(),
18896
+ block: Joi46.number().required(),
18897
+ level: Joi46.number().integer().min(1).required(),
18898
+ unit: Joi46.number().integer().min(1).required(),
18899
+ plateNumber: Joi46.string().trim().uppercase().required(),
18900
+ vehicleModel: Joi46.string().trim().allow("", null).optional(),
18901
+ vehicleColor: Joi46.string().trim().allow("", null).optional(),
18902
+ subscriptionExpiry: Joi46.string().trim().allow("", null).optional(),
18903
+ site: Joi46.string().hex().length(24).required(),
18904
+ org: Joi46.string().hex().length(24).required()
18905
+ });
18838
18906
  const workbook = new ExcelJS.Workbook();
18839
18907
  await workbook.xlsx.readFile(req.file.path);
18840
18908
  const worksheet = workbook.worksheets[0];
@@ -18859,12 +18927,39 @@ function useVehicleController() {
18859
18927
  rows.push(rowData);
18860
18928
  }
18861
18929
  });
18862
- const vehicles = rows.map(mapRowToVehicle);
18863
- const data = await _bulkUpsertVehicles(vehicles);
18930
+ const validRows = [];
18931
+ const invalidRows = [];
18932
+ rows.forEach((row, index) => {
18933
+ const { error, value } = schema2.validate(row, {
18934
+ abortEarly: false,
18935
+ convert: true
18936
+ });
18937
+ if (error) {
18938
+ invalidRows.push({
18939
+ row: index + 2,
18940
+ data: row,
18941
+ errors: error.details.map((d) => d.message)
18942
+ });
18943
+ return;
18944
+ }
18945
+ validRows.push(value);
18946
+ });
18947
+ const vehicles = validRows.map(mapRowToVehicle);
18948
+ let data = {
18949
+ matchedCount: 0,
18950
+ modifiedCount: 0,
18951
+ upsertedCount: 0
18952
+ };
18953
+ if (vehicles.length > 0) {
18954
+ data = await _bulkUpsertVehicles(vehicles);
18955
+ }
18864
18956
  res.status(200).json({
18865
18957
  message: "Excel import completed.",
18866
18958
  sheetName: worksheet.name,
18867
- count: rows.length,
18959
+ totalRows: rows.length,
18960
+ validRows: validRows.length,
18961
+ invalidRows: invalidRows.length,
18962
+ validationErrors: invalidRows,
18868
18963
  data
18869
18964
  });
18870
18965
  fs.unlink(req.file.path, () => {
@@ -19198,69 +19293,6 @@ function useVehicleController() {
19198
19293
  return;
19199
19294
  }
19200
19295
  }
19201
- async function bulkUpsertVehicles(req, res, next) {
19202
- const items = Array.isArray(req.body) ? req.body : req.body?.items;
19203
- if (!Array.isArray(items) || items.length === 0) {
19204
- next(new BadRequestError83("A non-empty array of vehicles is required."));
19205
- return;
19206
- }
19207
- const success = [];
19208
- const failed = [];
19209
- try {
19210
- for (let index = 0; index < items.length; index++) {
19211
- const item = items[index];
19212
- const schema2 = Joi46.object({
19213
- fullName: Joi46.string().trim().required(),
19214
- category: Joi46.string().trim().lowercase().valid(...Object.values(PersonTypes)).required(),
19215
- type: Joi46.string().trim().lowercase().valid(...Object.values(VehicleType)),
19216
- phoneNumber: Joi46.string().trim().allow(null, "").optional(),
19217
- block: Joi46.number().integer().min(1).required(),
19218
- level: Joi46.string().trim().required(),
19219
- unit: Joi46.string().trim().required(),
19220
- plateNumber: Joi46.string().trim().uppercase().required(),
19221
- vehicleModel: Joi46.string().trim().required(),
19222
- vehicleColor: Joi46.string().trim().required(),
19223
- subscriptionExpiry: Joi46.string().trim().allow(null, "").optional()
19224
- });
19225
- const { error, value } = schema2.validate(item, {
19226
- abortEarly: false
19227
- });
19228
- if (error) {
19229
- const message = error.details.map((d) => d.message).join(", ");
19230
- failed.push({
19231
- index,
19232
- item,
19233
- message
19234
- });
19235
- continue;
19236
- }
19237
- try {
19238
- const data = await _bulkUpsertVehicles(value);
19239
- success.push({
19240
- index,
19241
- data
19242
- });
19243
- } catch (error2) {
19244
- failed.push({
19245
- index,
19246
- item,
19247
- message: error2.message
19248
- });
19249
- }
19250
- }
19251
- res.status(201).json({
19252
- message: "Bulk vehicle import completed.",
19253
- successCount: success.length,
19254
- failedCount: failed.length,
19255
- success,
19256
- failed
19257
- });
19258
- } catch (error) {
19259
- logger65.log({ level: "error", message: error.message });
19260
- next(error);
19261
- return;
19262
- }
19263
- }
19264
19296
  return {
19265
19297
  add,
19266
19298
  getVehicles,
@@ -19272,7 +19304,6 @@ function useVehicleController() {
19272
19304
  getVehiclesByNRIC,
19273
19305
  reactivateVehicleById,
19274
19306
  getAllVehiclesByUnitId,
19275
- bulkUpsertVehicles,
19276
19307
  uploadCsvVehicles,
19277
19308
  uploadExcelVehicles
19278
19309
  };
@@ -20047,21 +20078,6 @@ function useCustomerSiteController() {
20047
20078
  import { BadRequestError as BadRequestError89, logger as logger69 } from "@7365admin1/node-server-utils";
20048
20079
  import Joi51 from "joi";
20049
20080
  import { ObjectId as ObjectId51 } from "mongodb";
20050
-
20051
- // src/models/base.model.ts
20052
- var AppServiceType = /* @__PURE__ */ ((AppServiceType2) => {
20053
- AppServiceType2["REAL_ESTATE_DEVELOPER"] = "real_estate_developer";
20054
- AppServiceType2["PROPERTY_MANAGEMENT_AGENCY"] = "property_management_agency";
20055
- AppServiceType2["SECURITY_AGENCY"] = "security_agency";
20056
- AppServiceType2["CLEANING_SERVICES"] = "cleaning_services";
20057
- AppServiceType2["MECHANICAL_ELECTRICAL_SERVICES"] = "mechanical_electrical_services";
20058
- AppServiceType2["LANDSCAPING_SERVICES"] = "landscaping_services";
20059
- AppServiceType2["PEST_CONTROL_SERVICES"] = "pest_control_services";
20060
- AppServiceType2["POOL_MAINTENANCE_SERVICES"] = "pool_maintenance_services";
20061
- return AppServiceType2;
20062
- })(AppServiceType || {});
20063
-
20064
- // src/models/attendance-settings.model.ts
20065
20081
  var attendanceSettingsSchema = Joi51.object({
20066
20082
  site: Joi51.string().hex().required(),
20067
20083
  serviceType: Joi51.string().valid(...Object.values(AppServiceType)).required(),
@@ -31696,6 +31712,95 @@ function useSiteUnitBillingRepo() {
31696
31712
  throw error;
31697
31713
  }
31698
31714
  }
31715
+ async function getResidentUserUnsettledBilling({
31716
+ search = "",
31717
+ page = 1,
31718
+ limit = 10,
31719
+ sort = {},
31720
+ status = "active",
31721
+ site = "",
31722
+ paymentStatus = "all",
31723
+ month,
31724
+ year,
31725
+ unitId
31726
+ }, session) {
31727
+ page = page > 0 ? page - 1 : 0;
31728
+ let dateExpr = {};
31729
+ if (month && year) {
31730
+ const monthNum = parseInt(month, 10);
31731
+ const yearNum = parseInt(year, 10);
31732
+ const startDate = new Date(yearNum, monthNum - 1, 1);
31733
+ const endDate = new Date(yearNum, monthNum, 1);
31734
+ dateExpr.createdAt = {
31735
+ $gte: startDate,
31736
+ $lt: endDate
31737
+ };
31738
+ }
31739
+ const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
31740
+ const query = {
31741
+ ...paymentStatus === "all" ? { paymentStatus: { $in: ["failed", "overdue"] } } : { paymentStatus },
31742
+ status,
31743
+ ...search && {
31744
+ $or: [
31745
+ { unitOwner: { $regex: search, $options: "i" } },
31746
+ { billName: { $regex: search, $options: "i" } },
31747
+ { unit: { $regex: unitSearchRegex, $options: "i" } },
31748
+ {
31749
+ $expr: {
31750
+ $regexMatch: {
31751
+ input: { $toString: "$amountPaid" },
31752
+ regex: search,
31753
+ options: "i"
31754
+ }
31755
+ }
31756
+ }
31757
+ ]
31758
+ },
31759
+ ...ObjectId86.isValid(site) && { site: new ObjectId86(site) },
31760
+ ...dateExpr,
31761
+ ...ObjectId86.isValid(unitId) && { unitId: new ObjectId86(unitId) }
31762
+ };
31763
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
31764
+ try {
31765
+ const basePipeline = [
31766
+ { $match: query },
31767
+ { $sort: sort },
31768
+ { $skip: page * limit },
31769
+ { $limit: limit },
31770
+ {
31771
+ $lookup: {
31772
+ from: "site.billing.items",
31773
+ localField: "billItem",
31774
+ foreignField: "_id",
31775
+ pipeline: [
31776
+ {
31777
+ $project: {
31778
+ _id: 1,
31779
+ totalAmount: 1
31780
+ }
31781
+ }
31782
+ ],
31783
+ as: "billDetails"
31784
+ }
31785
+ },
31786
+ {
31787
+ $unwind: {
31788
+ path: "$billDetails",
31789
+ preserveNullAndEmptyArrays: true
31790
+ }
31791
+ }
31792
+ ];
31793
+ const [items, countResult] = await Promise.all([
31794
+ collection.aggregate(basePipeline, { session }).toArray(),
31795
+ collection.aggregate([{ $match: query }, { $count: "total" }], { session }).toArray()
31796
+ ]);
31797
+ const totalCount = countResult[0]?.total || 0;
31798
+ const data = paginate37(items, page, limit, totalCount);
31799
+ return data;
31800
+ } catch (error) {
31801
+ throw error;
31802
+ }
31803
+ }
31699
31804
  function delCachedData() {
31700
31805
  delNamespace().then(() => {
31701
31806
  logger117.log({
@@ -31728,7 +31833,8 @@ function useSiteUnitBillingRepo() {
31728
31833
  updateById,
31729
31834
  deleteById,
31730
31835
  getUnitBillingBySite,
31731
- getResidentUserBilling
31836
+ getResidentUserBilling,
31837
+ getResidentUserUnsettledBilling
31732
31838
  };
31733
31839
  }
31734
31840
 
@@ -31920,7 +32026,8 @@ function useSiteUnitBillingController() {
31920
32026
  updateById: _updateById,
31921
32027
  deleteById: _deleteById,
31922
32028
  getById: _getById,
31923
- getResidentUserBilling: _getResidentUserBilling
32029
+ getResidentUserBilling: _getResidentUserBilling,
32030
+ getResidentUserUnsettledBilling: _getResidentUserUnsettledBilling
31924
32031
  } = useSiteUnitBillingRepo();
31925
32032
  async function add(req, res, next) {
31926
32033
  const data = { ...req.body };
@@ -32112,13 +32219,65 @@ function useSiteUnitBillingController() {
32112
32219
  return;
32113
32220
  }
32114
32221
  }
32222
+ async function getResidentUserUnsettledBilling(req, res, next) {
32223
+ const validation = Joi84.object({
32224
+ page: Joi84.number().integer().min(1).allow("", null).default(1),
32225
+ limit: Joi84.number().integer().min(1).max(100).allow("", null).default(10),
32226
+ status: Joi84.string().optional().allow(null, ""),
32227
+ search: Joi84.string().optional().allow(null, ""),
32228
+ site: Joi84.string().required(),
32229
+ paymentStatus: Joi84.string().optional().allow(null, ""),
32230
+ month: Joi84.string().optional().allow(null, ""),
32231
+ year: Joi84.string().optional().allow(null, ""),
32232
+ unitId: Joi84.string().optional().allow(null, "")
32233
+ });
32234
+ const query = { ...req.query };
32235
+ const { error } = validation.validate(query, {
32236
+ abortEarly: false
32237
+ });
32238
+ if (error) {
32239
+ const messages = error.details.map((d) => d.message).join(", ");
32240
+ logger119.log({ level: "error", message: messages });
32241
+ next(new BadRequestError140(messages));
32242
+ return;
32243
+ }
32244
+ const search = req.query.search ?? "";
32245
+ const page = parseInt(req.query.page ?? "1");
32246
+ const limit = parseInt(req.query.limit ?? "10");
32247
+ const status = req.query.status ?? "active";
32248
+ const site = req.query.site;
32249
+ const paymentStatus = req.query.paymentStatus ?? "awaiting_payment";
32250
+ const month = req.query.month ?? "";
32251
+ const year = req.query.year ?? "";
32252
+ const unitId = req.query.unitId ?? "";
32253
+ try {
32254
+ const data = await _getResidentUserUnsettledBilling({
32255
+ search,
32256
+ page,
32257
+ limit,
32258
+ status,
32259
+ site,
32260
+ paymentStatus,
32261
+ month,
32262
+ year,
32263
+ unitId
32264
+ });
32265
+ res.status(200).json(data);
32266
+ return;
32267
+ } catch (error2) {
32268
+ logger119.log({ level: "error", message: error2.message });
32269
+ next(error2);
32270
+ return;
32271
+ }
32272
+ }
32115
32273
  return {
32116
32274
  add,
32117
32275
  getAll,
32118
32276
  getById,
32119
32277
  updateById,
32120
32278
  deleteById,
32121
- getResidentUserBilling
32279
+ getResidentUserBilling,
32280
+ getResidentUserUnsettledBilling
32122
32281
  };
32123
32282
  }
32124
32283
 
@@ -34302,6 +34461,61 @@ function UseAccessManagementRepo() {
34302
34461
  await session?.endSession();
34303
34462
  }
34304
34463
  }
34464
+ async function getBlockLevelAndUnitListRepo(params) {
34465
+ const site = new ObjectId89(params.site);
34466
+ try {
34467
+ const blocks = await collectionName("buildings").aggregate([
34468
+ {
34469
+ $match: {
34470
+ site,
34471
+ status: { $eq: "active" }
34472
+ }
34473
+ },
34474
+ {
34475
+ $project: {
34476
+ _id: 1,
34477
+ name: 1,
34478
+ block: 1
34479
+ }
34480
+ },
34481
+ {
34482
+ $lookup: {
34483
+ from: "building-levels",
34484
+ localField: "_id",
34485
+ foreignField: "block",
34486
+ as: "levels",
34487
+ pipeline: [
34488
+ {
34489
+ $match: { status: { $ne: "deleted" } }
34490
+ },
34491
+ {
34492
+ $lookup: {
34493
+ from: "building-units",
34494
+ localField: "_id",
34495
+ foreignField: "level",
34496
+ pipeline: [
34497
+ { $match: { status: { $ne: "deleted" } } },
34498
+ { $project: { _id: 1, name: 1, buildingName: 1, level: 1, block: 1 } }
34499
+ ],
34500
+ as: "units"
34501
+ }
34502
+ },
34503
+ {
34504
+ $match: { "units.0": { $exists: true } }
34505
+ }
34506
+ ]
34507
+ }
34508
+ },
34509
+ // ✅ Filter out buildings with no levels early
34510
+ {
34511
+ $match: { "levels.0": { $exists: true } }
34512
+ }
34513
+ ]).toArray();
34514
+ return blocks;
34515
+ } catch (error) {
34516
+ throw new Error(error.message);
34517
+ }
34518
+ }
34305
34519
  return {
34306
34520
  createIndexes,
34307
34521
  createIndexForEntrypass,
@@ -34331,7 +34545,8 @@ function UseAccessManagementRepo() {
34331
34545
  vmsgenerateQrCodesRepo,
34332
34546
  addVisitorAccessCardRepo,
34333
34547
  signQrCodeRepo,
34334
- checkoutVisitorRepo
34548
+ checkoutVisitorRepo,
34549
+ getBlockLevelAndUnitListRepo
34335
34550
  };
34336
34551
  }
34337
34552
 
@@ -34370,7 +34585,8 @@ function useAccessManagementSvc() {
34370
34585
  vmsgenerateQrCodesRepo,
34371
34586
  addVisitorAccessCardRepo,
34372
34587
  signQrCodeRepo,
34373
- checkoutVisitorRepo
34588
+ checkoutVisitorRepo,
34589
+ getBlockLevelAndUnitListRepo
34374
34590
  } = UseAccessManagementRepo();
34375
34591
  const addPhysicalCardSvc = async (payload) => {
34376
34592
  try {
@@ -34654,6 +34870,14 @@ function useAccessManagementSvc() {
34654
34870
  throw new Error(err.message);
34655
34871
  }
34656
34872
  };
34873
+ const getBlockLevelAndUnitListSvc = async (params) => {
34874
+ try {
34875
+ const response = await getBlockLevelAndUnitListRepo({ ...params });
34876
+ return response;
34877
+ } catch (err) {
34878
+ throw new Error(err.message);
34879
+ }
34880
+ };
34657
34881
  return {
34658
34882
  addPhysicalCardSvc,
34659
34883
  addNonPhysicalCardSvc,
@@ -34687,7 +34911,8 @@ function useAccessManagementSvc() {
34687
34911
  vmsgenerateQrCodesSvc,
34688
34912
  addVisitorAccessCardSvc,
34689
34913
  signQrCodeSvc,
34690
- checkoutVisitorSvc
34914
+ checkoutVisitorSvc,
34915
+ getBlockLevelAndUnitListSvc
34691
34916
  };
34692
34917
  }
34693
34918
 
@@ -34726,7 +34951,8 @@ function useAccessManagementController() {
34726
34951
  vmsgenerateQrCodesSvc,
34727
34952
  addVisitorAccessCardSvc,
34728
34953
  signQrCodeSvc,
34729
- checkoutVisitorSvc
34954
+ checkoutVisitorSvc,
34955
+ getBlockLevelAndUnitListSvc
34730
34956
  } = useAccessManagementSvc();
34731
34957
  const addPhysicalCard = async (req, res) => {
34732
34958
  try {
@@ -35473,6 +35699,21 @@ function useAccessManagementController() {
35473
35699
  const removeAccessCard = async ({ cardNo, staffNo, url }) => {
35474
35700
  return removeAccessGroup({ cardNo, staffNo, url });
35475
35701
  };
35702
+ const getBlockLevelAndUnitList = async (req, res) => {
35703
+ try {
35704
+ const { site } = req.query;
35705
+ if (!site || typeof site !== "string") {
35706
+ throw new Error("Site is required");
35707
+ }
35708
+ const result = await getBlockLevelAndUnitListSvc({ site });
35709
+ return res.status(200).json({ message: "Success", data: result });
35710
+ } catch (error) {
35711
+ return res.status(400).json({
35712
+ data: null,
35713
+ message: error.message
35714
+ });
35715
+ }
35716
+ };
35476
35717
  return {
35477
35718
  addPhysicalCard,
35478
35719
  addNonPhysicalCard,
@@ -35504,7 +35745,8 @@ function useAccessManagementController() {
35504
35745
  addVisitorAccessCard,
35505
35746
  signQrCode,
35506
35747
  checkoutVisitor,
35507
- removeAccessCard
35748
+ removeAccessCard,
35749
+ getBlockLevelAndUnitList
35508
35750
  };
35509
35751
  }
35510
35752
 
@@ -49393,6 +49635,7 @@ function useRoleControllerV2() {
49393
49635
  export {
49394
49636
  ANPRMode,
49395
49637
  AccessTypeProps,
49638
+ AppServiceType,
49396
49639
  BuildingStatus,
49397
49640
  BulletinRecipient,
49398
49641
  BulletinSort,