feat(parcel-sync): add /api/eterra/debug-fields diagnostic endpoint
Shows all available eTerra fields for a parcel + buildings: - GIS layer attributes (raw from ArcGIS) - Immovable parcel details (intravilan, categories) - Immovable list entry (address, areas) - Documentation data (owners, registrations) - Local DB state (enrichment, sync dates) Usage: /api/eterra/debug-fields?siruta=161829&cadRef=77102 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { EterraClient } from "@/modules/parcel-sync/services/eterra-client";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
* GET /api/eterra/debug-fields?siruta=161829&cadRef=77102
|
||||
*
|
||||
* Diagnostic endpoint — shows all available fields from eTerra + local DB
|
||||
* for a specific parcel and its buildings.
|
||||
*/
|
||||
export async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const siruta = url.searchParams.get("siruta") ?? "161829";
|
||||
const cadRef = url.searchParams.get("cadRef") ?? "77102";
|
||||
|
||||
const username = process.env.ETERRA_USERNAME;
|
||||
const password = process.env.ETERRA_PASSWORD;
|
||||
if (!username || !password) {
|
||||
return NextResponse.json({ error: "ETERRA creds missing" }, { status: 500 });
|
||||
}
|
||||
|
||||
const result: Record<string, unknown> = {
|
||||
query: { siruta, cadRef },
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
try {
|
||||
const client = await EterraClient.create(username, password);
|
||||
|
||||
// 1. GIS layer: TERENURI_ACTIVE — raw attributes
|
||||
const terenuri = await client.listLayerByWhere(
|
||||
{ id: "TERENURI_ACTIVE", name: "TERENURI_ACTIVE", endpoint: "aut" },
|
||||
`ADMIN_UNIT_ID=${siruta} AND IS_ACTIVE=1 AND NATIONAL_CADASTRAL_REFERENCE='${cadRef}'`,
|
||||
{ limit: 1, outFields: "*" },
|
||||
);
|
||||
const parcelAttrs = terenuri[0]?.attributes ?? null;
|
||||
result.gis_parcela = {
|
||||
found: !!parcelAttrs,
|
||||
fields: parcelAttrs
|
||||
? Object.entries(parcelAttrs)
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([k, v]) => ({ field: k, value: v, type: typeof v }))
|
||||
: [],
|
||||
};
|
||||
|
||||
// 2. GIS layer: CLADIRI_ACTIVE — buildings on this parcel
|
||||
const cladiri = await client.listLayerByWhere(
|
||||
{ id: "CLADIRI_ACTIVE", name: "CLADIRI_ACTIVE", endpoint: "aut" },
|
||||
`ADMIN_UNIT_ID=${siruta} AND IS_ACTIVE=1 AND NATIONAL_CADASTRAL_REFERENCE LIKE '${cadRef}-%'`,
|
||||
{ limit: 20, outFields: "*" },
|
||||
);
|
||||
result.gis_cladiri = {
|
||||
count: cladiri.length,
|
||||
buildings: cladiri.map((c) => {
|
||||
const a = c.attributes;
|
||||
return {
|
||||
cadastralRef: a.NATIONAL_CADASTRAL_REFERENCE,
|
||||
fields: Object.entries(a)
|
||||
.sort(([x], [y]) => x.localeCompare(y))
|
||||
.filter(([, v]) => v != null && v !== "" && v !== 0)
|
||||
.map(([k, v]) => ({ field: k, value: v, type: typeof v })),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
// 3. Immovable details (enrichment source)
|
||||
const immId = parcelAttrs?.IMMOVABLE_ID;
|
||||
const wsId = parcelAttrs?.WORKSPACE_ID;
|
||||
if (immId && wsId) {
|
||||
try {
|
||||
const details = await client.fetchImmovableParcelDetails(
|
||||
wsId as string | number,
|
||||
immId as string | number,
|
||||
);
|
||||
result.immovable_parcel_details = {
|
||||
count: details.length,
|
||||
items: details,
|
||||
};
|
||||
} catch (e) {
|
||||
result.immovable_parcel_details = {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
};
|
||||
}
|
||||
|
||||
// 4. Immovable list entry (address source)
|
||||
try {
|
||||
const listResponse = await client.fetchImmovableListByAdminUnit(
|
||||
wsId as number,
|
||||
siruta,
|
||||
0,
|
||||
5,
|
||||
true,
|
||||
);
|
||||
const items = (listResponse?.content ?? []) as Record<string, unknown>[];
|
||||
// Find our specific immovable
|
||||
const match = items.find(
|
||||
(item) => String(item.immovablePk) === String(immId) ||
|
||||
String(item.identifierDetails ?? "").includes(cadRef),
|
||||
);
|
||||
result.immovable_list_entry = {
|
||||
totalInUat: listResponse?.totalElements ?? "?",
|
||||
matchFound: !!match,
|
||||
entry: match ?? null,
|
||||
note: "Acest obiect contine campul immovableAddresses cu adresa completa",
|
||||
};
|
||||
} catch (e) {
|
||||
result.immovable_list_entry = {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
};
|
||||
}
|
||||
|
||||
// 5. Documentation data (owner source)
|
||||
try {
|
||||
const docResponse = await client.fetchDocumentationData(
|
||||
wsId as number,
|
||||
[String(immId)],
|
||||
);
|
||||
const immovables = docResponse?.immovables ?? [];
|
||||
const regs = docResponse?.partTwoRegs ?? [];
|
||||
result.documentation_data = {
|
||||
immovablesCount: immovables.length,
|
||||
immovables: immovables.slice(0, 3),
|
||||
registrationsCount: regs.length,
|
||||
registrations: regs.slice(0, 10),
|
||||
note: "partTwoRegs contine proprietarii (nodeType=P, nodeStatus=-1=radiat)",
|
||||
};
|
||||
} catch (e) {
|
||||
result.documentation_data = {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Local DB data (what we have stored)
|
||||
const dbParcel = await prisma.gisFeature.findFirst({
|
||||
where: { layerId: "TERENURI_ACTIVE", siruta, cadastralRef: cadRef },
|
||||
select: {
|
||||
objectId: true,
|
||||
cadastralRef: true,
|
||||
areaValue: true,
|
||||
isActive: true,
|
||||
enrichment: true,
|
||||
enrichedAt: true,
|
||||
geometrySource: true,
|
||||
},
|
||||
});
|
||||
const dbBuildings = await prisma.gisFeature.findMany({
|
||||
where: {
|
||||
layerId: "CLADIRI_ACTIVE",
|
||||
siruta,
|
||||
cadastralRef: { startsWith: `${cadRef}-` },
|
||||
},
|
||||
select: {
|
||||
objectId: true,
|
||||
cadastralRef: true,
|
||||
areaValue: true,
|
||||
attributes: true,
|
||||
},
|
||||
});
|
||||
result.local_db = {
|
||||
parcel: dbParcel
|
||||
? {
|
||||
objectId: dbParcel.objectId,
|
||||
cadastralRef: dbParcel.cadastralRef,
|
||||
areaValue: dbParcel.areaValue,
|
||||
enrichedAt: dbParcel.enrichedAt,
|
||||
geometrySource: dbParcel.geometrySource,
|
||||
enrichment: dbParcel.enrichment,
|
||||
}
|
||||
: null,
|
||||
buildings: dbBuildings.map((b) => ({
|
||||
objectId: b.objectId,
|
||||
cadastralRef: b.cadastralRef,
|
||||
areaValue: b.areaValue,
|
||||
is_legal: (b.attributes as Record<string, unknown>)?.IS_LEGAL,
|
||||
})),
|
||||
};
|
||||
} catch (e) {
|
||||
result.error = e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
|
||||
return NextResponse.json(result, {
|
||||
headers: { "Content-Type": "application/json; charset=utf-8" },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user