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;
|
CATEGORIE_FOLOSINTA: string;
|
||||||
HAS_BUILDING: number;
|
HAS_BUILDING: number;
|
||||||
BUILD_LEGAL: 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 ──
|
// ── Fetch documentation/owner data ──
|
||||||
push({ phase: "Descărcare documentații CF" });
|
push({ phase: "Descărcare documentații CF" });
|
||||||
const docByImmovable = new Map<string, any>();
|
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 immovableIds = Array.from(immovableListById.keys());
|
||||||
const docBatchSize = 50;
|
const docBatchSize = 50;
|
||||||
for (let i = 0; i < immovableIds.length; i += docBatchSize) {
|
for (let i = 0; i < immovableIds.length; i += docBatchSize) {
|
||||||
@@ -385,6 +404,13 @@ export async function enrichFeatures(
|
|||||||
const nodeMap = new Map<number, any>();
|
const nodeMap = new Map<number, any>();
|
||||||
for (const reg of regs) {
|
for (const reg of regs) {
|
||||||
if (reg?.nodeId != null) nodeMap.set(Number(reg.nodeId), reg);
|
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
|
// Check if an entry or any ancestor "I" inscription is radiated
|
||||||
const isRadiated = (entry: any, depth = 0): boolean => {
|
const isRadiated = (entry: any, depth = 0): boolean => {
|
||||||
@@ -641,6 +667,59 @@ export async function enrichFeatures(
|
|||||||
: null);
|
: 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 = {
|
const enrichment: FeatureEnrichment = {
|
||||||
NR_CAD: cadRefRaw,
|
NR_CAD: cadRefRaw,
|
||||||
NR_CF: nrCF,
|
NR_CF: nrCF,
|
||||||
@@ -654,8 +733,16 @@ export async function enrichFeatures(
|
|||||||
SOLICITANT: solicitant,
|
SOLICITANT: solicitant,
|
||||||
INTRAVILAN: intravilan,
|
INTRAVILAN: intravilan,
|
||||||
CATEGORIE_FOLOSINTA: categorie,
|
CATEGORIE_FOLOSINTA: categorie,
|
||||||
HAS_BUILDING: hasBuilding,
|
HAS_BUILDING: hasBuilding || (nrCorpuri > 0 ? 1 : 0),
|
||||||
BUILD_LEGAL: buildLegal,
|
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
|
// Store enrichment in DB
|
||||||
|
|||||||
Reference in New Issue
Block a user