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
|
...(hasNoGeom
|
||||||
? [
|
? [
|
||||||
` 3. Import fără geometrie: ${fmt(noGeomImported)} noi importate` +
|
` 3. Import fără geometrie: ${fmt(noGeomImported)} noi importate` +
|
||||||
(noGeomCleaned > 0 ? `, ${fmt(noGeomCleaned)} vechi șterse` : "") +
|
(noGeomCleaned > 0
|
||||||
(noGeomSkipped > 0 ? `, ${fmt(noGeomSkipped)} filtrate/skip` : ""),
|
? `, ${fmt(noGeomCleaned)} vechi șterse`
|
||||||
|
: "") +
|
||||||
|
(noGeomSkipped > 0
|
||||||
|
? `, ${fmt(noGeomSkipped)} filtrate/skip`
|
||||||
|
: ""),
|
||||||
]
|
]
|
||||||
: [` 3. Import fără geometrie: dezactivat`]),
|
: [` 3. Import fără geometrie: dezactivat`]),
|
||||||
` 4. Îmbogățire (CF, prop.): da`,
|
` 4. Îmbogățire (CF, prop.): da`,
|
||||||
|
|||||||
@@ -390,6 +390,7 @@ export function ParcelSyncModule() {
|
|||||||
totalImmovables: number;
|
totalImmovables: number;
|
||||||
withGeometry: number;
|
withGeometry: number;
|
||||||
remoteGisCount: number;
|
remoteGisCount: number;
|
||||||
|
remoteCladiriCount: number;
|
||||||
noGeomCount: number;
|
noGeomCount: number;
|
||||||
matchedByRef: number;
|
matchedByRef: number;
|
||||||
matchedById: number;
|
matchedById: number;
|
||||||
@@ -720,6 +721,7 @@ export function ParcelSyncModule() {
|
|||||||
totalImmovables: 0,
|
totalImmovables: 0,
|
||||||
withGeometry: 0,
|
withGeometry: 0,
|
||||||
remoteGisCount: 0,
|
remoteGisCount: 0,
|
||||||
|
remoteCladiriCount: 0,
|
||||||
noGeomCount: 0,
|
noGeomCount: 0,
|
||||||
matchedByRef: 0,
|
matchedByRef: 0,
|
||||||
matchedById: 0,
|
matchedById: 0,
|
||||||
@@ -751,6 +753,7 @@ export function ParcelSyncModule() {
|
|||||||
totalImmovables: Number(data.totalImmovables ?? 0),
|
totalImmovables: Number(data.totalImmovables ?? 0),
|
||||||
withGeometry: Number(data.withGeometry ?? 0),
|
withGeometry: Number(data.withGeometry ?? 0),
|
||||||
remoteGisCount: Number(data.remoteGisCount ?? 0),
|
remoteGisCount: Number(data.remoteGisCount ?? 0),
|
||||||
|
remoteCladiriCount: Number(data.remoteCladiriCount ?? 0),
|
||||||
noGeomCount: Number(data.noGeomCount ?? 0),
|
noGeomCount: Number(data.noGeomCount ?? 0),
|
||||||
matchedByRef: Number(data.matchedByRef ?? 0),
|
matchedByRef: Number(data.matchedByRef ?? 0),
|
||||||
matchedById: Number(data.matchedById ?? 0),
|
matchedById: Number(data.matchedById ?? 0),
|
||||||
@@ -2430,7 +2433,13 @@ export function ParcelSyncModule() {
|
|||||||
session.connected &&
|
session.connected &&
|
||||||
(() => {
|
(() => {
|
||||||
const scanDone = noGeomScan !== null && noGeomScanSiruta === siruta;
|
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;
|
const scanning = noGeomScanning;
|
||||||
|
|
||||||
// Still scanning
|
// Still scanning
|
||||||
@@ -2517,9 +2526,7 @@ export function ParcelSyncModule() {
|
|||||||
</p>
|
</p>
|
||||||
<ol className="text-[11px] text-muted-foreground list-decimal ml-4 space-y-px">
|
<ol className="text-[11px] text-muted-foreground list-decimal ml-4 space-y-px">
|
||||||
<li>
|
<li>
|
||||||
{noGeomScan.localSyncFresh && noGeomScan.localDbWithGeom > 0
|
{"Sync GIS — "}
|
||||||
? "Sync terenuri + clădiri — "
|
|
||||||
: "Sync terenuri + clădiri — "}
|
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"font-medium",
|
"font-medium",
|
||||||
@@ -2532,7 +2539,10 @@ export function ParcelSyncModule() {
|
|||||||
{noGeomScan.localSyncFresh &&
|
{noGeomScan.localSyncFresh &&
|
||||||
noGeomScan.localDbWithGeom > 0
|
noGeomScan.localDbWithGeom > 0
|
||||||
? "skip (date proaspete în DB)"
|
? "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>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
{includeNoGeom && (
|
{includeNoGeom && (
|
||||||
@@ -2540,7 +2550,6 @@ export function ParcelSyncModule() {
|
|||||||
Import parcele fără geometrie —{" "}
|
Import parcele fără geometrie —{" "}
|
||||||
<span className="font-medium text-amber-600 dark:text-amber-400">
|
<span className="font-medium text-amber-600 dark:text-amber-400">
|
||||||
{(() => {
|
{(() => {
|
||||||
// Only useful items will be imported (quality filter)
|
|
||||||
const usefulNoGeom =
|
const usefulNoGeom =
|
||||||
noGeomScan.qualityBreakdown.useful;
|
noGeomScan.qualityBreakdown.useful;
|
||||||
const newNoGeom = Math.max(
|
const newNoGeom = Math.max(
|
||||||
@@ -2562,20 +2571,23 @@ export function ParcelSyncModule() {
|
|||||||
Îmbogățire CF, proprietari, adrese —{" "}
|
Îmbogățire CF, proprietari, adrese —{" "}
|
||||||
<span className="font-medium text-teal-600 dark:text-teal-400">
|
<span className="font-medium text-teal-600 dark:text-teal-400">
|
||||||
{(() => {
|
{(() => {
|
||||||
const usefulNoGeom = noGeomScan.qualityBreakdown.useful;
|
// What will be in DB after sync + optional no-geom import:
|
||||||
const totalToEnrich =
|
// If DB is empty: sync will add remoteGisCount geo features
|
||||||
noGeomScan.localDbTotal +
|
// If DB is fresh: keep localDbTotal
|
||||||
(includeNoGeom
|
const geoAfterSync =
|
||||||
|
noGeomScan.localSyncFresh &&
|
||||||
|
noGeomScan.localDbWithGeom > 0
|
||||||
|
? noGeomScan.localDbWithGeom
|
||||||
|
: noGeomScan.remoteGisCount;
|
||||||
|
const noGeomAfterImport = includeNoGeom
|
||||||
? Math.max(
|
? Math.max(
|
||||||
0,
|
noGeomScan.localDbNoGeom,
|
||||||
usefulNoGeom - noGeomScan.localDbNoGeom,
|
noGeomScan.qualityBreakdown.useful,
|
||||||
)
|
)
|
||||||
: 0);
|
: noGeomScan.localDbNoGeom;
|
||||||
// Use enrichedComplete (not enriched) — stale
|
const totalAfter = geoAfterSync + noGeomAfterImport;
|
||||||
// enrichment (missing PROPRIETARI_VECHI etc.)
|
|
||||||
// will be re-processed
|
|
||||||
const remaining =
|
const remaining =
|
||||||
totalToEnrich - noGeomScan.localDbEnrichedComplete;
|
totalAfter - noGeomScan.localDbEnrichedComplete;
|
||||||
return remaining > 0
|
return remaining > 0
|
||||||
? `~${remaining.toLocaleString("ro-RO")} de procesat (~${Math.ceil((remaining * 0.25) / 60)} min)`
|
? `~${remaining.toLocaleString("ro-RO")} de procesat (~${Math.ceil((remaining * 0.25) / 60)} min)`
|
||||||
: "deja îmbogățite";
|
: "deja îmbogățite";
|
||||||
@@ -2604,38 +2616,37 @@ export function ParcelSyncModule() {
|
|||||||
<AlertTriangle className="h-4 w-4 text-amber-500 shrink-0" />
|
<AlertTriangle className="h-4 w-4 text-amber-500 shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm">
|
<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:{" "}
|
Layer GIS:{" "}
|
||||||
<span className="font-medium">
|
<span className="text-emerald-600 dark:text-emerald-400 font-semibold">
|
||||||
{noGeomScan.remoteGisCount.toLocaleString("ro-RO")}
|
{noGeomScan.remoteGisCount.toLocaleString("ro-RO")}
|
||||||
</span>
|
</span>{" "}
|
||||||
{" features (se descarcă toate)"}
|
terenuri
|
||||||
{noGeomScan.remoteGisCount !== noGeomScan.withGeometry && (
|
{noGeomScan.remoteCladiriCount > 0 && (
|
||||||
<>
|
<>
|
||||||
{" · "}
|
{" + "}
|
||||||
{noGeomScan.withGeometry.toLocaleString("ro-RO")} potrivite
|
<span className="font-semibold">
|
||||||
cu lista de imobile
|
{noGeomScan.remoteCladiriCount.toLocaleString(
|
||||||
{noGeomScan.matchedByRef > 0 && noGeomScan.matchedById > 0 && (
|
"ro-RO",
|
||||||
<span className="text-muted-foreground/50">
|
|
||||||
{" "}({noGeomScan.matchedByRef} cadRef + {noGeomScan.matchedById} ID)
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
|
</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>
|
||||||
<p className="text-[11px] text-muted-foreground mt-0.5">
|
<p className="text-[11px] text-muted-foreground mt-0.5">
|
||||||
Cele fără geometrie există în baza de date eTerra dar
|
Cele fără geometrie există în baza de date eTerra dar
|
||||||
|
|||||||
@@ -113,10 +113,12 @@ export type NoGeomQuality = {
|
|||||||
|
|
||||||
export type NoGeomScanResult = {
|
export type NoGeomScanResult = {
|
||||||
totalImmovables: number;
|
totalImmovables: number;
|
||||||
/** Immovables that matched a remote GIS feature (have geometry) */
|
/** Immovables that matched a remote GIS feature (cross-ref, may vary) */
|
||||||
withGeometry: number;
|
withGeometry: number;
|
||||||
/** Total features in the remote ArcGIS TERENURI_ACTIVE layer */
|
/** Total features in the remote ArcGIS TERENURI_ACTIVE layer (stable) */
|
||||||
remoteGisCount: number;
|
remoteGisCount: number;
|
||||||
|
/** Total features in the remote ArcGIS CLADIRI_ACTIVE layer (stable) */
|
||||||
|
remoteCladiriCount: number;
|
||||||
noGeomCount: number;
|
noGeomCount: number;
|
||||||
/** Match quality: how many matched by cadastral ref vs immovable ID */
|
/** Match quality: how many matched by cadastral ref vs immovable ID */
|
||||||
matchedByRef: number;
|
matchedByRef: number;
|
||||||
@@ -185,6 +187,7 @@ export async function scanNoGeometryParcels(
|
|||||||
totalImmovables: 0,
|
totalImmovables: 0,
|
||||||
withGeometry: 0,
|
withGeometry: 0,
|
||||||
remoteGisCount: 0,
|
remoteGisCount: 0,
|
||||||
|
remoteCladiriCount: 0,
|
||||||
noGeomCount: 0,
|
noGeomCount: 0,
|
||||||
matchedByRef: 0,
|
matchedByRef: 0,
|
||||||
matchedById: 0,
|
matchedById: 0,
|
||||||
@@ -233,6 +236,25 @@ export async function scanNoGeometryParcels(
|
|||||||
pageSize: 2000,
|
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 remoteCadRefs = new Set<string>();
|
||||||
const remoteImmIds = new Set<string>();
|
const remoteImmIds = new Set<string>();
|
||||||
for (const f of remoteFeatures) {
|
for (const f of remoteFeatures) {
|
||||||
@@ -392,6 +414,7 @@ export async function scanNoGeometryParcels(
|
|||||||
totalImmovables: allImmovables.length,
|
totalImmovables: allImmovables.length,
|
||||||
withGeometry: matchedCount,
|
withGeometry: matchedCount,
|
||||||
remoteGisCount: remoteFeatures.length,
|
remoteGisCount: remoteFeatures.length,
|
||||||
|
remoteCladiriCount,
|
||||||
noGeomCount: noGeomItems.length,
|
noGeomCount: noGeomItems.length,
|
||||||
matchedByRef,
|
matchedByRef,
|
||||||
matchedById,
|
matchedById,
|
||||||
|
|||||||
Reference in New Issue
Block a user