fix: show remoteGisCount (505) as cu geometrie, add no-geom cleanup step
- UI: scan card now shows remoteGisCount instead of matchedCount (withGeometry) as the primary 'cu geometrie' number — this is the true GIS layer feature count - UI: workflow preview step 1 shows remoteGisCount for download count - UI: mismatch note reworded as secondary detail about cross-reference matching - Import: automatic cleanup step at start of syncNoGeometryParcels - Builds valid immovablePk set from fresh list (active + identification/area) - Deletes stale NO_GEOMETRY records not in the valid set - Reports cleaned count in result + progress note - NoGeomSyncResult type: added 'cleaned' field - Gitignore: temp-db-check.cjs
This commit is contained in:
BIN
Binary file not shown.
@@ -282,10 +282,14 @@ export async function POST(req: Request) {
|
||||
pushProgress();
|
||||
} else {
|
||||
noGeomImported = noGeomResult.imported;
|
||||
const cleanedNote =
|
||||
noGeomResult.cleaned > 0
|
||||
? `, ${noGeomResult.cleaned} vechi șterse`
|
||||
: "";
|
||||
note =
|
||||
noGeomImported > 0
|
||||
? `${noGeomImported} parcele noi fără geometrie importate`
|
||||
: "Nicio parcelă nouă fără geometrie";
|
||||
? `${noGeomImported} parcele noi fără geometrie importate${cleanedNote}`
|
||||
: `Nicio parcelă nouă fără geometrie${cleanedNote}`;
|
||||
pushProgress();
|
||||
}
|
||||
updatePhaseProgress(1, 1);
|
||||
|
||||
@@ -2523,7 +2523,7 @@ export function ParcelSyncModule() {
|
||||
{noGeomScan.localSyncFresh &&
|
||||
noGeomScan.localDbWithGeom > 0
|
||||
? "skip (date proaspete în DB)"
|
||||
: `descarcă ${noGeomScan.withGeometry.toLocaleString("ro-RO")} features`}
|
||||
: `descarcă ${noGeomScan.remoteGisCount.toLocaleString("ro-RO")} features`}
|
||||
</span>
|
||||
</li>
|
||||
{includeNoGeom && (
|
||||
@@ -2601,7 +2601,7 @@ export function ParcelSyncModule() {
|
||||
</span>{" "}
|
||||
imobile în eTerra:{" "}
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-medium">
|
||||
{noGeomScan.withGeometry.toLocaleString("ro-RO")}
|
||||
{noGeomScan.remoteGisCount.toLocaleString("ro-RO")}
|
||||
</span>{" "}
|
||||
cu geometrie,{" "}
|
||||
<span className="font-semibold text-amber-600 dark:text-amber-400">
|
||||
@@ -2609,19 +2609,15 @@ export function ParcelSyncModule() {
|
||||
</span>{" "}
|
||||
<span className="font-medium">fără geometrie</span>
|
||||
</p>
|
||||
{noGeomScan.remoteGisCount > 0 &&
|
||||
noGeomScan.remoteGisCount !==
|
||||
noGeomScan.withGeometry && (
|
||||
<p className="text-[10px] text-muted-foreground/70 mt-0.5">
|
||||
Layerul GIS are{" "}
|
||||
{noGeomScan.remoteGisCount.toLocaleString(
|
||||
"ro-RO",
|
||||
)}{" "}
|
||||
features, dar doar{" "}
|
||||
{noGeomScan.withGeometry.toLocaleString("ro-RO")}{" "}
|
||||
se potrivesc cu lista de imobile
|
||||
</p>
|
||||
)}
|
||||
{noGeomScan.withGeometry <
|
||||
noGeomScan.remoteGisCount && (
|
||||
<p className="text-[10px] text-muted-foreground/70 mt-0.5">
|
||||
{noGeomScan.withGeometry.toLocaleString("ro-RO")}{" "}
|
||||
din{" "}
|
||||
{noGeomScan.remoteGisCount.toLocaleString("ro-RO")}{" "}
|
||||
features GIS au corespondent în lista de imobile
|
||||
</p>
|
||||
)}
|
||||
<p className="text-[11px] text-muted-foreground mt-0.5">
|
||||
Cele fără geometrie există în baza de date eTerra dar
|
||||
nu au contur desenat în layerul GIS.
|
||||
|
||||
@@ -149,6 +149,7 @@ export type NoGeomScanResult = {
|
||||
export type NoGeomSyncResult = {
|
||||
imported: number;
|
||||
skipped: number;
|
||||
cleaned: number;
|
||||
errors: number;
|
||||
status: "done" | "error";
|
||||
error?: string;
|
||||
@@ -410,6 +411,7 @@ export async function syncNoGeometryParcels(
|
||||
return {
|
||||
imported: 0,
|
||||
skipped: 0,
|
||||
cleaned: 0,
|
||||
errors: 0,
|
||||
status: "error",
|
||||
error: `Nu s-a putut determina workspace-ul pentru SIRUTA ${siruta}`,
|
||||
@@ -420,7 +422,58 @@ export async function syncNoGeometryParcels(
|
||||
options?.onProgress?.(0, 1, "Descărcare listă imobile (fără geometrie)");
|
||||
const allImmovables = await fetchAllImmovables(client, siruta, wsPk);
|
||||
|
||||
// 2. Get existing features from DB
|
||||
// 2. Cleanup: remove stale/orphan no-geom records from DB
|
||||
// Build a set of valid immovablePks from the fresh immovable list.
|
||||
// Any NO_GEOMETRY record whose immovablePk is NOT in the fresh list
|
||||
// (or whose immovable is inactive/empty) gets deleted.
|
||||
const validImmPks = new Set<number>();
|
||||
for (const item of allImmovables) {
|
||||
const pk = Number(item.immovablePk ?? 0);
|
||||
if (pk > 0) {
|
||||
const status = typeof item.status === "number" ? item.status : 1;
|
||||
const cadRef = (item.identifierDetails ?? "").toString().trim();
|
||||
const hasPaperLb = !!(item.paperLbNo ?? "").toString().trim();
|
||||
const hasPaperCad = !!(item.paperCadNo ?? "").toString().trim();
|
||||
const hasArea =
|
||||
(typeof item.measuredArea === "number" && item.measuredArea > 0) ||
|
||||
(typeof item.legalArea === "number" && item.legalArea > 0);
|
||||
const hasIdentification = !!cadRef || hasPaperLb || hasPaperCad;
|
||||
|
||||
// Only keep items that pass the quality gate (active + identification/area)
|
||||
if (status === 1 && (hasIdentification || hasArea)) {
|
||||
validImmPks.add(pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find stale no-geom records: objectId < 0 means no-geom (objectId = -immPk)
|
||||
const existingNoGeom = await prisma.gisFeature.findMany({
|
||||
where: {
|
||||
layerId: "TERENURI_ACTIVE",
|
||||
siruta,
|
||||
geometrySource: "NO_GEOMETRY",
|
||||
},
|
||||
select: { id: true, objectId: true },
|
||||
});
|
||||
|
||||
const staleIds: string[] = [];
|
||||
for (const f of existingNoGeom) {
|
||||
const immPk = -f.objectId; // objectId = -immovablePk for no-geom
|
||||
if (!validImmPks.has(immPk)) {
|
||||
staleIds.push(f.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (staleIds.length > 0) {
|
||||
await prisma.gisFeature.deleteMany({
|
||||
where: { id: { in: staleIds } },
|
||||
});
|
||||
console.log(
|
||||
`[no-geom-sync] Cleanup: removed ${staleIds.length} stale/invalid no-geom records`,
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Get existing features from DB (after cleanup)
|
||||
const existingFeatures = await prisma.gisFeature.findMany({
|
||||
where: { layerId: "TERENURI_ACTIVE", siruta },
|
||||
select: { cadastralRef: true, objectId: true },
|
||||
@@ -433,7 +486,7 @@ export async function syncNoGeometryParcels(
|
||||
existingObjIds.add(f.objectId);
|
||||
}
|
||||
|
||||
// 3. Filter: not yet in DB + quality gate
|
||||
// 4. Filter: not yet in DB + quality gate
|
||||
// Quality: must be ACTIVE (status=1) AND have identification OR area.
|
||||
// Items that are inactive, or have no identification AND no area = noise.
|
||||
let filteredOut = 0;
|
||||
@@ -475,6 +528,7 @@ export async function syncNoGeometryParcels(
|
||||
return {
|
||||
imported: 0,
|
||||
skipped: filteredOut,
|
||||
cleaned: staleIds.length,
|
||||
errors: 0,
|
||||
status: "done",
|
||||
};
|
||||
@@ -594,10 +648,23 @@ export async function syncNoGeometryParcels(
|
||||
options?.onProgress?.(done, total, "Import parcele fără geometrie");
|
||||
}
|
||||
|
||||
return { imported, skipped: skipped + filteredOut, errors, status: "done" };
|
||||
return {
|
||||
imported,
|
||||
skipped: skipped + filteredOut,
|
||||
cleaned: staleIds.length,
|
||||
errors,
|
||||
status: "done",
|
||||
};
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : "Unknown error";
|
||||
return { imported: 0, skipped: 0, errors: 0, status: "error", error: msg };
|
||||
return {
|
||||
imported: 0,
|
||||
skipped: 0,
|
||||
cleaned: 0,
|
||||
errors: 0,
|
||||
status: "error",
|
||||
error: msg,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
const { PrismaClient } = require("@prisma/client");
|
||||
const p = new PrismaClient({
|
||||
datasourceUrl:
|
||||
"postgresql://architools_user:stictMyFon34!_gonY@10.10.10.166:5432/architools_db?schema=public",
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const features = await p.$queryRawUnsafe(`
|
||||
SELECT "layerId", siruta, "geometrySource", COUNT(*)::int as nr
|
||||
FROM "GisFeature"
|
||||
GROUP BY "layerId", siruta, "geometrySource"
|
||||
ORDER BY "layerId", siruta, "geometrySource"
|
||||
`);
|
||||
console.log("=== GisFeature ===");
|
||||
console.table(features);
|
||||
|
||||
const syncs = await p.$queryRawUnsafe(`
|
||||
SELECT "layerId", siruta, status, COUNT(*)::int as nr
|
||||
FROM "GisSyncRun"
|
||||
GROUP BY "layerId", siruta, status
|
||||
ORDER BY "layerId", siruta
|
||||
`);
|
||||
console.log("=== GisSyncRun ===");
|
||||
console.table(syncs);
|
||||
|
||||
const uats = await p.$queryRawUnsafe(`
|
||||
SELECT siruta, name, county, "workspacePk"
|
||||
FROM "GisUat"
|
||||
WHERE "workspacePk" IS NOT NULL AND "workspacePk" > 0
|
||||
LIMIT 20
|
||||
`);
|
||||
console.log("=== GisUat (with workspacePk) ===");
|
||||
console.table(uats);
|
||||
|
||||
await p.$disconnect();
|
||||
})();
|
||||
Reference in New Issue
Block a user