fix(search): use legalArea/measuredArea + nodeStatus=-1 for radiated owners

- Area: use measuredArea/legalArea from immovable list and documentation
  (actual fields from eTerra API, not area/areaValue which don't exist)
- Owners: detect radiated via nodeStatus === -1 on ancestor I (inscription)
  nodes. Walk up parentId tree from P (person)  I  A  C.
  nodeStatus: -1=radiated, 0=active, 2=pending
- Remove debug logging (data structure now understood)
This commit is contained in:
AI Assistant
2026-03-06 22:16:14 +02:00
parent 79c45adc37
commit 4aa8e6c324
+46 -59
View File
@@ -111,19 +111,19 @@ function formatAddress(item?: any) {
const streetName =
typeof address.street === "string"
? address.street
: address.street?.name ?? "";
: (address.street?.name ?? "");
if (streetName) parts.push(`Str. ${streetName}`);
if (address.buildingNo) parts.push(`Nr. ${address.buildingNo}`);
// locality can also be a string or object
const localityName =
typeof address.locality === "string"
? address.locality
: address.locality?.name ?? "";
: (address.locality?.name ?? "");
if (localityName) parts.push(localityName);
const countyName =
typeof address.county === "string"
? address.county
: address.county?.name ?? "";
: (address.county?.name ?? "");
if (countyName) parts.push(`Jud. ${countyName}`);
return parts.length ? parts.join(", ") : "";
}
@@ -310,24 +310,20 @@ export async function POST(req: Request) {
let categorie = "";
let suprafata: number | null = null;
// Try multiple area fields
const areaStr =
item?.area ?? item?.areaValue ?? item?.areaMP ?? item?.suprafata;
if (areaStr != null) {
const parsed = Number(areaStr);
if (Number.isFinite(parsed) && parsed > 0) suprafata = parsed;
}
// Log raw item keys once for debugging (first item only)
if (results.length === 0) {
console.log(
"[search] immovable item keys:",
Object.keys(item ?? {}),
);
console.log("[search] area fields:", {
area: item?.area,
areaValue: item?.areaValue,
areaMP: item?.areaMP,
});
// Area: use measuredArea first, then legalArea as fallback
for (const areaField of [
item?.measuredArea,
item?.legalArea,
item?.area,
item?.areaValue,
]) {
if (areaField != null) {
const parsed = Number(areaField);
if (Number.isFinite(parsed) && parsed > 0) {
suprafata = parsed;
break;
}
}
}
// 2. Fetch documentation data (CF, proprietari)
@@ -350,60 +346,50 @@ export async function POST(req: Request) {
if (oldCF && oldCF !== nrCF) nrCFVechi = oldCF;
}
if (docImm.topNo) nrTopo = String(docImm.topNo);
if (docImm.area != null) {
const docArea = Number(docImm.area);
if (Number.isFinite(docArea)) suprafata = docArea;
// Try measuredArea/legalArea from doc too
for (const af of [
docImm.measuredArea,
docImm.legalArea,
docImm.area,
]) {
if (af != null) {
const docArea = Number(af);
if (Number.isFinite(docArea) && docArea > 0) {
suprafata = docArea;
break;
}
}
}
}
// Extract owners from partTwoRegs — separate active vs cancelled
// using tree structure: "P" (person) nodes are children of
// inscription nodes. If a parent inscription has radiationDate,
// its person entries are former owners.
// Tree: C (cerere) → A (act) → I (inscription) → P (person)
// nodeStatus === -1 on an "I" node means it's radiated.
// Walk up from each "P" to its parent "I", check nodeStatus.
const activeOwners: string[] = [];
const cancelledOwners: string[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const regs: any[] = docResponse?.partTwoRegs ?? [];
if (regs.length > 0 && results.length === 0) {
console.log(
"[search] partTwoRegs count:",
regs.length,
"types:",
[...new Set(regs.map((r: any) => r?.nodeType))],
);
// Log first 3 entries fully
regs.slice(0, 3).forEach((r: any, i: number) =>
console.log(`[search] partTwoRegs[${i}]:`, JSON.stringify(r).slice(0, 600)),
);
// Log last 3 entries
regs.slice(-3).forEach((r: any, i: number) =>
console.log(`[search] partTwoRegs[${regs.length - 3 + i}]:`, JSON.stringify(r).slice(0, 600)),
);
}
// Build nodeId → entry map for tree traversal
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const nodeMap = new Map<string | number, any>();
const nodeMap = new Map<number, any>();
for (const reg of regs) {
const nid = reg?.nodeId ?? reg?.id;
if (nid != null) nodeMap.set(nid, reg);
if (reg?.nodeId != null) nodeMap.set(Number(reg.nodeId), reg);
}
// Check if an entry or any ancestor is radiated
// Check if an entry or any ancestor "I" inscription is radiated
// nodeStatus: -1 = radiated, 0 = active, 2 = pending
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const isRadiated = (entry: any): boolean => {
// Direct checks on the entry itself
if (entry?.radiationDate != null) return true;
if (entry?.cancelled === true) return true;
if (entry?.isActive === false) return true;
if (entry?.closed === true) return true;
const st = String(entry?.status ?? "").toUpperCase();
if (st === "RADIAT" || st === "CANCELLED" || st === "CLOSED") return true;
const isRadiated = (entry: any, depth = 0): boolean => {
if (!entry || depth > 10) return false;
// nodeStatus === -1 means radiated/cancelled
if (entry?.nodeStatus === -1) return true;
// Walk up to parent
const pid = entry?.parentNodeId ?? entry?.parentId;
const pid = entry?.parentId;
if (pid != null) {
const parent = nodeMap.get(pid);
if (parent) return isRadiated(parent);
const parent = nodeMap.get(Number(pid));
if (parent) return isRadiated(parent, depth + 1);
}
return false;
};
@@ -412,7 +398,8 @@ export async function POST(req: Request) {
if (
String(reg?.nodeType ?? "").toUpperCase() !== "P" ||
!reg?.nodeName
) continue;
)
continue;
const name = String(reg.nodeName).trim();
if (!name) continue;
if (isRadiated(reg)) {