From 189e9a218ae08d7f340eac0ace39215d766d6b29 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Fri, 27 Mar 2026 06:43:04 +0200 Subject: [PATCH] fix(parcel-sync): fix [object Object] in address field + re-enrich corrupted The eTerra API returns street and locality as objects ({name: "..."}) not strings. formatAddress now extracts .name correctly. Also added: - streetNumber fallback (alongside buildingNo) - String() safety on addressDescription - Corruption check: any enrichment containing "[object Object]" is automatically re-enriched on next cycle Co-Authored-By: Claude Opus 4.6 (1M context) --- .../parcel-sync/services/enrich-service.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/modules/parcel-sync/services/enrich-service.ts b/src/modules/parcel-sync/services/enrich-service.ts index 781a0be..f14a502 100644 --- a/src/modules/parcel-sync/services/enrich-service.ts +++ b/src/modules/parcel-sync/services/enrich-service.ts @@ -99,10 +99,21 @@ const formatAddress = (item?: any) => { const address = item?.immovableAddresses?.[0]?.address ?? null; if (!address) return "-"; const parts: string[] = []; - if (address.addressDescription) parts.push(address.addressDescription); - if (address.street) parts.push(`Str. ${address.street}`); - if (address.buildingNo) parts.push(`Nr. ${address.buildingNo}`); - if (address.locality?.name) parts.push(address.locality.name); + if (address.addressDescription) parts.push(String(address.addressDescription)); + // street can be a string or an object { name: "..." } + const streetName = + typeof address.street === "string" + ? address.street + : address.street?.name ?? null; + if (streetName) parts.push(`Str. ${streetName}`); + if (address.streetNumber) parts.push(`Nr. ${address.streetNumber}`); + else if (address.buildingNo) parts.push(`Nr. ${address.buildingNo}`); + // locality can be a string or an object { name: "..." } + const localityName = + typeof address.locality === "string" + ? address.locality + : address.locality?.name ?? null; + if (localityName) parts.push(localityName); return parts.length ? parts.join(", ") : "-"; }; @@ -439,11 +450,18 @@ export async function enrichFeatures( enrichJson[k] !== "", ); + // Corruption check: re-enrich if any field contains "[object Object]" + const hasCorruptedValues = + enrichJson != null && + Object.values(enrichJson).some( + (v) => typeof v === "string" && v.includes("[object Object]"), + ); + // Age check: re-enrich if older than 30 days (catches eTerra updates) const ageMs = Date.now() - new Date(feature.enrichedAt).getTime(); const isTooOld = ageMs > 30 * 24 * 60 * 60 * 1000; - if (structurallyComplete && hasRealValues && !isTooOld) { + if (structurallyComplete && hasRealValues && !isTooOld && !hasCorruptedValues) { enrichedCount += 1; if (index % 50 === 0) { options?.onProgress?.(