v1.33: Eintraege in allen Listen loeschbar
This commit is contained in:
@@ -553,6 +553,19 @@ app.patch('/api/tickets/:id', auth, adminOnly, async (req, res, next) => {
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Schadensmeldung löschen inkl. verknüpfter Unterhaltung und Nachrichten (v1.33)
|
||||
app.delete('/api/tickets/:id', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const { rows } = await pool.query('DELETE FROM tickets WHERE id = $1 RETURNING id', [req.params.id])
|
||||
if (!rows.length) return res.status(404).json({ fehler: 'Schadensmeldung nicht gefunden.' })
|
||||
const { rows: threads } = await pool.query('DELETE FROM threads WHERE ticket_id = $1 RETURNING id', [req.params.id])
|
||||
for (const th of threads) {
|
||||
await pool.query('DELETE FROM messages WHERE thread_id = $1', [th.id])
|
||||
}
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// ── Zählerstände (Strom / Wasser / Gas) ─────────────────────
|
||||
const ZAEHLER_ARTEN = ['Strom', 'Wasser', 'Gas', 'Wärme', 'Sonstiges']
|
||||
// Gängige Heizungsformen (v1.29) – Dropdown in Haus-/Wohnungsformular
|
||||
@@ -707,6 +720,16 @@ app.delete('/api/meters/:id/readings/:rid', auth, adminOnly, async (req, res, ne
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Zähler komplett löschen inkl. Verlauf (v1.33)
|
||||
app.delete('/api/meters/:id', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const { rows } = await pool.query('DELETE FROM meters WHERE id = $1 RETURNING id', [req.params.id])
|
||||
if (!rows.length) return res.status(404).json({ fehler: 'Zähler nicht gefunden.' })
|
||||
await pool.query('DELETE FROM meter_readings WHERE meter_id = $1', [req.params.id])
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
// ── Dokumente (Verwaltung) ───────────────────────────────────
|
||||
const DOKUMENT_KATEGORIEN = ['Mietvertrag', 'Nebenkostenabrechnung', 'Hausordnung', 'Protokoll', 'Sonstiges']
|
||||
|
||||
@@ -1135,6 +1158,15 @@ app.post('/api/belege/:id/analysieren', auth, adminOnly, async (req, res, next)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Beleg löschen (v1.33)
|
||||
app.delete('/api/belege/:id', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const { rows } = await pool.query('DELETE FROM belege WHERE id = $1 RETURNING id', [req.params.id])
|
||||
if (!rows.length) return res.status(404).json({ fehler: 'Beleg nicht gefunden.' })
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// ── Bestand: Häuser & Wohnungen (Verwaltung) ────────────────
|
||||
/** Name/Anzeigeadresse werden aus den Einzelfeldern zusammengesetzt (v1.26). */
|
||||
function hausNameAus(b) {
|
||||
@@ -1237,6 +1269,28 @@ app.patch('/api/buildings/:name', auth, adminOnly, async (req, res, next) => {
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Haus löschen – nur wenn keine Wohnungen, Zähler oder Kosten mehr darauf verweisen (v1.33)
|
||||
app.delete('/api/buildings/:name', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const { rows } = await pool.query('SELECT 1 FROM buildings WHERE name = $1', [req.params.name])
|
||||
if (!rows.length) return res.status(404).json({ fehler: 'Haus nicht gefunden.' })
|
||||
const blocker = []
|
||||
for (const [tabelle, spalte, label] of [
|
||||
['units', 'building', 'Wohnungen'],
|
||||
['meters', 'building', 'Zähler'],
|
||||
['costs', 'building', 'Kostenpositionen'],
|
||||
]) {
|
||||
const { rows: r } = await pool.query(`SELECT COUNT(*)::int AS n FROM ${tabelle} WHERE ${spalte} = $1`, [req.params.name])
|
||||
if (r[0].n > 0) blocker.push(`${r[0].n} ${label}`)
|
||||
}
|
||||
if (blocker.length) {
|
||||
return res.status(400).json({ fehler: `Haus kann nicht gelöscht werden – bitte zuerst entfernen: ${blocker.join(', ')}.` })
|
||||
}
|
||||
await pool.query('DELETE FROM buildings WHERE name = $1', [req.params.name])
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
app.post('/api/units', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const u = req.body || {}
|
||||
@@ -1297,6 +1351,29 @@ app.patch('/api/units/:id', auth, adminOnly, async (req, res, next) => {
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Wohnung löschen – nur wenn keine Zähler, Dokumente, Kosten oder Mieter sie referenzieren (v1.33)
|
||||
app.delete('/api/units/:id', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const { rows } = await pool.query('SELECT 1 FROM units WHERE id = $1', [req.params.id])
|
||||
if (!rows.length) return res.status(404).json({ fehler: 'Wohnung nicht gefunden.' })
|
||||
const blocker = []
|
||||
for (const [tabelle, spalte, label] of [
|
||||
['meters', 'unit_id', 'Zähler'],
|
||||
['documents', 'unit_id', 'Dokumente'],
|
||||
['costs', 'unit_id', 'Kostenpositionen'],
|
||||
['users', 'einheit_id', 'zugeordnete Benutzer'],
|
||||
]) {
|
||||
const { rows: r } = await pool.query(`SELECT COUNT(*)::int AS n FROM ${tabelle} WHERE ${spalte} = $1`, [req.params.id])
|
||||
if (r[0].n > 0) blocker.push(`${r[0].n} ${label}`)
|
||||
}
|
||||
if (blocker.length) {
|
||||
return res.status(400).json({ fehler: `Wohnung kann nicht gelöscht werden – bitte zuerst entfernen/zuordnen: ${blocker.join(', ')}.` })
|
||||
}
|
||||
await pool.query('DELETE FROM units WHERE id = $1', [req.params.id])
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// ── Nachrichten (Threads) ────────────────────────────────────
|
||||
app.post('/api/threads', auth, async (req, res, next) => {
|
||||
try {
|
||||
@@ -1458,6 +1535,26 @@ app.patch('/api/users/:id', auth, adminOnly, async (req, res, next) => {
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Benutzer löschen: eigenes Konto und den letzten aktiven Verwalter schützen (v1.33)
|
||||
app.delete('/api/users/:id', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
if (req.params.id === req.user.id)
|
||||
return res.status(400).json({ fehler: 'Das eigene Konto kann nicht gelöscht werden.' })
|
||||
const { rows } = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id])
|
||||
if (!rows.length) return res.status(404).json({ fehler: 'Benutzer nicht gefunden.' })
|
||||
if (rows[0].rolle === 'verwaltung' && rows[0].aktiv) {
|
||||
const { rows: rest } = await pool.query(
|
||||
`SELECT COUNT(*)::int AS n FROM users WHERE rolle = 'verwaltung' AND aktiv AND id <> $1`, [req.params.id],
|
||||
)
|
||||
if (rest[0].n === 0)
|
||||
return res.status(400).json({ fehler: 'Der letzte aktive Verwaltungsbenutzer kann nicht gelöscht werden.' })
|
||||
}
|
||||
await pool.query('DELETE FROM sessions WHERE user_id = $1', [req.params.id])
|
||||
await pool.query('DELETE FROM users WHERE id = $1', [req.params.id])
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Direkte E-Mail der Verwaltung an einen Mieter (immer mit CC an die
|
||||
// Verwaltung, damit die Kommunikation dort vollständig nachvollziehbar ist)
|
||||
app.post('/api/users/:id/email', auth, adminOnly, async (req, res, next) => {
|
||||
|
||||
Reference in New Issue
Block a user