From 74f2af9b21d1ba9a08eb768bc32639d03e896ea9 Mon Sep 17 00:00:00 2001 From: Johannes Baumeister Date: Thu, 30 Apr 2026 08:47:34 +0200 Subject: [PATCH] Fix owner matching logic, automate mapping, and improve status persistence --- app.js | 60 +++++++++++++++++++++++++++++---- backend/check_assignments.js | 35 +++++++++++++++++++ backend/check_project_status.js | 22 ++++++++++++ backend/inspect_alkis.js | 30 +++++++++++++++++ server.js | 14 ++++++-- 5 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 backend/check_assignments.js create mode 100644 backend/check_project_status.js create mode 100644 backend/inspect_alkis.js diff --git a/app.js b/app.js index 5006a7f..5d76c37 100644 --- a/app.js +++ b/app.js @@ -961,7 +961,7 @@ document.addEventListener('DOMContentLoaded', async () => { const props = feature.properties; const firstName = props[state.ownerMapping.firstName] || ''; const lastName = props[state.ownerMapping.lastName] || ''; - const ownerName = `${firstName} ${lastName}`.trim(); + const ownerName = `${firstName} ${lastName}`.trim().toLowerCase(); const status = state.ownerStatuses[ownerName]; if (status === 'gbr') fillColor = '#2ecc71'; @@ -1243,6 +1243,26 @@ document.addEventListener('DOMContentLoaded', async () => { } ownerModal.style.display = 'flex'; + + // Versuche Auto-Mapping falls noch nicht geschehen + if (!state.ownerMapping) { + const layer = overlays[ownerLayer]; + const allKeys = new Set(); + layer.eachLayer(l => { + if (l.feature && l.feature.properties) { + Object.keys(l.feature.properties).forEach(k => allKeys.add(k)); + } + }); + const sortedKeys = Array.from(allKeys); + const vnaMatch = sortedKeys.find(k => k.toUpperCase() === 'VNA'); + const gnaMatch = sortedKeys.find(k => k.toUpperCase() === 'GNA' || k.toUpperCase() === 'NBA'); + + if (vnaMatch && gnaMatch) { + state.ownerMapping = { firstName: vnaMatch, lastName: gnaMatch }; + console.log("Auto-Mapping erfolgreich:", state.ownerMapping); + } + } + if (!state.ownerMapping) { showMappingStage(overlays[ownerLayer]); } else { @@ -1364,9 +1384,9 @@ document.addEventListener('DOMContentLoaded', async () => { // Add event listeners to dropdowns document.querySelectorAll('.status-select').forEach(sel => { sel.onchange = async (e) => { - const name = e.target.dataset.owner; + const name = e.target.dataset.owner; // original name for display const status = e.target.value; - state.ownerStatuses[name] = status; + state.ownerStatuses[name.toLowerCase()] = status; // Sync with DB const data = owners[name]; @@ -1419,7 +1439,7 @@ document.addEventListener('DOMContentLoaded', async () => { const fullName = `${vorname || ''} ${nachname || ''}`.trim(); if (fullName) { - state.ownerStatuses[fullName] = status; + state.ownerStatuses[fullName.toLowerCase()] = status; } const ownerLayerName = Object.keys(overlays).find(k => k.toLowerCase().includes('eigentümer')); @@ -1507,7 +1527,7 @@ document.addEventListener('DOMContentLoaded', async () => { statuses.forEach(s => { const first = s.vorname || ''; const last = s.nachname || ''; - const fullName = `${first} ${last}`.trim(); + const fullName = `${first} ${last}`.trim().toLowerCase(); if (fullName) { state.ownerStatuses[fullName] = s.status; } @@ -1527,8 +1547,34 @@ document.addEventListener('DOMContentLoaded', async () => { } } - initDynamicLayers().then(() => { - loadOwnerStatusesFromDB(); + // Hilfsfunktion zum Neu-Stylen des Eigentümer-Layers + function refreshOwnerLayerStyle() { + const ownerLayerName = Object.keys(overlays).find(k => k.toLowerCase().includes('eigentümer')); + if (ownerLayerName && overlays[ownerLayerName]) { + overlays[ownerLayerName].setStyle(overlays[ownerLayerName].options.style); + } + } + + initDynamicLayers().then(async () => { + // Erst Status laden, dann WEAs + await loadOwnerStatusesFromDB(); + + // Nach dem Laden der Status: Prüfen ob wir den Layer automatisch mappen können + const ownerLayerName = Object.keys(overlays).find(k => k.toLowerCase().includes('eigentümer')); + if (ownerLayerName && overlays[ownerLayerName]) { + const layer = overlays[ownerLayerName]; + const firstFeature = layer.getLayers()[0]?.feature; + if (firstFeature && firstFeature.properties) { + const props = firstFeature.properties; + const vna = Object.keys(props).find(k => k.toUpperCase() === 'VNA'); + const gna = Object.keys(props).find(k => k.toUpperCase() === 'GNA' || k.toUpperCase() === 'NBA'); + if (vna && gna) { + state.ownerMapping = { firstName: vna, lastName: gna }; + refreshOwnerLayerStyle(); // Jetzt werden die Farben sichtbar! + } + } + } + loadTurbinesFromDB(); }); diff --git a/backend/check_assignments.js b/backend/check_assignments.js new file mode 100644 index 0000000..df1ed7d --- /dev/null +++ b/backend/check_assignments.js @@ -0,0 +1,35 @@ +const { Client } = require('pg'); +const client = new Client({ + host: '87.106.21.21', + port: 5432, + user: 'enwelo_admin', + password: 'WX1t1cgP1qK09', + database: 'enwelo' +}); +client.connect().then(async () => { + try { + const res = await client.query(` + SELECT + a."VNA", a."GNA", a."FSK", + z.projekt_id IS NOT NULL as is_assigned + FROM geodaten.flaecheneigentuemer_alkis a + LEFT JOIN geodaten.flaecheneigentuemer_alkis_zuweisung z ON a."FSK" = z.fsk + LIMIT 20 + `); + console.table(res.rows); + + const summary = await client.query(` + SELECT + count(*) as total, + count(z.fsk) as assigned + FROM geodaten.flaecheneigentuemer_alkis a + LEFT JOIN geodaten.flaecheneigentuemer_alkis_zuweisung z ON a."FSK" = z.fsk + `); + console.log("Summary:", summary.rows[0]); + + } catch (e) { + console.error(e); + } finally { + client.end(); + } +}); diff --git a/backend/check_project_status.js b/backend/check_project_status.js new file mode 100644 index 0000000..8995ed4 --- /dev/null +++ b/backend/check_project_status.js @@ -0,0 +1,22 @@ +const { Client } = require('pg'); +const client = new Client({ + host: '87.106.21.21', + port: 5432, + user: 'enwelo_admin', + password: 'WX1t1cgP1qK09', + database: 'enwelo' +}); +client.connect().then(async () => { + try { + const res = await client.query('SELECT count(*) FROM geodaten.flaecheneigentuemer_status WHERE projekt_id = \'5bb4e049-85f2-4433-b38e-6a66b81e9f06\''); + console.log('Status count for bw_samern-ohne: ' + res.rows[0].count); + + const sample = await client.query('SELECT * FROM geodaten.flaecheneigentuemer_status WHERE projekt_id = \'5bb4e049-85f2-4433-b38e-6a66b81e9f06\' LIMIT 5'); + console.log('Sample entries:'); + console.table(sample.rows); + } catch (e) { + console.error(e); + } finally { + client.end(); + } +}); diff --git a/backend/inspect_alkis.js b/backend/inspect_alkis.js new file mode 100644 index 0000000..9440d66 --- /dev/null +++ b/backend/inspect_alkis.js @@ -0,0 +1,30 @@ +const { Client } = require('pg'); +const client = new Client({ + host: '87.106.21.21', + port: 5432, + user: 'enwelo_admin', + password: 'WX1t1cgP1qK09', + database: 'enwelo' +}); +client.connect().then(async () => { + try { + console.log("--- Sample from flaecheneigentuemer_alkis ---"); + const alkis = await client.query('SELECT "GNA", "VNA", "FSK" FROM geodaten.flaecheneigentuemer_alkis LIMIT 5'); + console.log("Keys in result row:", Object.keys(alkis.rows[0])); + console.table(alkis.rows); + + console.log("\n--- Sample from flaecheneigentuemer_alkis_zuweisung ---"); + const zuweisung = await client.query('SELECT * FROM geodaten.flaecheneigentuemer_alkis_zuweisung LIMIT 5'); + console.table(zuweisung.rows); + + console.log("\n--- Checking if names match exactly ---"); + // Try a name search with LOWER for testing + const nameSearch = await client.query('SELECT count(*) FROM geodaten.flaecheneigentuemer_alkis WHERE "GNA" = $1', [alkis.rows[0].GNA]); + console.log(`Searching for "${alkis.rows[0].GNA}": found ${nameSearch.rows[0].count} entries.`); + + } catch (e) { + console.error(e); + } finally { + client.end(); + } +}); diff --git a/server.js b/server.js index 79e8d42..f093f9e 100644 --- a/server.js +++ b/server.js @@ -150,11 +150,19 @@ app.post('/api/sicherung', async (req, res) => { } // 2. FSKs für den Namen finden (Behandlung von NULL vs leerem String) + // Wir suchen flexibel: entweder exakter Match oder der Name ist Teil eines kombinierten Feldes + const searchNachname = (nachname || '').trim().toLowerCase(); + const searchVorname = (vorname || '').trim().toLowerCase(); + const searchFull = `${searchVorname} ${searchNachname}`.trim().toLowerCase(); + const ownerRes = await client.query( `SELECT "FSK" FROM ${schema}.flaecheneigentuemer_alkis - WHERE ("GNA" = $1 OR ("GNA" IS NULL AND $1 = '')) - AND ("VNA" = $2 OR ("VNA" IS NULL AND $2 = ''))`, - [nachname, vorname] + WHERE + (LOWER("GNA") = $1 AND LOWER("VNA") = $2) OR + (LOWER("VNA") = $3 AND "GNA" IS NULL) OR + (LOWER("GNA") = $3 AND "VNA" IS NULL) OR + (LOWER("VNA") LIKE '%' || $1 || '%' AND LOWER("VNA") LIKE '%' || $2 || '%')`, + [searchNachname, searchVorname, searchFull] ); const fsks = ownerRes.rows.map(r => r.FSK);