v1.29: Heizungsart-Dropdown (Haus + Wohnung), Listenansicht als Standard

This commit is contained in:
2026-10-04 18:04:02 +02:00
parent e115908cd9
commit 662676a992
8 changed files with 127 additions and 21 deletions
+3
View File
@@ -172,6 +172,9 @@ ALTER TABLE buildings ADD COLUMN IF NOT EXISTS ort TEXT NOT NULL DEFAULT '';
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS lage TEXT NOT NULL DEFAULT '';
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS wohnflaeche NUMERIC(9,1);
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS grundstuecksflaeche NUMERIC(9,1);
-- v1.29: Heizungsart (Dropdown) in Haus- und Wohnungsverwaltung
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS heizungsart TEXT NOT NULL DEFAULT '';
ALTER TABLE units ADD COLUMN IF NOT EXISTS heizungsart TEXT NOT NULL DEFAULT '';
CREATE TABLE IF NOT EXISTS costs (
id TEXT PRIMARY KEY,
+29 -12
View File
@@ -156,7 +156,7 @@ async function sessionAnlegen(res, userId, req) {
const mapUnit = (u) => ({
id: u.id, building: u.building, number: u.number, floor: u.floor,
size: Number(u.size), coldRent: Number(u.cold_rent), warmRent: Number(u.warm_rent),
tenant: u.tenant, status: u.status,
tenant: u.tenant, status: u.status, heizungsart: u.heizungsart ?? '',
})
const mapTransaction = (t) => ({
id: t.id, unitId: t.unit_id, tenant: t.tenant, month: t.month, year: t.year,
@@ -194,6 +194,7 @@ const mapBuilding = (b) => ({
wohnflaeche: Number(b.wohnflaeche ?? b.quadratmeter),
grundstuecksflaeche: b.grundstuecksflaeche === null || b.grundstuecksflaeche === undefined
? null : Number(b.grundstuecksflaeche),
heizungsart: b.heizungsart ?? '',
})
const mapKosten = (k) => ({
id: k.id, building: k.building, unitId: k.unit_id, jahr: k.jahr,
@@ -549,6 +550,14 @@ app.patch('/api/tickets/:id', auth, adminOnly, async (req, res, next) => {
// ── Zählerstände (Strom / Wasser / Gas) ─────────────────────
const ZAEHLER_ARTEN = ['Strom', 'Wasser', 'Gas', 'Wärme', 'Sonstiges']
// Gängige Heizungsformen (v1.29) – Dropdown in Haus-/Wohnungsformular
const HEIZUNGSARTEN = [
'Gas-Zentralheizung', 'Öl-Zentralheizung', 'Fernwärme', 'Wärmepumpe',
'Pellets-/Holzheizung', 'Nachtspeicherheizung', 'Elektro-Direktheizung',
'Einzelraumheizung (Ofen/Kamin)', 'Sonstiges',
]
/** Heizungsart validieren: leer (nicht angegeben) oder Listenwert. */
const heizungsartValid = (h) => !h || HEIZUNGSARTEN.includes(h)
app.post('/api/meters', auth, adminOnly, async (req, res, next) => {
try {
@@ -1094,14 +1103,16 @@ app.post('/api/buildings', auth, adminOnly, async (req, res, next) => {
const vorhanden = await pool.query('SELECT 1 FROM buildings WHERE name = $1', [name])
if (vorhanden.rows.length) return res.status(400).json({ fehler: 'Ein Haus mit dieser Straße und Hausnummer existiert bereits.' })
const grund = b.grundstuecksflaeche ? Number(b.grundstuecksflaeche) : null
if (!heizungsartValid(String(b.heizungsart || '').trim())) return res.status(400).json({ fehler: 'Ungültige Heizungsart.' })
const heizung = String(b.heizungsart || '').trim()
const { rows } = await pool.query(
`INSERT INTO buildings (name, adresse, strasse, hausnummer, plz, ort, lage,
baujahr, stockwerke, etagenhoehe, wohneinheiten, wohnflaeche, grundstuecksflaeche)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING *`,
baujahr, stockwerke, etagenhoehe, wohneinheiten, wohnflaeche, grundstuecksflaeche, heizungsart)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) RETURNING *`,
[name, hausAdresseAus(b), String(b.strasse).trim(), String(b.hausnummer).trim(),
String(b.plz).trim(), String(b.ort).trim(), String(b.lage || '').trim(),
Number(b.baujahr), Number(b.stockwerke), Number(b.etagenhoehe),
Number(b.wohneinheiten), Number(b.wohnflaeche), grund],
Number(b.wohneinheiten), Number(b.wohnflaeche), grund, heizung],
)
res.json({ building: mapBuilding(rows[0]) })
} catch (e) { next(e) }
@@ -1122,18 +1133,20 @@ app.patch('/api/buildings/:name', auth, adminOnly, async (req, res, next) => {
if (vorhanden.rows.length) return res.status(400).json({ fehler: 'Ein Haus mit dieser Straße und Hausnummer existiert bereits.' })
}
const grund = b.grundstuecksflaeche ? Number(b.grundstuecksflaeche) : null
if (!heizungsartValid(String(b.heizungsart || '').trim())) return res.status(400).json({ fehler: 'Ungültige Heizungsart.' })
const heizung = String(b.heizungsart || '').trim()
const client = await pool.connect()
try {
await client.query('BEGIN')
await client.query(
`UPDATE buildings SET name = $1, adresse = $2, strasse = $3, hausnummer = $4, plz = $5, ort = $6, lage = $7,
baujahr = $8, stockwerke = $9, etagenhoehe = $10, wohneinheiten = $11,
wohnflaeche = $12, grundstuecksflaeche = $13
WHERE name = $14`,
wohnflaeche = $12, grundstuecksflaeche = $13, heizungsart = $14
WHERE name = $15`,
[neuName, hausAdresseAus(b), String(b.strasse).trim(), String(b.hausnummer).trim(),
String(b.plz).trim(), String(b.ort).trim(), String(b.lage || '').trim(),
Number(b.baujahr), Number(b.stockwerke), Number(b.etagenhoehe), Number(b.wohneinheiten),
Number(b.wohnflaeche), grund, req.params.name],
Number(b.wohnflaeche), grund, heizung, req.params.name],
)
// Haus umbenannt → Bezüge in Wohnungen und Zählern mitziehen
if (neuName !== alt.name) {
@@ -1168,12 +1181,14 @@ app.post('/api/units', auth, adminOnly, async (req, res, next) => {
if (!Number.isFinite(coldRent) || coldRent < 0) return res.status(400).json({ fehler: 'Kaltmiete ungültig.' })
if (!Number.isFinite(warmRent) || warmRent < 0) return res.status(400).json({ fehler: 'Warmmiete ungültig.' })
const status = u.status === 'frei' ? 'frei' : 'vermietet'
if (!heizungsartValid(String(u.heizungsart || '').trim())) return res.status(400).json({ fehler: 'Ungültige Heizungsart.' })
const heizung = String(u.heizungsart || '').trim()
const id = `u-${Date.now()}`
const { rows } = await pool.query(
`INSERT INTO units (id, building, number, floor, size, cold_rent, warm_rent, tenant, status)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9) RETURNING *`,
`INSERT INTO units (id, building, number, floor, size, cold_rent, warm_rent, tenant, status, heizungsart)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING *`,
[id, building, String(u.number).trim(), floor, size, coldRent, warmRent,
String(u.tenant || '').trim() || null, status],
String(u.tenant || '').trim() || null, status, heizung],
)
res.json({ unit: mapUnit(rows[0]) })
} catch (e) { next(e) }
@@ -1198,11 +1213,13 @@ app.patch('/api/units/:id', auth, adminOnly, async (req, res, next) => {
if (!Number.isFinite(coldRent) || coldRent < 0) return res.status(400).json({ fehler: 'Kaltmiete ungültig.' })
if (!Number.isFinite(warmRent) || warmRent < 0) return res.status(400).json({ fehler: 'Warmmiete ungültig.' })
const status = u.status === 'frei' ? 'frei' : 'vermietet'
if (!heizungsartValid(String(u.heizungsart || '').trim())) return res.status(400).json({ fehler: 'Ungültige Heizungsart.' })
const heizung = String(u.heizungsart || '').trim()
const { rows: neu } = await pool.query(
`UPDATE units SET building = $1, number = $2, floor = $3, size = $4, cold_rent = $5,
warm_rent = $6, tenant = $7, status = $8 WHERE id = $9 RETURNING *`,
warm_rent = $6, tenant = $7, status = $8, heizungsart = $9 WHERE id = $10 RETURNING *`,
[building, String(u.number).trim(), floor, size, coldRent, warmRent,
String(u.tenant || '').trim() || null, status, req.params.id],
String(u.tenant || '').trim() || null, status, heizung, req.params.id],
)
res.json({ unit: mapUnit(neu[0]) })
} catch (e) { next(e) }