feat: filter no-geom by IE status (hasLandbook), add checkIfIsIE + CF PDF APIs

QUALITY GATE TIGHTENED:
No-geometry import now requires hasLandbook=1 (imobil electronic).
This filters out immovables without carte funciara — they have no
CF data, no owners, no parcel details to extract. For Cosbuc this
reduces useful no-geom from ~1916 to ~468 (only IEs with real data).

Three-tier quality gate:
1. Active (status=1)
2. Has landbook (hasLandbook=1) — is electronic immovable  [NEW]
3. Has identification (cadRef/paperLbNo/paperCadNo) OR area

CLEANUP also updated: DB cleanup now removes stale no-geom records
that don't pass the tightened gate (existing non-IE records will be
cleaned on next import run).

NEW API METHODS (eterra-client):
- checkIfIsIE(adminUnitId, paperCadNo, topNo, paperCfNo) → boolean
  Calls /api/immovable/checkIfIsIE — verifies IE status per-parcel
  Available for future per-item verification if needed
- getCfExtractUrl(immovablePk, workspaceId) → string
  Returns URL for /api/cf/landbook/copycf/get/{pk}/{ws}/0/true
  Downloads the CF extract as PDF blob (future enrichment)

UI updated: 'Filtrate' label now says 'fara CF/inactive/fara date'
to reflect the new hasLandbook filter.
This commit is contained in:
AI Assistant
2026-03-08 00:57:16 +02:00
parent f09eaaad7c
commit aee28b6768
3 changed files with 65 additions and 10 deletions
@@ -403,10 +403,10 @@ export async function scanNoGeometryParcels(
if (hasLb) qWithLandbook++;
if (hasArea) qWithArea++;
if (isActive) qWithActiveStatus++;
// "Useful" = ACTIVE + has any form of identification OR area
// Matches the import quality gate
// "Useful" = ACTIVE + HAS_LANDBOOK (imobil electronic) + has identification OR area
// Matches the import quality gate — only IE items are worth importing
const hasIdentification = hasCad || hasPaperLb || hasPaperCad;
if (isActive && (hasIdentification || hasArea)) qUseful++;
if (isActive && hasLb && (hasIdentification || hasArea)) qUseful++;
else qEmpty++;
}
@@ -483,13 +483,14 @@ export async function syncNoGeometryParcels(
const cadRef = (item.identifierDetails ?? "").toString().trim();
const hasPaperLb = !!(item.paperLbNo ?? "").toString().trim();
const hasPaperCad = !!(item.paperCadNo ?? "").toString().trim();
const hasLandbook = typeof item.hasLandbook === "number" ? item.hasLandbook : 0;
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)) {
// Only keep items that pass the quality gate (active + hasLandbook + identification/area)
if (status === 1 && hasLandbook === 1 && (hasIdentification || hasArea)) {
validImmPks.add(pk);
}
}
@@ -536,8 +537,8 @@ export async function syncNoGeometryParcels(
}
// 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.
// Quality: must be ACTIVE (status=1) AND hasLandbook=1 (IE) AND have identification OR area.
// Items without landbook are not electronic immovables — no CF data to extract.
let filteredOut = 0;
const candidates = allImmovables.filter((item) => {
const cadRef = normalizeCadRef(item.identifierDetails ?? "");
@@ -554,7 +555,14 @@ export async function syncNoGeometryParcels(
return false;
}
// Quality gate 2: must have identification OR area
// Quality gate 2: must be an electronic immovable (hasLandbook=1)
const hasLandbook = typeof item.hasLandbook === "number" ? item.hasLandbook : 0;
if (hasLandbook !== 1) {
filteredOut++;
return false;
}
// Quality gate 3: must have identification OR area
const hasCadRef = !!cadRef;
const hasPaperLb = !!(item.paperLbNo ?? "").toString().trim();
const hasPaperCad = !!(item.paperCadNo ?? "").toString().trim();