feat: enrichment fallback via direct parcel details endpoint
PROBLEM:
For no-geometry parcels (and many geometry parcels without application
IDs), CATEGORIE_FOLOSINTA was always '-' because:
1. fetchImmAppsByImmovable returned no apps (no applicationId)
2. Without appId, fetchParcelFolosinte was skipped entirely
3. No fallback existed
DISCOVERY (from eTerra UI investigation):
The endpoint /api/immovable/details/parcels/list/{wp}/{pk}/{page}/{size}
returns parcel use categories DIRECTLY — no applicationId needed.
Example: [{useCategory:'arabil', intravilan:'Necunoscut', parcelPk:17753903}]
FIX:
- After the app-based CATEGORIE_FOLOSINTA attempt, if result is still '-',
fall back to fetchImmovableParcelDetails (the direct endpoint)
- formatCategories now handles both API formats:
- App-based: categorieFolosinta + suprafata fields
- Direct: useCategory field (no area — shows category name only)
- When direct endpoint provides area=0, format shows just the category
name without ':0' (e.g. 'arabil; faneata' instead of 'arabil:0; faneata:0')
- Also picks up intravilan from direct endpoint if app-based was empty
- Fixed fetchImmovableParcelDetails default size: 1 → 20 (one immovable
can have multiple parcels, e.g. IE 25332 has 2: arabil + faneata)
- Results are cached in folCache to avoid duplicate requests
This commit is contained in:
@@ -80,13 +80,18 @@ const normalizeIntravilan = (values: string[]) => {
|
||||
const formatCategories = (entries: any[]) => {
|
||||
const map = new Map<string, number>();
|
||||
for (const entry of entries) {
|
||||
const key = String(entry?.categorieFolosinta ?? "").trim();
|
||||
// Support both API formats:
|
||||
// fetchParcelFolosinte (via app): categorieFolosinta + suprafata
|
||||
// fetchImmovableParcelDetails (direct): useCategory (no area)
|
||||
const key = String(
|
||||
entry?.categorieFolosinta ?? entry?.useCategory ?? "",
|
||||
).trim();
|
||||
if (!key) continue;
|
||||
const area = Number(entry?.suprafata ?? 0);
|
||||
map.set(key, (map.get(key) ?? 0) + (Number.isFinite(area) ? area : 0));
|
||||
}
|
||||
return Array.from(map.entries())
|
||||
.map(([k, a]) => `${k}:${formatNumber(a)}`)
|
||||
.map(([k, a]) => (a > 0 ? `${k}:${formatNumber(a)}` : k))
|
||||
.join("; ");
|
||||
};
|
||||
|
||||
@@ -482,6 +487,40 @@ export async function enrichFeatures(
|
||||
);
|
||||
categorie = formatCategories(fol);
|
||||
}
|
||||
|
||||
// Fallback: if no application or empty categories, use direct
|
||||
// parcel details endpoint (doesn't need applicationId).
|
||||
// Discovered via eTerra UI: /api/immovable/details/parcels/list/{wp}/{pk}/{page}/{size}
|
||||
// Returns: [{useCategory: "arabil", intravilan: "Necunoscut", ...}]
|
||||
if (categorie === "-" && immovableId) {
|
||||
const immPkForDetails =
|
||||
immovableListById.get(normalizeId(immovableId))?.immovablePk ??
|
||||
immovableId;
|
||||
const detKey = `${workspaceId}:${immPkForDetails}:details`;
|
||||
let details = folCache.get(detKey);
|
||||
if (!details) {
|
||||
try {
|
||||
details = await throttled(() =>
|
||||
client.fetchImmovableParcelDetails(
|
||||
workspaceId as string | number,
|
||||
immPkForDetails as string | number,
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
details = [];
|
||||
}
|
||||
folCache.set(detKey, details);
|
||||
}
|
||||
if (details && details.length > 0) {
|
||||
const detIntravilan = normalizeIntravilan(
|
||||
details.map((d: any) => d?.intravilan ?? ""),
|
||||
);
|
||||
const detCategorie = formatCategories(details);
|
||||
if (detCategorie && detCategorie !== "-") categorie = detCategorie;
|
||||
if (detIntravilan && detIntravilan !== "-" && intravilan === "-")
|
||||
intravilan = detIntravilan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cadRefRaw = (attrs.NATIONAL_CADASTRAL_REFERENCE ?? "") as string;
|
||||
|
||||
@@ -362,9 +362,7 @@ export class EterraClient {
|
||||
await sleep(500); // small delay before retry with smaller page
|
||||
continue;
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to fetch layer ${layer.name}: ${cause}`,
|
||||
);
|
||||
throw new Error(`Failed to fetch layer ${layer.name}: ${cause}`);
|
||||
}
|
||||
|
||||
const features = data.features ?? [];
|
||||
@@ -504,7 +502,7 @@ export class EterraClient {
|
||||
workspaceId: string | number,
|
||||
immovableId: string | number,
|
||||
page = 1,
|
||||
size = 1,
|
||||
size = 20,
|
||||
): Promise<any[]> {
|
||||
const url = `${BASE_URL}/api/immovable/details/parcels/list/${workspaceId}/${immovableId}/${page}/${size}`;
|
||||
return this.getRawJson(url);
|
||||
|
||||
Reference in New Issue
Block a user