174 lines
6.8 KiB
JavaScript
174 lines
6.8 KiB
JavaScript
/** Idempotenter Seed: füllt die Datenbank beim ersten Start mit den
|
||
* Demo-Daten (50 Wohnungen, 552 Buchungen, Tickets, Dokumente, Threads,
|
||
* Ankündigungen, Demo-Benutzer, Registrierungscode). Läuft nur, wenn die
|
||
* users-Tabelle leer ist – bestehende Daten werden nie überschrieben.
|
||
*/
|
||
|
||
const { pool } = require('./db')
|
||
const { users, registrationCodes, tickets, documents, threads, announcements, meters, buildings, kosten } = require('./seed-data')
|
||
const generated = require('../seed/generated.json')
|
||
|
||
async function seed() {
|
||
const { rows } = await pool.query('SELECT COUNT(*)::int AS n FROM users')
|
||
if (rows[0].n > 0) return false
|
||
|
||
const client = await pool.connect()
|
||
try {
|
||
await client.query('BEGIN')
|
||
|
||
for (const u of generated.units) {
|
||
await client.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)`,
|
||
[u.id, u.building, u.number, u.floor, u.size, u.coldRent, u.warmRent, u.tenant, u.status],
|
||
)
|
||
}
|
||
|
||
for (const t of generated.transactions) {
|
||
await client.query(
|
||
`INSERT INTO transactions (id, unit_id, tenant, month, year, amount, type, status, paid_at)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
|
||
[t.id, t.unitId, t.tenant, t.month, t.year, t.amount, t.type, t.status, t.paidAt ?? null],
|
||
)
|
||
}
|
||
|
||
// Mieter-Namen in Tickets/Threads aus der units-Tabelle nachziehen
|
||
const { rows: unitRows } = await client.query('SELECT id, tenant FROM units')
|
||
const mieterVon = Object.fromEntries(unitRows.map((u) => [u.id, u.tenant]))
|
||
|
||
for (const t of tickets) {
|
||
await client.query(
|
||
`INSERT INTO tickets (id, unit_id, tenant, title, description, category, priority, status, created_at)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
|
||
[t.id, t.unit_id, mieterVon[t.unit_id] ?? t.tenant, t.title, t.description, t.category, t.priority, t.status, t.created_at],
|
||
)
|
||
}
|
||
|
||
for (const d of documents) {
|
||
await client.query(
|
||
`INSERT INTO documents (id, unit_id, title, category, size, date) VALUES ($1,$2,$3,$4,$5,$6)`,
|
||
[d.id, d.unit_id, d.title, d.category, d.size, d.date],
|
||
)
|
||
}
|
||
|
||
for (const th of threads) {
|
||
await client.query(
|
||
`INSERT INTO threads (id, unit_id, tenant, subject, unread_by_admin, unread_by_tenant)
|
||
VALUES ($1,$2,$3,$4,$5,$6)`,
|
||
[th.id, th.unit_id, mieterVon[th.unit_id] ?? th.tenant, th.subject, th.unread_by_admin, th.unread_by_tenant],
|
||
)
|
||
for (const m of th.messages) {
|
||
await client.query(
|
||
`INSERT INTO messages (id, thread_id, from_role, author, text, at) VALUES ($1,$2,$3,$4,$5,$6)`,
|
||
[m.id, th.id, m.from_role, m.author, m.text, m.at],
|
||
)
|
||
}
|
||
}
|
||
|
||
for (const a of announcements) {
|
||
await client.query(
|
||
`INSERT INTO announcements (id, title, text, date, tag) VALUES ($1,$2,$3,$4,$5)`,
|
||
[a.id, a.title, a.text, a.date, a.tag],
|
||
)
|
||
}
|
||
|
||
for (const b of users) {
|
||
await client.query(
|
||
`INSERT INTO users (id, benutzername, name, passwort_hash, rolle, email, einheit_id, aktiv, erstellt_am, letzter_login)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`,
|
||
[b.id, b.benutzername, b.name, b.passwort_hash, b.rolle, b.email, b.einheit_id, b.aktiv, b.erstellt_am, b.letzter_login ?? null],
|
||
)
|
||
}
|
||
|
||
for (const c of registrationCodes) {
|
||
await client.query(
|
||
`INSERT INTO registration_codes (code, einheit_id, erstellt_am, verwendet_am) VALUES ($1,$2,$3,$4)`,
|
||
[c.code, c.einheit_id, c.erstellt_am, c.verwendet_am],
|
||
)
|
||
}
|
||
|
||
for (const z of meters) {
|
||
await client.query(
|
||
`INSERT INTO meters (id, unit_id, building, nummer, art, stand, stand_am, geaendert_am)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
|
||
[z.id, z.unit_id, z.building, z.nummer, z.art, z.stand, z.stand_am, z.geaendert_am],
|
||
)
|
||
}
|
||
|
||
await client.query('COMMIT')
|
||
return true
|
||
} catch (e) {
|
||
await client.query('ROLLBACK')
|
||
throw e
|
||
} finally {
|
||
client.release()
|
||
}
|
||
}
|
||
|
||
module.exports = { seed }
|
||
|
||
/** Der Demo-Bestand (Einheit hs-7) muss existieren, sonst gehören die
|
||
* Demo-Nachseeds (Zähler/Häuser/Kosten) nicht in diese Datenbank – z. B. nach
|
||
* dem Live-Import (v1.21) darf nichts mehr nachgeseedet werden. */
|
||
async function demoBestandVorhanden() {
|
||
const { rows } = await pool.query(`SELECT 1 FROM units WHERE id = 'hs-7'`)
|
||
return rows.length > 0
|
||
}
|
||
|
||
/** Zähler-Demo-Daten: idempotent – läuft bei jedem Start und füllt die
|
||
* Zähler-Tabelle nur, wenn sie leer ist (auch nachträglich auf bestehenden
|
||
* Datenbanken, z. B. nach dem v1.9-Update). */
|
||
async function seedeZaehler() {
|
||
const { rows } = await pool.query('SELECT COUNT(*)::int AS n FROM meters')
|
||
if (rows[0].n > 0 || !(await demoBestandVorhanden())) return false
|
||
for (const z of meters) {
|
||
await pool.query(
|
||
`INSERT INTO meters (id, unit_id, building, nummer, art, stand, stand_am, geaendert_am)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
|
||
[z.id, z.unit_id, z.building, z.nummer, z.art, z.stand, z.stand_am, z.geaendert_am],
|
||
)
|
||
}
|
||
return true
|
||
}
|
||
|
||
module.exports = { seed, seedeZaehler }
|
||
|
||
/** Haus-Demo-Daten: idempotent – füllt die buildings-Tabelle nur, wenn sie
|
||
* leer ist (auch nachträglich auf bestehenden Datenbanken, z. B. v1.10). */
|
||
async function seedeHaeuser() {
|
||
const { rows } = await pool.query('SELECT COUNT(*)::int AS n FROM buildings')
|
||
if (rows[0].n > 0 || !(await demoBestandVorhanden())) return false
|
||
for (const h of buildings) {
|
||
const teile = String(h.adresse).trim().split(/\s+/)
|
||
const hausnummer = teile.length > 1 ? teile.pop() : ''
|
||
const strasse = teile.join(' ')
|
||
await pool.query(
|
||
`INSERT INTO buildings (name, adresse, strasse, hausnummer, plz, ort, lage,
|
||
baujahr, stockwerke, etagenhoehe, wohneinheiten, quadratmeter, wohnflaeche)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)`,
|
||
[h.name, h.adresse, strasse, hausnummer, '', '', '',
|
||
h.baujahr, h.stockwerke, h.etagenhoehe, h.wohneinheiten, h.quadratmeter, h.quadratmeter],
|
||
)
|
||
}
|
||
return true
|
||
}
|
||
|
||
module.exports = { seed, seedeZaehler, seedeHaeuser }
|
||
|
||
/** Kosten-Demo-Daten: idempotent – füllt die costs-Tabelle nur, wenn sie
|
||
* leer ist (auch nachträglich auf bestehenden Datenbanken, z. B. v1.14). */
|
||
async function seedeKosten() {
|
||
const { rows } = await pool.query('SELECT COUNT(*)::int AS n FROM costs')
|
||
if (rows[0].n > 0 || !(await demoBestandVorhanden())) return false
|
||
for (const k of kosten) {
|
||
await pool.query(
|
||
`INSERT INTO costs (id, building, unit_id, jahr, kostenart, betrag, umlegbar, notiz, erfasst_am)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
|
||
[k.id, k.building, k.unit_id, k.jahr, k.kostenart, k.betrag, k.umlegbar, k.notiz, k.erfasst_am],
|
||
)
|
||
}
|
||
return true
|
||
}
|
||
|
||
module.exports = { seed, seedeZaehler, seedeHaeuser, seedeKosten }
|