diff --git a/src/app/api/eterra/no-geom-scan/route.ts b/src/app/api/eterra/no-geom-scan/route.ts
index 36b77e3..662edce 100644
--- a/src/app/api/eterra/no-geom-scan/route.ts
+++ b/src/app/api/eterra/no-geom-scan/route.ts
@@ -2,11 +2,11 @@
* POST /api/eterra/no-geom-scan
*
* Scans eTerra immovable list for a UAT and counts how many parcels
- * exist in the eTerra database but have no geometry in the GIS layer
- * (i.e., they are NOT in the local TERENURI_ACTIVE DB).
+ * exist in the eTerra database but have no geometry in the remote
+ * ArcGIS GIS layer (TERENURI_ACTIVE). Cross-references remotely.
*
* Body: { siruta: string }
- * Returns: { totalImmovables, totalInDb, noGeomCount, samples }
+ * Returns: { totalImmovables, withGeometry, noGeomCount, samples }
*
* Requires active eTerra session.
*/
diff --git a/src/modules/parcel-sync/components/parcel-sync-module.tsx b/src/modules/parcel-sync/components/parcel-sync-module.tsx
index d57bcb3..71293da 100644
--- a/src/modules/parcel-sync/components/parcel-sync-module.tsx
+++ b/src/modules/parcel-sync/components/parcel-sync-module.tsx
@@ -388,7 +388,7 @@ export function ParcelSyncModule() {
const [noGeomScanning, setNoGeomScanning] = useState(false);
const [noGeomScan, setNoGeomScan] = useState<{
totalImmovables: number;
- totalInDb: number;
+ withGeometry: number;
noGeomCount: number;
} | null>(null);
const [noGeomScanSiruta, setNoGeomScanSiruta] = useState(""); // siruta for which scan was done
@@ -697,23 +697,23 @@ export function ParcelSyncModule() {
});
const data = (await res.json()) as {
totalImmovables?: number;
- totalInDb?: number;
+ withGeometry?: number;
noGeomCount?: number;
error?: string;
};
if (data.error) {
console.warn("[no-geom-scan]", data.error);
- setNoGeomScan({ totalImmovables: 0, totalInDb: 0, noGeomCount: 0 });
+ setNoGeomScan({ totalImmovables: 0, withGeometry: 0, noGeomCount: 0 });
} else {
setNoGeomScan({
totalImmovables: data.totalImmovables ?? 0,
- totalInDb: data.totalInDb ?? 0,
+ withGeometry: data.withGeometry ?? 0,
noGeomCount: data.noGeomCount ?? 0,
});
}
} catch {
// Show zero result on network error
- setNoGeomScan({ totalImmovables: 0, totalInDb: 0, noGeomCount: 0 });
+ setNoGeomScan({ totalImmovables: 0, withGeometry: 0, noGeomCount: 0 });
}
setNoGeomScanning(false);
},
@@ -2407,7 +2407,7 @@ export function ParcelSyncModule() {
{" "}
imobile în eTerra:{" "}
- {noGeomScan.totalInDb.toLocaleString("ro-RO")}
+ {noGeomScan.withGeometry.toLocaleString("ro-RO")}
{" "}
cu geometrie,{" "}
diff --git a/src/modules/parcel-sync/services/eterra-client.ts b/src/modules/parcel-sync/services/eterra-client.ts
index 31d0a2e..1d69b61 100644
--- a/src/modules/parcel-sync/services/eterra-client.ts
+++ b/src/modules/parcel-sync/services/eterra-client.ts
@@ -309,6 +309,8 @@ export class EterraClient {
total?: number;
onProgress?: ProgressCallback;
delayMs?: number;
+ outFields?: string;
+ returnGeometry?: boolean;
},
) {
const where = await this.buildWhere(layer, siruta);
diff --git a/src/modules/parcel-sync/services/no-geom-sync.ts b/src/modules/parcel-sync/services/no-geom-sync.ts
index d9a4c55..c90aa12 100644
--- a/src/modules/parcel-sync/services/no-geom-sync.ts
+++ b/src/modules/parcel-sync/services/no-geom-sync.ts
@@ -93,7 +93,8 @@ const normalizeCadRef = (value: unknown) =>
export type NoGeomScanResult = {
totalImmovables: number;
- totalInDb: number;
+ /** Features present in the remote ArcGIS TERENURI_ACTIVE layer (have geometry) */
+ withGeometry: number;
noGeomCount: number;
/** Sample of immovable identifiers without geometry */
samples: Array<{
@@ -116,7 +117,11 @@ export type NoGeomSyncResult = {
/**
* Scan: count how many eTerra immovables for this UAT have no geometry
- * in the local DB.
+ * in the remote GIS layer (TERENURI_ACTIVE).
+ *
+ * Cross-references the eTerra immovable list against the REMOTE ArcGIS
+ * layer (lightweight fetch, no geometry download). This works correctly
+ * regardless of whether the user has synced to local DB yet.
*
* This does NOT write anything — it's a read-only operation.
*/
@@ -133,14 +138,14 @@ export async function scanNoGeometryParcels(
if (!wsPk) {
return {
totalImmovables: 0,
- totalInDb: 0,
+ withGeometry: 0,
noGeomCount: 0,
samples: [],
error: `Nu s-a putut determina workspace-ul (județul) pentru SIRUTA ${siruta}`,
};
}
- // 1. Fetch all immovables from eTerra
+ // 1. Fetch all immovables from eTerra immovable list API
const allImmovables = await fetchAllImmovables(
client,
siruta,
@@ -148,20 +153,33 @@ export async function scanNoGeometryParcels(
options?.onProgress,
);
- // 2. Get all existing cadastralRefs in DB for TERENURI_ACTIVE
- const existingFeatures = await prisma.gisFeature.findMany({
- where: { layerId: "TERENURI_ACTIVE", siruta },
- select: { cadastralRef: true, objectId: true },
+ // 2. Fetch remote GIS cadastral refs (lightweight — no geometry)
+ // This is the source of truth for "has geometry" regardless of local DB state.
+ // ~4 pages for 8k features with outFields only = very fast.
+ const terenuriLayer = {
+ id: "TERENURI_ACTIVE",
+ name: "TERENURI_ACTIVE",
+ endpoint: "aut" as const,
+ whereTemplate: "{{adminField}}={{siruta}} AND IS_ACTIVE=1",
+ };
+ const remoteFeatures = await client.fetchAllLayer(terenuriLayer, siruta, {
+ returnGeometry: false,
+ outFields: "OBJECTID,NATIONAL_CADASTRAL_REFERENCE,IMMOVABLE_ID",
+ pageSize: 2000,
});
- const existingCadRefs = new Set();
- const existingObjIds = new Set();
- for (const f of existingFeatures) {
- if (f.cadastralRef) existingCadRefs.add(normalizeCadRef(f.cadastralRef));
- existingObjIds.add(f.objectId);
+ const remoteCadRefs = new Set();
+ const remoteImmIds = new Set();
+ for (const f of remoteFeatures) {
+ const cadRef = normalizeCadRef(
+ f.attributes?.NATIONAL_CADASTRAL_REFERENCE ?? "",
+ );
+ if (cadRef) remoteCadRefs.add(cadRef);
+ const immId = normalizeId(f.attributes?.IMMOVABLE_ID);
+ if (immId) remoteImmIds.add(immId);
}
- // 3. Find immovables not in DB
+ // 3. Cross-reference: immovables NOT in remote GIS = no geometry
const noGeomItems: Array<{
immovablePk: number;
identifierDetails: string;
@@ -172,12 +190,13 @@ export async function scanNoGeometryParcels(
for (const item of allImmovables) {
const cadRef = normalizeCadRef(item.identifierDetails ?? "");
const immPk = Number(item.immovablePk ?? 0);
+ const immId = normalizeId(item.immovablePk);
- // Already in DB by cadastral ref?
- if (cadRef && existingCadRefs.has(cadRef)) continue;
+ // Present in remote GIS layer by cadastral ref? → has geometry
+ if (cadRef && remoteCadRefs.has(cadRef)) continue;
- // Already in DB by negative objectId?
- if (immPk > 0 && existingObjIds.has(-immPk)) continue;
+ // Present in remote GIS layer by IMMOVABLE_ID? → has geometry
+ if (immId && remoteImmIds.has(immId)) continue;
noGeomItems.push({
immovablePk: immPk,
@@ -189,7 +208,7 @@ export async function scanNoGeometryParcels(
return {
totalImmovables: allImmovables.length,
- totalInDb: existingFeatures.length,
+ withGeometry: remoteFeatures.length,
noGeomCount: noGeomItems.length,
samples: noGeomItems.slice(0, 20),
};