fix(parcel-sync): scan uses remote GIS layer instead of empty local DB
- scanNoGeometryParcels now fetches TERENURI_ACTIVE features from remote ArcGIS (lightweight, no geometry) to cross-reference with eTerra immovable list - Cross-references by both NATIONAL_CADASTRAL_REFERENCE and IMMOVABLE_ID - Works correctly regardless of whether user has synced to local DB - Renamed totalInDb -> withGeometry in NoGeomScanResult, UI, and API - Extended fetchAllLayer() to forward outFields/returnGeometry options
This commit is contained in:
@@ -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<string>();
|
||||
const existingObjIds = new Set<number>();
|
||||
for (const f of existingFeatures) {
|
||||
if (f.cadastralRef) existingCadRefs.add(normalizeCadRef(f.cadastralRef));
|
||||
existingObjIds.add(f.objectId);
|
||||
const remoteCadRefs = new Set<string>();
|
||||
const remoteImmIds = new Set<string>();
|
||||
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),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user