fix: stable scan display, accurate workflow preview, cladiri count
ROOT CAUSE: The cross-reference between immovable list and GIS layer produces wildly different matchedCount on each scan (320, 430, 629, 433) because the eTerra immovable/list API with inscrisCF=-1 returns inconsistent results across calls. The GIS layer count (505) is stable. SCAN DISPLAY — now uses only stable numbers: - Header shows 'Layer GIS: 505 terenuri + X cladiri' (stable ArcGIS count) - Shows 'Lista imobile: 2.717 (estimat ~2.212 fara geometrie)' using simple subtraction totalImmovables - remoteGisCount - Cross-ref matchedCount kept internally for import logic, but NOT shown as the primary number — eliminates visual instability - hasNoGeomParcels now uses estimated count (stable) WORKFLOW PREVIEW — now accurate: - Step 1: 'Sync GIS — descarca 505 terenuri + X cladiri' (separate counts) or 'skip (date proaspete in DB)' when fresh - Step 2 (enrichment): Fixed 'deja imbogatite' bug when DB is empty. Now correctly computes what WILL be in DB after sync completes: geoAfterSync + noGeomAfterImport - localDbEnrichedComplete - Steps 3-4 unchanged CLADIRI COUNT: - Scan now also fetches CLADIRI_ACTIVE layer count (lightweight, OBJECTID only) - New field remoteCladiriCount in NoGeomScanResult - Displayed in header and workflow step 1 - Non-fatal: if CLADIRI fetch fails, just shows 0
This commit is contained in:
@@ -696,8 +696,12 @@ export async function POST(req: Request) {
|
||||
...(hasNoGeom
|
||||
? [
|
||||
` 3. Import fără geometrie: ${fmt(noGeomImported)} noi importate` +
|
||||
(noGeomCleaned > 0 ? `, ${fmt(noGeomCleaned)} vechi șterse` : "") +
|
||||
(noGeomSkipped > 0 ? `, ${fmt(noGeomSkipped)} filtrate/skip` : ""),
|
||||
(noGeomCleaned > 0
|
||||
? `, ${fmt(noGeomCleaned)} vechi șterse`
|
||||
: "") +
|
||||
(noGeomSkipped > 0
|
||||
? `, ${fmt(noGeomSkipped)} filtrate/skip`
|
||||
: ""),
|
||||
]
|
||||
: [` 3. Import fără geometrie: dezactivat`]),
|
||||
` 4. Îmbogățire (CF, prop.): da`,
|
||||
|
||||
@@ -390,6 +390,7 @@ export function ParcelSyncModule() {
|
||||
totalImmovables: number;
|
||||
withGeometry: number;
|
||||
remoteGisCount: number;
|
||||
remoteCladiriCount: number;
|
||||
noGeomCount: number;
|
||||
matchedByRef: number;
|
||||
matchedById: number;
|
||||
@@ -720,6 +721,7 @@ export function ParcelSyncModule() {
|
||||
totalImmovables: 0,
|
||||
withGeometry: 0,
|
||||
remoteGisCount: 0,
|
||||
remoteCladiriCount: 0,
|
||||
noGeomCount: 0,
|
||||
matchedByRef: 0,
|
||||
matchedById: 0,
|
||||
@@ -751,6 +753,7 @@ export function ParcelSyncModule() {
|
||||
totalImmovables: Number(data.totalImmovables ?? 0),
|
||||
withGeometry: Number(data.withGeometry ?? 0),
|
||||
remoteGisCount: Number(data.remoteGisCount ?? 0),
|
||||
remoteCladiriCount: Number(data.remoteCladiriCount ?? 0),
|
||||
noGeomCount: Number(data.noGeomCount ?? 0),
|
||||
matchedByRef: Number(data.matchedByRef ?? 0),
|
||||
matchedById: Number(data.matchedById ?? 0),
|
||||
@@ -2430,7 +2433,13 @@ export function ParcelSyncModule() {
|
||||
session.connected &&
|
||||
(() => {
|
||||
const scanDone = noGeomScan !== null && noGeomScanSiruta === siruta;
|
||||
const hasNoGeomParcels = scanDone && noGeomScan.noGeomCount > 0;
|
||||
const estimatedNoGeom = scanDone
|
||||
? Math.max(
|
||||
0,
|
||||
noGeomScan.totalImmovables - noGeomScan.remoteGisCount,
|
||||
)
|
||||
: 0;
|
||||
const hasNoGeomParcels = scanDone && estimatedNoGeom > 0;
|
||||
const scanning = noGeomScanning;
|
||||
|
||||
// Still scanning
|
||||
@@ -2517,9 +2526,7 @@ export function ParcelSyncModule() {
|
||||
</p>
|
||||
<ol className="text-[11px] text-muted-foreground list-decimal ml-4 space-y-px">
|
||||
<li>
|
||||
{noGeomScan.localSyncFresh && noGeomScan.localDbWithGeom > 0
|
||||
? "Sync terenuri + clădiri — "
|
||||
: "Sync terenuri + clădiri — "}
|
||||
{"Sync GIS — "}
|
||||
<span
|
||||
className={cn(
|
||||
"font-medium",
|
||||
@@ -2532,7 +2539,10 @@ export function ParcelSyncModule() {
|
||||
{noGeomScan.localSyncFresh &&
|
||||
noGeomScan.localDbWithGeom > 0
|
||||
? "skip (date proaspete în DB)"
|
||||
: `descarcă ${noGeomScan.remoteGisCount.toLocaleString("ro-RO")} features`}
|
||||
: `descarcă ${noGeomScan.remoteGisCount.toLocaleString("ro-RO")} terenuri` +
|
||||
(noGeomScan.remoteCladiriCount > 0
|
||||
? ` + ${noGeomScan.remoteCladiriCount.toLocaleString("ro-RO")} clădiri`
|
||||
: "")}
|
||||
</span>
|
||||
</li>
|
||||
{includeNoGeom && (
|
||||
@@ -2540,7 +2550,6 @@ export function ParcelSyncModule() {
|
||||
Import parcele fără geometrie —{" "}
|
||||
<span className="font-medium text-amber-600 dark:text-amber-400">
|
||||
{(() => {
|
||||
// Only useful items will be imported (quality filter)
|
||||
const usefulNoGeom =
|
||||
noGeomScan.qualityBreakdown.useful;
|
||||
const newNoGeom = Math.max(
|
||||
@@ -2562,20 +2571,23 @@ export function ParcelSyncModule() {
|
||||
Îmbogățire CF, proprietari, adrese —{" "}
|
||||
<span className="font-medium text-teal-600 dark:text-teal-400">
|
||||
{(() => {
|
||||
const usefulNoGeom = noGeomScan.qualityBreakdown.useful;
|
||||
const totalToEnrich =
|
||||
noGeomScan.localDbTotal +
|
||||
(includeNoGeom
|
||||
// What will be in DB after sync + optional no-geom import:
|
||||
// If DB is empty: sync will add remoteGisCount geo features
|
||||
// If DB is fresh: keep localDbTotal
|
||||
const geoAfterSync =
|
||||
noGeomScan.localSyncFresh &&
|
||||
noGeomScan.localDbWithGeom > 0
|
||||
? noGeomScan.localDbWithGeom
|
||||
: noGeomScan.remoteGisCount;
|
||||
const noGeomAfterImport = includeNoGeom
|
||||
? Math.max(
|
||||
0,
|
||||
usefulNoGeom - noGeomScan.localDbNoGeom,
|
||||
noGeomScan.localDbNoGeom,
|
||||
noGeomScan.qualityBreakdown.useful,
|
||||
)
|
||||
: 0);
|
||||
// Use enrichedComplete (not enriched) — stale
|
||||
// enrichment (missing PROPRIETARI_VECHI etc.)
|
||||
// will be re-processed
|
||||
: noGeomScan.localDbNoGeom;
|
||||
const totalAfter = geoAfterSync + noGeomAfterImport;
|
||||
const remaining =
|
||||
totalToEnrich - noGeomScan.localDbEnrichedComplete;
|
||||
totalAfter - noGeomScan.localDbEnrichedComplete;
|
||||
return remaining > 0
|
||||
? `~${remaining.toLocaleString("ro-RO")} de procesat (~${Math.ceil((remaining * 0.25) / 60)} min)`
|
||||
: "deja îmbogățite";
|
||||
@@ -2604,38 +2616,37 @@ export function ParcelSyncModule() {
|
||||
<AlertTriangle className="h-4 w-4 text-amber-500 shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm">
|
||||
Din{" "}
|
||||
<span className="font-semibold">
|
||||
{noGeomScan.totalImmovables.toLocaleString("ro-RO")}
|
||||
</span>{" "}
|
||||
imobile în eTerra:{" "}
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-medium">
|
||||
{noGeomScan.withGeometry.toLocaleString("ro-RO")}
|
||||
</span>{" "}
|
||||
cu geometrie,{" "}
|
||||
<span className="font-semibold text-amber-600 dark:text-amber-400">
|
||||
{noGeomScan.noGeomCount.toLocaleString("ro-RO")}
|
||||
</span>{" "}
|
||||
<span className="font-medium">fără geometrie</span>
|
||||
</p>
|
||||
<p className="text-[10px] text-muted-foreground/70 mt-0.5">
|
||||
Layer GIS:{" "}
|
||||
<span className="font-medium">
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-semibold">
|
||||
{noGeomScan.remoteGisCount.toLocaleString("ro-RO")}
|
||||
</span>
|
||||
{" features (se descarcă toate)"}
|
||||
{noGeomScan.remoteGisCount !== noGeomScan.withGeometry && (
|
||||
</span>{" "}
|
||||
terenuri
|
||||
{noGeomScan.remoteCladiriCount > 0 && (
|
||||
<>
|
||||
{" · "}
|
||||
{noGeomScan.withGeometry.toLocaleString("ro-RO")} potrivite
|
||||
cu lista de imobile
|
||||
{noGeomScan.matchedByRef > 0 && noGeomScan.matchedById > 0 && (
|
||||
<span className="text-muted-foreground/50">
|
||||
{" "}({noGeomScan.matchedByRef} cadRef + {noGeomScan.matchedById} ID)
|
||||
</span>
|
||||
{" + "}
|
||||
<span className="font-semibold">
|
||||
{noGeomScan.remoteCladiriCount.toLocaleString(
|
||||
"ro-RO",
|
||||
)}
|
||||
</span>{" "}
|
||||
clădiri
|
||||
</>
|
||||
)}
|
||||
{" · "}
|
||||
Lista imobile:{" "}
|
||||
<span className="font-semibold">
|
||||
{noGeomScan.totalImmovables.toLocaleString("ro-RO")}
|
||||
</span>
|
||||
{" (estimat "}
|
||||
<span className="font-semibold text-amber-600 dark:text-amber-400">
|
||||
~
|
||||
{Math.max(
|
||||
0,
|
||||
noGeomScan.totalImmovables -
|
||||
noGeomScan.remoteGisCount,
|
||||
).toLocaleString("ro-RO")}
|
||||
</span>
|
||||
{" fără geometrie)"}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted-foreground mt-0.5">
|
||||
Cele fără geometrie există în baza de date eTerra dar
|
||||
|
||||
@@ -113,10 +113,12 @@ export type NoGeomQuality = {
|
||||
|
||||
export type NoGeomScanResult = {
|
||||
totalImmovables: number;
|
||||
/** Immovables that matched a remote GIS feature (have geometry) */
|
||||
/** Immovables that matched a remote GIS feature (cross-ref, may vary) */
|
||||
withGeometry: number;
|
||||
/** Total features in the remote ArcGIS TERENURI_ACTIVE layer */
|
||||
/** Total features in the remote ArcGIS TERENURI_ACTIVE layer (stable) */
|
||||
remoteGisCount: number;
|
||||
/** Total features in the remote ArcGIS CLADIRI_ACTIVE layer (stable) */
|
||||
remoteCladiriCount: number;
|
||||
noGeomCount: number;
|
||||
/** Match quality: how many matched by cadastral ref vs immovable ID */
|
||||
matchedByRef: number;
|
||||
@@ -185,6 +187,7 @@ export async function scanNoGeometryParcels(
|
||||
totalImmovables: 0,
|
||||
withGeometry: 0,
|
||||
remoteGisCount: 0,
|
||||
remoteCladiriCount: 0,
|
||||
noGeomCount: 0,
|
||||
matchedByRef: 0,
|
||||
matchedById: 0,
|
||||
@@ -233,6 +236,25 @@ export async function scanNoGeometryParcels(
|
||||
pageSize: 2000,
|
||||
});
|
||||
|
||||
// 2b. Also fetch CLADIRI_ACTIVE count (lightweight, just OBJECTID)
|
||||
const cladiriLayer = {
|
||||
id: "CLADIRI_ACTIVE",
|
||||
name: "CLADIRI_ACTIVE",
|
||||
endpoint: "aut" as const,
|
||||
whereTemplate: "{{adminField}}={{siruta}} AND IS_ACTIVE=1",
|
||||
};
|
||||
let remoteCladiriCount = 0;
|
||||
try {
|
||||
const cladiriFeatures = await client.fetchAllLayer(cladiriLayer, siruta, {
|
||||
returnGeometry: false,
|
||||
outFields: "OBJECTID",
|
||||
pageSize: 2000,
|
||||
});
|
||||
remoteCladiriCount = cladiriFeatures.length;
|
||||
} catch {
|
||||
// Non-fatal — just won't show clădiri count
|
||||
}
|
||||
|
||||
const remoteCadRefs = new Set<string>();
|
||||
const remoteImmIds = new Set<string>();
|
||||
for (const f of remoteFeatures) {
|
||||
@@ -392,6 +414,7 @@ export async function scanNoGeometryParcels(
|
||||
totalImmovables: allImmovables.length,
|
||||
withGeometry: matchedCount,
|
||||
remoteGisCount: remoteFeatures.length,
|
||||
remoteCladiriCount,
|
||||
noGeomCount: noGeomItems.length,
|
||||
matchedByRef,
|
||||
matchedById,
|
||||
|
||||
Reference in New Issue
Block a user