@7365admin1/core 3.21.0 → 3.22.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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +362 -85
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +362 -85
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -2014,6 +2014,14 @@ declare function useBuildingService(): {
|
|
|
2014
2014
|
uploadSpreadsheetBuilding: (data: any[], site: string) => Promise<{
|
|
2015
2015
|
buildingPayloads: any[];
|
|
2016
2016
|
buildingUnitPayloads: any[];
|
|
2017
|
+
insertedBuildings: number;
|
|
2018
|
+
insertedUnits: number;
|
|
2019
|
+
skippedRows: {
|
|
2020
|
+
row: number;
|
|
2021
|
+
block: number;
|
|
2022
|
+
name: string;
|
|
2023
|
+
reason: string;
|
|
2024
|
+
}[];
|
|
2017
2025
|
}>;
|
|
2018
2026
|
};
|
|
2019
2027
|
|
package/dist/index.js
CHANGED
|
@@ -24143,6 +24143,7 @@ function useDahuaService() {
|
|
|
24143
24143
|
return match ? match[1] : null;
|
|
24144
24144
|
}
|
|
24145
24145
|
async function addPlateNumber(value) {
|
|
24146
|
+
console.log("addPlateNumber called with value:", value);
|
|
24146
24147
|
let recno = null;
|
|
24147
24148
|
const validation = import_joi40.default.object({
|
|
24148
24149
|
host: import_joi40.default.string().required(),
|
|
@@ -24160,7 +24161,7 @@ function useDahuaService() {
|
|
|
24160
24161
|
throw new import_node_server_utils72.BadRequestError(`Validation error: ${validationError.message}`);
|
|
24161
24162
|
}
|
|
24162
24163
|
try {
|
|
24163
|
-
value.owner = String(value.owner ?? "").substring(0, 15) || "unknown";
|
|
24164
|
+
value.owner = String(value.owner ?? "").replace(/['"]/g, "").replace(/[\/\\]/g, " - ").substring(0, 15).trim() || "unknown";
|
|
24164
24165
|
const formatDahuaDate2 = (dateStr, fallbackYearsAhead = 0) => {
|
|
24165
24166
|
const date = dateStr ? new Date(dateStr) : /* @__PURE__ */ new Date();
|
|
24166
24167
|
if (!dateStr) {
|
|
@@ -27027,58 +27028,25 @@ function useVehicleService() {
|
|
|
27027
27028
|
for (const camera of siteCameras) {
|
|
27028
27029
|
const { host, username, password } = camera;
|
|
27029
27030
|
const plateNumber2 = vehicleValue.plateNumber;
|
|
27030
|
-
const
|
|
27031
|
+
const dahuaPayload = {
|
|
27031
27032
|
host,
|
|
27032
27033
|
username,
|
|
27033
27034
|
password,
|
|
27034
27035
|
plateNumber: plateNumber2,
|
|
27035
|
-
mode: _mode
|
|
27036
|
+
mode: _mode,
|
|
27037
|
+
owner,
|
|
27038
|
+
...startDateDahua ? { start: startDateDahua } : {},
|
|
27039
|
+
...endDateDahua ? { end: endDateDahua } : {}
|
|
27036
27040
|
};
|
|
27037
|
-
const
|
|
27038
|
-
const
|
|
27039
|
-
|
|
27040
|
-
|
|
27041
|
-
|
|
27042
|
-
|
|
27043
|
-
|
|
27044
|
-
plateNumber: plateNumber2,
|
|
27045
|
-
mode: _mode,
|
|
27046
|
-
owner,
|
|
27047
|
-
...startDateDahua ? { start: startDateDahua } : {},
|
|
27048
|
-
...endDateDahua ? { end: endDateDahua } : {}
|
|
27049
|
-
};
|
|
27050
|
-
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
27051
|
-
const responseStatus = dahuaResponse?.statusCode || dahuaResponse?.status || dahuaResponse?.res?.status || "unknown";
|
|
27052
|
-
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
27053
|
-
if (responseStatus !== 200) {
|
|
27054
|
-
throw new import_node_server_utils78.BadRequestError(
|
|
27055
|
-
`Failed to add plate number to ANPR ${_type}: status=${responseStatus}, response=${responseData}`
|
|
27056
|
-
);
|
|
27057
|
-
}
|
|
27058
|
-
vehicleValue.recNo = responseData.split("=")[1]?.trim();
|
|
27059
|
-
} else {
|
|
27060
|
-
const dahuaPayload = {
|
|
27061
|
-
host,
|
|
27062
|
-
username,
|
|
27063
|
-
password,
|
|
27064
|
-
plateNumber: plateNumber2,
|
|
27065
|
-
recno: parsed.recNo,
|
|
27066
|
-
mode: _mode,
|
|
27067
|
-
...startDateDahua ? { start: startDateDahua } : {},
|
|
27068
|
-
...endDateDahua ? { end: endDateDahua } : {},
|
|
27069
|
-
owner,
|
|
27070
|
-
isOpenGate: true
|
|
27071
|
-
};
|
|
27072
|
-
const dahuaResponse = await _updatePlateNumber(dahuaPayload);
|
|
27073
|
-
const responseStatus = dahuaResponse?.statusCode || dahuaResponse?.status || dahuaResponse?.res?.status || "unknown";
|
|
27074
|
-
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
27075
|
-
if (responseStatus !== 200) {
|
|
27076
|
-
throw new import_node_server_utils78.BadRequestError(
|
|
27077
|
-
`Failed to update plate number to ANPR ${_type}: status=${responseStatus}, response=${responseData}`
|
|
27078
|
-
);
|
|
27079
|
-
}
|
|
27080
|
-
vehicleValue.recNo = parsed.recNo;
|
|
27041
|
+
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
27042
|
+
const responseStatus = dahuaResponse?.statusCode || dahuaResponse?.status || dahuaResponse?.res?.status || "unknown";
|
|
27043
|
+
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
27044
|
+
if (responseStatus !== 200) {
|
|
27045
|
+
throw new import_node_server_utils78.BadRequestError(
|
|
27046
|
+
`Failed to add plate number to ANPR ${_type}: status=${responseStatus}, response=${responseData}`
|
|
27047
|
+
);
|
|
27081
27048
|
}
|
|
27049
|
+
vehicleValue.recNo = responseData.split("=")[1]?.trim();
|
|
27082
27050
|
if (value.peopleId && vehicleValue.recNo) {
|
|
27083
27051
|
await _pushVehicleById(
|
|
27084
27052
|
value.peopleId,
|
|
@@ -28609,10 +28577,22 @@ function useBuildingRepo() {
|
|
|
28609
28577
|
{
|
|
28610
28578
|
$lookup: {
|
|
28611
28579
|
from: "building-levels",
|
|
28612
|
-
|
|
28613
|
-
|
|
28580
|
+
let: {
|
|
28581
|
+
buildingId: "$_id",
|
|
28582
|
+
buildingLevelIds: { $ifNull: ["$levels", []] }
|
|
28583
|
+
},
|
|
28614
28584
|
pipeline: [
|
|
28615
|
-
{
|
|
28585
|
+
{
|
|
28586
|
+
$match: {
|
|
28587
|
+
status: "active" /* ACTIVE */,
|
|
28588
|
+
$expr: {
|
|
28589
|
+
$or: [
|
|
28590
|
+
{ $in: ["$_id", "$$buildingLevelIds"] },
|
|
28591
|
+
{ $eq: ["$blockId", "$$buildingId"] }
|
|
28592
|
+
]
|
|
28593
|
+
}
|
|
28594
|
+
}
|
|
28595
|
+
},
|
|
28616
28596
|
{ $project: { _id: 1, name: 1 } }
|
|
28617
28597
|
],
|
|
28618
28598
|
as: "levels"
|
|
@@ -29179,6 +29159,12 @@ function useBuildingLevelRepo() {
|
|
|
29179
29159
|
message: `Failed to clear cache namespace for ${building_level_namespace_collection}: ${err.message}`
|
|
29180
29160
|
});
|
|
29181
29161
|
});
|
|
29162
|
+
delBuildingNamespace().catch((err) => {
|
|
29163
|
+
import_node_server_utils87.logger.log({
|
|
29164
|
+
level: "error",
|
|
29165
|
+
message: `Failed to clear buildings cache: ${err.message}`
|
|
29166
|
+
});
|
|
29167
|
+
});
|
|
29182
29168
|
}
|
|
29183
29169
|
async function add(value, session) {
|
|
29184
29170
|
try {
|
|
@@ -29378,12 +29364,6 @@ function useBuildingLevelRepo() {
|
|
|
29378
29364
|
{ session }
|
|
29379
29365
|
);
|
|
29380
29366
|
delCachedData();
|
|
29381
|
-
delBuildingNamespace().catch((err) => {
|
|
29382
|
-
import_node_server_utils87.logger.log({
|
|
29383
|
-
level: "error",
|
|
29384
|
-
message: `Failed to clear buildings cache: ${err.message}`
|
|
29385
|
-
});
|
|
29386
|
-
});
|
|
29387
29367
|
return "Successfully deleted building level";
|
|
29388
29368
|
} catch (error) {
|
|
29389
29369
|
import_node_server_utils87.logger.log({
|
|
@@ -29546,6 +29526,18 @@ function useBuildingService() {
|
|
|
29546
29526
|
const { updateStatusById } = useFileRepo();
|
|
29547
29527
|
const { deleteFile } = useFileService();
|
|
29548
29528
|
const { bulkWriteLevels: _bulkWriteLevels } = useBuildingLevelRepo();
|
|
29529
|
+
function normalizeImportKey(value) {
|
|
29530
|
+
return String(value ?? "").trim().replace(/\s+/g, " ").toLowerCase();
|
|
29531
|
+
}
|
|
29532
|
+
function toObjectIdOrNull(value) {
|
|
29533
|
+
if (value instanceof import_mongodb54.ObjectId) {
|
|
29534
|
+
return value;
|
|
29535
|
+
}
|
|
29536
|
+
if (typeof value === "string" && import_mongodb54.ObjectId.isValid(value)) {
|
|
29537
|
+
return new import_mongodb54.ObjectId(value);
|
|
29538
|
+
}
|
|
29539
|
+
return null;
|
|
29540
|
+
}
|
|
29549
29541
|
async function add(value) {
|
|
29550
29542
|
const session = import_node_server_utils88.useAtlas.getClient()?.startSession();
|
|
29551
29543
|
if (!session) {
|
|
@@ -29688,47 +29680,161 @@ function useBuildingService() {
|
|
|
29688
29680
|
}
|
|
29689
29681
|
async function uploadSpreadsheetBuilding(data, site) {
|
|
29690
29682
|
const session = import_node_server_utils88.useAtlas.getClient()?.startSession();
|
|
29683
|
+
const db = import_node_server_utils88.useAtlas.getDb();
|
|
29691
29684
|
if (!session) {
|
|
29692
29685
|
throw new import_node_server_utils88.BadRequestError("Database session not available.");
|
|
29693
29686
|
}
|
|
29687
|
+
if (!db) {
|
|
29688
|
+
throw new import_node_server_utils88.BadRequestError("Database not available.");
|
|
29689
|
+
}
|
|
29694
29690
|
try {
|
|
29695
29691
|
session.startTransaction();
|
|
29696
29692
|
const blockMap = /* @__PURE__ */ new Map();
|
|
29693
|
+
const skippedRows = [];
|
|
29697
29694
|
for (const row of data) {
|
|
29698
29695
|
const block = row.block;
|
|
29699
29696
|
const name = row.name;
|
|
29697
|
+
const rowNumber = row.__rowNumber;
|
|
29700
29698
|
const rawLevel = row.level?.toString().trim();
|
|
29701
29699
|
const rawUnit = row.unit?.toString().trim();
|
|
29702
29700
|
const normalizedLevel = /^(na|n\/a)$/i.test(rawLevel) ? null : rawLevel;
|
|
29703
29701
|
const normalizedUnit = /^(na|n\/a)$/i.test(rawUnit) ? null : rawUnit;
|
|
29704
29702
|
if (!blockMap.has(block)) {
|
|
29705
|
-
blockMap.set(block, {
|
|
29703
|
+
blockMap.set(block, {
|
|
29704
|
+
name,
|
|
29705
|
+
levels: [],
|
|
29706
|
+
rows: [],
|
|
29707
|
+
unitsByLevel: /* @__PURE__ */ new Map(),
|
|
29708
|
+
unitRowsByLevel: /* @__PURE__ */ new Map()
|
|
29709
|
+
});
|
|
29706
29710
|
}
|
|
29707
29711
|
const blockEntry = blockMap.get(block);
|
|
29712
|
+
blockEntry.rows.push(rowNumber);
|
|
29708
29713
|
if (blockEntry.name !== name) {
|
|
29709
29714
|
blockEntry.name = `${blockEntry.name}, ${name}`;
|
|
29710
29715
|
}
|
|
29711
|
-
if (normalizedLevel && !blockEntry.levels.
|
|
29716
|
+
if (normalizedLevel && !blockEntry.levels.some(
|
|
29717
|
+
(level) => normalizeImportKey(level) === normalizeImportKey(normalizedLevel)
|
|
29718
|
+
)) {
|
|
29712
29719
|
blockEntry.levels.push(normalizedLevel);
|
|
29713
29720
|
}
|
|
29714
29721
|
if (normalizedLevel && normalizedUnit) {
|
|
29715
29722
|
if (!blockEntry.unitsByLevel.has(normalizedLevel)) {
|
|
29716
29723
|
blockEntry.unitsByLevel.set(normalizedLevel, []);
|
|
29717
29724
|
}
|
|
29725
|
+
if (!blockEntry.unitRowsByLevel.has(normalizedLevel)) {
|
|
29726
|
+
blockEntry.unitRowsByLevel.set(normalizedLevel, /* @__PURE__ */ new Map());
|
|
29727
|
+
}
|
|
29718
29728
|
const units = blockEntry.unitsByLevel.get(normalizedLevel);
|
|
29719
|
-
|
|
29729
|
+
const unitRows = blockEntry.unitRowsByLevel.get(normalizedLevel);
|
|
29730
|
+
if (!units.some(
|
|
29731
|
+
(unit) => normalizeImportKey(unit) === normalizeImportKey(normalizedUnit)
|
|
29732
|
+
)) {
|
|
29720
29733
|
units.push(normalizedUnit);
|
|
29721
29734
|
}
|
|
29735
|
+
unitRows.set(normalizedUnit, [
|
|
29736
|
+
...unitRows.get(normalizedUnit) ?? [],
|
|
29737
|
+
rowNumber
|
|
29738
|
+
]);
|
|
29722
29739
|
}
|
|
29723
29740
|
}
|
|
29724
29741
|
const buildingPayloads = [];
|
|
29725
29742
|
const buildingUnitPayloads = [];
|
|
29743
|
+
if (blockMap.size === 0) {
|
|
29744
|
+
await session.commitTransaction();
|
|
29745
|
+
return {
|
|
29746
|
+
buildingPayloads,
|
|
29747
|
+
buildingUnitPayloads,
|
|
29748
|
+
insertedBuildings: 0,
|
|
29749
|
+
insertedUnits: 0,
|
|
29750
|
+
skippedRows
|
|
29751
|
+
};
|
|
29752
|
+
}
|
|
29753
|
+
const buildingsCollection = db.collection(buildings_namespace_collection);
|
|
29754
|
+
const buildingLevelsCollection = db.collection(
|
|
29755
|
+
building_level_namespace_collection
|
|
29756
|
+
);
|
|
29757
|
+
const buildingUnitsCollection = db.collection(
|
|
29758
|
+
building_units_namespace_collection
|
|
29759
|
+
);
|
|
29760
|
+
const existingBuildings = await buildingsCollection.find(
|
|
29761
|
+
{
|
|
29762
|
+
site: new import_mongodb54.ObjectId(site),
|
|
29763
|
+
status: "active" /* ACTIVE */,
|
|
29764
|
+
$or: Array.from(blockMap.entries()).flatMap(([block, entry]) => [
|
|
29765
|
+
{ block },
|
|
29766
|
+
{ name: entry.name }
|
|
29767
|
+
])
|
|
29768
|
+
},
|
|
29769
|
+
{ session, projection: { block: 1, name: 1, levels: 1 } }
|
|
29770
|
+
).toArray();
|
|
29771
|
+
const existingBuildingsByBlock = new Map(
|
|
29772
|
+
existingBuildings.map((building) => [building.block, building])
|
|
29773
|
+
);
|
|
29774
|
+
const existingBuildingsByName = new Map(
|
|
29775
|
+
existingBuildings.map((building) => [building.name, building])
|
|
29776
|
+
);
|
|
29777
|
+
const uploadedNames = /* @__PURE__ */ new Set();
|
|
29726
29778
|
for (const [
|
|
29727
29779
|
block,
|
|
29728
|
-
{ name, levels, unitsByLevel }
|
|
29780
|
+
{ name, levels, rows, unitsByLevel, unitRowsByLevel }
|
|
29729
29781
|
] of blockMap.entries()) {
|
|
29730
|
-
const
|
|
29731
|
-
const
|
|
29782
|
+
const conflictReasons = [];
|
|
29783
|
+
const existingBuildingByBlock = existingBuildingsByBlock.get(block);
|
|
29784
|
+
const existingBuildingByName = existingBuildingsByName.get(name);
|
|
29785
|
+
if (existingBuildingByBlock && existingBuildingByBlock.name !== name) {
|
|
29786
|
+
conflictReasons.push(
|
|
29787
|
+
`Block ${block} already exists with building name "${existingBuildingByBlock.name}".`
|
|
29788
|
+
);
|
|
29789
|
+
}
|
|
29790
|
+
if (existingBuildingByName && existingBuildingByName.block !== block) {
|
|
29791
|
+
conflictReasons.push(
|
|
29792
|
+
`Building name "${name}" already exists in block ${existingBuildingByName.block}.`
|
|
29793
|
+
);
|
|
29794
|
+
}
|
|
29795
|
+
if (uploadedNames.has(name)) {
|
|
29796
|
+
conflictReasons.push(
|
|
29797
|
+
`Building name "${name}" is duplicated in this spreadsheet.`
|
|
29798
|
+
);
|
|
29799
|
+
}
|
|
29800
|
+
if (conflictReasons.length) {
|
|
29801
|
+
rows.forEach((row) => {
|
|
29802
|
+
skippedRows.push({
|
|
29803
|
+
row,
|
|
29804
|
+
block,
|
|
29805
|
+
name,
|
|
29806
|
+
reason: conflictReasons.join(" ")
|
|
29807
|
+
});
|
|
29808
|
+
});
|
|
29809
|
+
continue;
|
|
29810
|
+
}
|
|
29811
|
+
uploadedNames.add(name);
|
|
29812
|
+
const existingBuilding = existingBuildingByBlock ?? null;
|
|
29813
|
+
const buildingId = existingBuilding?._id ?? new import_mongodb54.ObjectId();
|
|
29814
|
+
const existingLevelIds = (existingBuilding?.levels ?? []).map(toObjectIdOrNull).filter(
|
|
29815
|
+
(levelId) => Boolean(levelId)
|
|
29816
|
+
);
|
|
29817
|
+
const existingLevels = existingBuilding || existingLevelIds.length > 0 ? await buildingLevelsCollection.find(
|
|
29818
|
+
{
|
|
29819
|
+
$or: [
|
|
29820
|
+
{ blockId: buildingId },
|
|
29821
|
+
{ blockId: buildingId.toString() },
|
|
29822
|
+
...existingLevelIds.length > 0 ? [{ _id: { $in: existingLevelIds } }] : []
|
|
29823
|
+
],
|
|
29824
|
+
status: "active" /* ACTIVE */
|
|
29825
|
+
},
|
|
29826
|
+
{ session, projection: { name: 1 } }
|
|
29827
|
+
).toArray() : [];
|
|
29828
|
+
const levelNameToId = new Map(
|
|
29829
|
+
existingLevels.map((level) => [
|
|
29830
|
+
normalizeImportKey(level.name),
|
|
29831
|
+
level._id
|
|
29832
|
+
])
|
|
29833
|
+
);
|
|
29834
|
+
const missingLevels = (levels ?? []).filter(
|
|
29835
|
+
(levelName) => !levelNameToId.has(normalizeImportKey(levelName))
|
|
29836
|
+
);
|
|
29837
|
+
const levelsPayload = missingLevels.map((levelName) => ({
|
|
29732
29838
|
blockId: buildingId.toString(),
|
|
29733
29839
|
site: site.toString(),
|
|
29734
29840
|
name: levelName,
|
|
@@ -29741,23 +29847,80 @@ function useBuildingService() {
|
|
|
29741
29847
|
if (levelsPayload.length > 0) {
|
|
29742
29848
|
insertedLevels = await _bulkWriteLevels(levelsPayload, session);
|
|
29743
29849
|
}
|
|
29744
|
-
const levelNameToId = /* @__PURE__ */ new Map();
|
|
29745
29850
|
if (insertedLevels) {
|
|
29746
29851
|
insertedLevels.levels.forEach((lvl) => {
|
|
29747
|
-
levelNameToId.set(lvl.name, lvl._id);
|
|
29852
|
+
levelNameToId.set(normalizeImportKey(lvl.name), lvl._id);
|
|
29748
29853
|
});
|
|
29749
29854
|
}
|
|
29750
|
-
const
|
|
29751
|
-
|
|
29752
|
-
|
|
29753
|
-
|
|
29754
|
-
|
|
29755
|
-
|
|
29756
|
-
|
|
29757
|
-
|
|
29855
|
+
const levelIds = Array.from(levelNameToId.values());
|
|
29856
|
+
if (existingBuilding) {
|
|
29857
|
+
if (insertedLevels) {
|
|
29858
|
+
await _updateById(
|
|
29859
|
+
buildingId,
|
|
29860
|
+
{
|
|
29861
|
+
levels: [
|
|
29862
|
+
...existingLevelIds,
|
|
29863
|
+
...Object.values(insertedLevels.insertedIds)
|
|
29864
|
+
]
|
|
29865
|
+
},
|
|
29866
|
+
session
|
|
29867
|
+
);
|
|
29868
|
+
}
|
|
29869
|
+
} else {
|
|
29870
|
+
const buildingPayload = {
|
|
29871
|
+
_id: buildingId,
|
|
29872
|
+
site,
|
|
29873
|
+
block,
|
|
29874
|
+
name,
|
|
29875
|
+
levels: levelIds
|
|
29876
|
+
};
|
|
29877
|
+
const buildingResult = await _add(buildingPayload, session);
|
|
29878
|
+
buildingPayloads.push({ ...buildingPayload, _id: buildingResult });
|
|
29879
|
+
}
|
|
29880
|
+
const existingUnits = levelIds.length > 0 ? await buildingUnitsCollection.find(
|
|
29881
|
+
{
|
|
29882
|
+
building: { $in: [buildingId, buildingId.toString()] },
|
|
29883
|
+
level: {
|
|
29884
|
+
$in: [
|
|
29885
|
+
...levelIds,
|
|
29886
|
+
...levelIds.map((levelId) => levelId.toString())
|
|
29887
|
+
]
|
|
29888
|
+
}
|
|
29889
|
+
},
|
|
29890
|
+
{ session, projection: { name: 1, level: 1 } }
|
|
29891
|
+
).toArray() : [];
|
|
29892
|
+
const existingUnitKeys = new Set(
|
|
29893
|
+
existingUnits.map(
|
|
29894
|
+
(unit) => `${unit.level.toString()}::${normalizeImportKey(unit.name)}`
|
|
29895
|
+
)
|
|
29896
|
+
);
|
|
29758
29897
|
Array.from(unitsByLevel.entries()).forEach(([levelName, units]) => {
|
|
29759
|
-
const levelId = levelNameToId.get(levelName);
|
|
29898
|
+
const levelId = levelNameToId.get(normalizeImportKey(levelName));
|
|
29899
|
+
if (!levelId)
|
|
29900
|
+
return;
|
|
29760
29901
|
units.forEach((unit) => {
|
|
29902
|
+
const unitKey = `${levelId.toString()}::${normalizeImportKey(unit)}`;
|
|
29903
|
+
const unitRows = unitRowsByLevel.get(levelName)?.get(unit) ?? rows;
|
|
29904
|
+
if (existingUnitKeys.has(unitKey)) {
|
|
29905
|
+
unitRows.forEach((row) => {
|
|
29906
|
+
skippedRows.push({
|
|
29907
|
+
row,
|
|
29908
|
+
block,
|
|
29909
|
+
name,
|
|
29910
|
+
reason: `Unit "${unit}" already exists in level "${levelName}".`
|
|
29911
|
+
});
|
|
29912
|
+
});
|
|
29913
|
+
return;
|
|
29914
|
+
}
|
|
29915
|
+
existingUnitKeys.add(unitKey);
|
|
29916
|
+
unitRows.slice(1).forEach((row) => {
|
|
29917
|
+
skippedRows.push({
|
|
29918
|
+
row,
|
|
29919
|
+
block,
|
|
29920
|
+
name,
|
|
29921
|
+
reason: `Unit "${unit}" is duplicated in this spreadsheet for level "${levelName}".`
|
|
29922
|
+
});
|
|
29923
|
+
});
|
|
29761
29924
|
buildingUnitPayloads.push({
|
|
29762
29925
|
site: new import_mongodb54.ObjectId(site),
|
|
29763
29926
|
name: unit,
|
|
@@ -29783,9 +29946,17 @@ function useBuildingService() {
|
|
|
29783
29946
|
});
|
|
29784
29947
|
});
|
|
29785
29948
|
}
|
|
29786
|
-
|
|
29949
|
+
if (buildingUnitPayloads.length > 0) {
|
|
29950
|
+
await _bulkAddBuildingUnits(buildingUnitPayloads, session);
|
|
29951
|
+
}
|
|
29787
29952
|
await session.commitTransaction();
|
|
29788
|
-
return {
|
|
29953
|
+
return {
|
|
29954
|
+
buildingPayloads,
|
|
29955
|
+
buildingUnitPayloads,
|
|
29956
|
+
insertedBuildings: buildingPayloads.length,
|
|
29957
|
+
insertedUnits: buildingUnitPayloads.length,
|
|
29958
|
+
skippedRows
|
|
29959
|
+
};
|
|
29789
29960
|
} catch (error) {
|
|
29790
29961
|
await session.abortTransaction();
|
|
29791
29962
|
throw error;
|
|
@@ -29807,6 +29978,19 @@ var import_joi46 = __toESM(require("joi"));
|
|
|
29807
29978
|
var import_exceljs = __toESM(require("exceljs"));
|
|
29808
29979
|
var import_csv_parser = __toESM(require("csv-parser"));
|
|
29809
29980
|
var import_fs3 = __toESM(require("fs"));
|
|
29981
|
+
function normalizeSpreadsheetHeader(header) {
|
|
29982
|
+
let value = "";
|
|
29983
|
+
if (header && typeof header === "object" && "richText" in header && Array.isArray(header.richText)) {
|
|
29984
|
+
value = header.richText.map((part) => part.text ?? "").join("");
|
|
29985
|
+
} else if (header && typeof header === "object" && "text" in header) {
|
|
29986
|
+
value = String(header.text ?? "");
|
|
29987
|
+
} else if (header && typeof header === "object" && "result" in header) {
|
|
29988
|
+
value = String(header.result ?? "");
|
|
29989
|
+
} else {
|
|
29990
|
+
value = String(header ?? "");
|
|
29991
|
+
}
|
|
29992
|
+
return value.replace(/^\uFEFF/, "").trim().replace(/\(.*\)/, "").trim().toLowerCase();
|
|
29993
|
+
}
|
|
29810
29994
|
function useBuildingController() {
|
|
29811
29995
|
const {
|
|
29812
29996
|
getAll: _getAll,
|
|
@@ -30059,12 +30243,14 @@ function useBuildingController() {
|
|
|
30059
30243
|
}
|
|
30060
30244
|
const { originalname, path: path5 } = req.file;
|
|
30061
30245
|
const lowerName = originalname.toLowerCase();
|
|
30246
|
+
const expectedHeaders = ["block", "name", "level", "unit"];
|
|
30247
|
+
const requiredFields = ["block", "name"];
|
|
30062
30248
|
const rowSchema = import_joi46.default.object({
|
|
30063
30249
|
block: import_joi46.default.number().integer().min(0).required(),
|
|
30250
|
+
name: import_joi46.default.string().trim().required(),
|
|
30064
30251
|
level: import_joi46.default.alternatives().try(import_joi46.default.string(), import_joi46.default.number()).custom((value) => String(value)).optional().allow(null, ""),
|
|
30065
30252
|
unit: import_joi46.default.alternatives().try(import_joi46.default.string(), import_joi46.default.number()).custom((value) => String(value)).optional().allow(null, ""),
|
|
30066
30253
|
category: import_joi46.default.string().trim().optional().allow(null, ""),
|
|
30067
|
-
name: import_joi46.default.string().trim().optional().allow(null, ""),
|
|
30068
30254
|
email: import_joi46.default.string().email().lowercase().optional().allow(null, ""),
|
|
30069
30255
|
phoneNumber: import_joi46.default.string().trim().optional().allow(null, "")
|
|
30070
30256
|
});
|
|
@@ -30095,9 +30281,18 @@ function useBuildingController() {
|
|
|
30095
30281
|
return;
|
|
30096
30282
|
}
|
|
30097
30283
|
const headerRow = worksheet.getRow(1);
|
|
30098
|
-
const headers = (headerRow.values || []).slice(1).map(
|
|
30099
|
-
|
|
30284
|
+
const headers = (headerRow.values || []).slice(1).map((header) => normalizeSpreadsheetHeader(header));
|
|
30285
|
+
const missingHeaders = expectedHeaders.filter(
|
|
30286
|
+
(expected) => !headers.some((h) => h === expected)
|
|
30100
30287
|
);
|
|
30288
|
+
if (missingHeaders.length > 0) {
|
|
30289
|
+
next(
|
|
30290
|
+
new import_node_server_utils89.BadRequestError(
|
|
30291
|
+
`Invalid Excel format. Missing required columns: ${missingHeaders.join(", ")}. Expected columns: ${expectedHeaders.join(", ")}`
|
|
30292
|
+
)
|
|
30293
|
+
);
|
|
30294
|
+
return;
|
|
30295
|
+
}
|
|
30101
30296
|
worksheet.eachRow((row, rowNumber) => {
|
|
30102
30297
|
if (rowNumber === 1)
|
|
30103
30298
|
return;
|
|
@@ -30116,10 +30311,30 @@ function useBuildingController() {
|
|
|
30116
30311
|
const parsed = [];
|
|
30117
30312
|
import_fs3.default.createReadStream(path5).pipe(
|
|
30118
30313
|
(0, import_csv_parser.default)({
|
|
30119
|
-
mapHeaders: ({ header }) => header
|
|
30314
|
+
mapHeaders: ({ header }) => normalizeSpreadsheetHeader(header)
|
|
30120
30315
|
})
|
|
30121
|
-
).on("data", (row) =>
|
|
30316
|
+
).on("data", (row) => {
|
|
30317
|
+
if (Object.values(row).some(
|
|
30318
|
+
(v) => v !== "" && v !== null && v !== void 0
|
|
30319
|
+
)) {
|
|
30320
|
+
parsed.push(row);
|
|
30321
|
+
}
|
|
30322
|
+
}).on("end", () => resolve(parsed)).on("error", reject);
|
|
30122
30323
|
});
|
|
30324
|
+
if (rows.length > 0) {
|
|
30325
|
+
const csvHeaders = Object.keys(rows[0]);
|
|
30326
|
+
const missingHeaders = expectedHeaders.filter(
|
|
30327
|
+
(expected) => !csvHeaders.some((h) => h === expected)
|
|
30328
|
+
);
|
|
30329
|
+
if (missingHeaders.length > 0) {
|
|
30330
|
+
next(
|
|
30331
|
+
new import_node_server_utils89.BadRequestError(
|
|
30332
|
+
`Invalid CSV format. Missing required columns: ${missingHeaders.join(", ")}. Expected columns: ${expectedHeaders.join(", ")}`
|
|
30333
|
+
)
|
|
30334
|
+
);
|
|
30335
|
+
return;
|
|
30336
|
+
}
|
|
30337
|
+
}
|
|
30123
30338
|
} else {
|
|
30124
30339
|
next(
|
|
30125
30340
|
new import_node_server_utils89.BadRequestError("Only .xlsx, .xls, or .csv files are allowed.")
|
|
@@ -30129,6 +30344,23 @@ function useBuildingController() {
|
|
|
30129
30344
|
const validRows = [];
|
|
30130
30345
|
const invalidRows = [];
|
|
30131
30346
|
rows.forEach((row, index) => {
|
|
30347
|
+
const emptyCells = Object.entries(row).filter(
|
|
30348
|
+
([, value2]) => value2 === "" || value2 === null || value2 === void 0
|
|
30349
|
+
).map(([key]) => key);
|
|
30350
|
+
const emptyRequiredFields = emptyCells.filter(
|
|
30351
|
+
(cell) => requiredFields.includes(cell)
|
|
30352
|
+
);
|
|
30353
|
+
if (emptyRequiredFields.length > 0) {
|
|
30354
|
+
invalidRows.push({
|
|
30355
|
+
row: index + 2,
|
|
30356
|
+
// +2 because header is row 1
|
|
30357
|
+
data: row,
|
|
30358
|
+
errors: [
|
|
30359
|
+
`Required field(s) empty: ${emptyRequiredFields.join(", ")}`
|
|
30360
|
+
]
|
|
30361
|
+
});
|
|
30362
|
+
return;
|
|
30363
|
+
}
|
|
30132
30364
|
const { error, value } = rowSchema.validate(row, {
|
|
30133
30365
|
abortEarly: false,
|
|
30134
30366
|
stripUnknown: true
|
|
@@ -30141,19 +30373,64 @@ function useBuildingController() {
|
|
|
30141
30373
|
errors: error.details.map((d) => d.message)
|
|
30142
30374
|
});
|
|
30143
30375
|
} else {
|
|
30144
|
-
validRows.push(value);
|
|
30376
|
+
validRows.push({ ...value, __rowNumber: index + 2 });
|
|
30145
30377
|
}
|
|
30146
30378
|
});
|
|
30147
|
-
|
|
30379
|
+
let result;
|
|
30380
|
+
try {
|
|
30381
|
+
result = await _uploadSpreadsheetBuilding(validRows, site);
|
|
30382
|
+
} catch (uploadError) {
|
|
30383
|
+
import_node_server_utils89.logger.log({
|
|
30384
|
+
level: "error",
|
|
30385
|
+
message: `Upload error: ${uploadError.message}`
|
|
30386
|
+
});
|
|
30387
|
+
result = {
|
|
30388
|
+
insertedBuildings: 0,
|
|
30389
|
+
insertedUnits: 0,
|
|
30390
|
+
skippedRows: [
|
|
30391
|
+
{
|
|
30392
|
+
rowNumber: "all",
|
|
30393
|
+
reason: "Critical Upload Error",
|
|
30394
|
+
error: uploadError.message
|
|
30395
|
+
}
|
|
30396
|
+
]
|
|
30397
|
+
};
|
|
30398
|
+
}
|
|
30399
|
+
const allSkippedItems = [
|
|
30400
|
+
...invalidRows.map((item) => ({
|
|
30401
|
+
rowNumber: item.row,
|
|
30402
|
+
reason: "Validation Error",
|
|
30403
|
+
errors: item.errors,
|
|
30404
|
+
data: item.data
|
|
30405
|
+
})),
|
|
30406
|
+
...(result.skippedRows || []).map((item) => ({
|
|
30407
|
+
rowNumber: "row" in item ? item.row : item.rowNumber,
|
|
30408
|
+
reason: item.reason,
|
|
30409
|
+
block: "block" in item ? item.block : void 0,
|
|
30410
|
+
name: "name" in item ? item.name : void 0,
|
|
30411
|
+
data: item
|
|
30412
|
+
}))
|
|
30413
|
+
].sort((a, b) => {
|
|
30414
|
+
if (typeof a.rowNumber === "string")
|
|
30415
|
+
return 1;
|
|
30416
|
+
if (typeof b.rowNumber === "string")
|
|
30417
|
+
return -1;
|
|
30418
|
+
return a.rowNumber - b.rowNumber;
|
|
30419
|
+
});
|
|
30420
|
+
const skippedSummary = allSkippedItems.map((item, idx) => `${idx + 1}. Row ${item.rowNumber}: ${item.reason}`).join("\n");
|
|
30148
30421
|
res.status(200).json({
|
|
30149
|
-
message:
|
|
30422
|
+
message: `Spreadsheet import completed (buildings).${allSkippedItems.length > 0 ? "\n\nSkipped items:\n" + skippedSummary : ""}`,
|
|
30150
30423
|
fileName: originalname,
|
|
30151
30424
|
totalRows: rows.length,
|
|
30152
30425
|
validRows: validRows.length,
|
|
30153
30426
|
invalidRows: invalidRows.length,
|
|
30427
|
+
insertedBuildings: result.insertedBuildings,
|
|
30428
|
+
insertedUnits: result.insertedUnits,
|
|
30429
|
+
skippedRows: allSkippedItems.length,
|
|
30154
30430
|
validationErrors: invalidRows,
|
|
30155
|
-
|
|
30156
|
-
|
|
30431
|
+
skippedErrors: result.skippedRows,
|
|
30432
|
+
skippedItemsSummary: allSkippedItems,
|
|
30433
|
+
data: result
|
|
30157
30434
|
});
|
|
30158
30435
|
import_fs3.default.unlink(path5, () => {
|
|
30159
30436
|
});
|