feat(parcel-sync): extended enrichment fields from existing API data
New fields extracted from already-fetched documentation/GIS data (zero extra API calls, no performance impact): - TIP_INSCRIERE: "Intabulare, drept de PROPRIETATE, dobandit prin..." - ACT_PROPRIETATE: "hotarare judecatoreasca nr... / contract vanzare..." - COTA_PROPRIETATE: "1/1" or fractional - DATA_CERERE: date of registration application - NR_CORPURI: number of building bodies on parcel - CORPURI_DETALII: "C1:352mp, C2:248mp, C3:104mp" - IS_CONDOMINIUM: condominium flag - DATA_CREARE: parcel creation date in eTerra Also fixed HAS_BUILDING: now also uses NR_CORPURI count as fallback (was 0 for parcels where buildingMap cross-ref missed matches). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,6 +135,23 @@ export type FeatureEnrichment = {
|
||||
CATEGORIE_FOLOSINTA: string;
|
||||
HAS_BUILDING: number;
|
||||
BUILD_LEGAL: number;
|
||||
// Extended fields (extracted from existing API calls, zero overhead)
|
||||
/** "Intabulare, drept de PROPRIETATE, dobandit prin..." */
|
||||
TIP_INSCRIERE?: string;
|
||||
/** "hotarare judecatoreasca nr..." / "contract vanzare cumparare nr..." */
|
||||
ACT_PROPRIETATE?: string;
|
||||
/** "1/1" or fractional */
|
||||
COTA_PROPRIETATE?: string;
|
||||
/** Date of registration application (ISO) */
|
||||
DATA_CERERE?: string;
|
||||
/** Number of building bodies on this parcel */
|
||||
NR_CORPURI?: number;
|
||||
/** Comma-separated list: "C1:352mp, C2:248mp, C3:104mp" */
|
||||
CORPURI_DETALII?: string;
|
||||
/** 1 if condominium, 0 otherwise */
|
||||
IS_CONDOMINIUM?: number;
|
||||
/** Date parcel was created in eTerra (ISO) */
|
||||
DATA_CREARE?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -369,6 +386,8 @@ export async function enrichFeatures(
|
||||
// ── Fetch documentation/owner data ──
|
||||
push({ phase: "Descărcare documentații CF" });
|
||||
const docByImmovable = new Map<string, any>();
|
||||
// Store raw registrations per landbookIE for extended enrichment fields
|
||||
const regsByLandbook = new Map<string, any[]>();
|
||||
const immovableIds = Array.from(immovableListById.keys());
|
||||
const docBatchSize = 50;
|
||||
for (let i = 0; i < immovableIds.length; i += docBatchSize) {
|
||||
@@ -385,6 +404,13 @@ export async function enrichFeatures(
|
||||
const nodeMap = new Map<number, any>();
|
||||
for (const reg of regs) {
|
||||
if (reg?.nodeId != null) nodeMap.set(Number(reg.nodeId), reg);
|
||||
// Store all registrations by landbookIE for extended enrichment
|
||||
if (reg?.landbookIE) {
|
||||
const lbKey = String(reg.landbookIE);
|
||||
const existing = regsByLandbook.get(lbKey) ?? [];
|
||||
existing.push(reg);
|
||||
regsByLandbook.set(lbKey, existing);
|
||||
}
|
||||
}
|
||||
// Check if an entry or any ancestor "I" inscription is radiated
|
||||
const isRadiated = (entry: any, depth = 0): boolean => {
|
||||
@@ -641,6 +667,59 @@ export async function enrichFeatures(
|
||||
: null);
|
||||
}
|
||||
|
||||
// Extended fields — extracted from existing data, zero extra API calls
|
||||
let tipInscriere = "";
|
||||
let actProprietate = "";
|
||||
let cotaProprietate = "";
|
||||
let dataCerere = "";
|
||||
// Extract registration details from already-fetched documentation
|
||||
const lbKey = landbookIE || cadRefRaw;
|
||||
const regsForParcel = regsByLandbook.get(String(lbKey)) ?? [];
|
||||
for (const reg of regsForParcel) {
|
||||
const nt = String(reg?.nodeType ?? "").toUpperCase();
|
||||
const nn = String(reg?.nodeName ?? "").trim();
|
||||
if (nt === "I" && nn && !tipInscriere) {
|
||||
tipInscriere = nn;
|
||||
const quota = reg?.registration?.actualQuota;
|
||||
if (quota) cotaProprietate = String(quota);
|
||||
}
|
||||
if (nt === "A" && nn && !actProprietate) {
|
||||
actProprietate = nn;
|
||||
}
|
||||
if (nt === "C" && !dataCerere) {
|
||||
const appDate = reg?.application?.appDate;
|
||||
if (typeof appDate === "number" && appDate > 0) {
|
||||
dataCerere = new Date(appDate).toISOString().slice(0, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Building body details from local DB cladiri
|
||||
const cadRefBase = baseCadRef(cadRefRaw);
|
||||
let nrCorpuri = 0;
|
||||
const corpuriParts: string[] = [];
|
||||
for (const cFeature of cladiri) {
|
||||
const cAttrs = cFeature.attributes as Record<string, unknown>;
|
||||
const cRef = String(cAttrs.NATIONAL_CADASTRAL_REFERENCE ?? "");
|
||||
if (baseCadRef(cRef) === cadRefBase && cRef.includes("-")) {
|
||||
nrCorpuri++;
|
||||
const suffix = cRef.slice(cRef.lastIndexOf("-") + 1);
|
||||
const cArea = typeof cAttrs.AREA_VALUE === "number" ? cAttrs.AREA_VALUE : 0;
|
||||
corpuriParts.push(`${suffix}:${Math.round(cArea)}mp`);
|
||||
}
|
||||
}
|
||||
|
||||
// Condominium status and creation date from documentation
|
||||
const docImmovable = docKey ? docByImmovable.get(docKey) : undefined;
|
||||
const isCondominium = Number(
|
||||
(docImmovable as Record<string, unknown>)?.isCondominium ?? 0,
|
||||
);
|
||||
const createdDtm = attrs.CREATED_DTM;
|
||||
const dataCreare =
|
||||
typeof createdDtm === "number" && createdDtm > 0
|
||||
? new Date(createdDtm).toISOString().slice(0, 10)
|
||||
: "";
|
||||
|
||||
const enrichment: FeatureEnrichment = {
|
||||
NR_CAD: cadRefRaw,
|
||||
NR_CF: nrCF,
|
||||
@@ -654,8 +733,16 @@ export async function enrichFeatures(
|
||||
SOLICITANT: solicitant,
|
||||
INTRAVILAN: intravilan,
|
||||
CATEGORIE_FOLOSINTA: categorie,
|
||||
HAS_BUILDING: hasBuilding,
|
||||
HAS_BUILDING: hasBuilding || (nrCorpuri > 0 ? 1 : 0),
|
||||
BUILD_LEGAL: buildLegal,
|
||||
TIP_INSCRIERE: tipInscriere || undefined,
|
||||
ACT_PROPRIETATE: actProprietate || undefined,
|
||||
COTA_PROPRIETATE: cotaProprietate || undefined,
|
||||
DATA_CERERE: dataCerere || undefined,
|
||||
NR_CORPURI: nrCorpuri,
|
||||
CORPURI_DETALII: corpuriParts.length > 0 ? corpuriParts.join(", ") : undefined,
|
||||
IS_CONDOMINIUM: isCondominium,
|
||||
DATA_CREARE: dataCreare || undefined,
|
||||
};
|
||||
|
||||
// Store enrichment in DB
|
||||
|
||||
Reference in New Issue
Block a user