Fix owner matching logic, automate mapping, and improve status persistence
Deploy Bürgerwind / deploy (push) Successful in 16s Details

This commit is contained in:
Johannes Baumeister 2026-04-30 08:47:34 +02:00
parent 7374e98612
commit 74f2af9b21
5 changed files with 151 additions and 10 deletions

60
app.js
View File

@ -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();
});

View File

@ -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();
}
});

View File

@ -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();
}
});

30
backend/inspect_alkis.js Normal file
View File

@ -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();
}
});

View File

@ -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);