264 lines
18 KiB
JavaScript
264 lines
18 KiB
JavaScript
#!/usr/bin/env node
|
||
// Testdaten für vereins.haus – Staging
|
||
// Aufruf: PB_URL=http://localhost:8090 PB_EMAIL=admin@test.de PB_PASSWORD=Test123456! node scripts/seed.js
|
||
|
||
const PB_URL = process.env.PB_URL || 'http://localhost:8090';
|
||
const PB_EMAIL = process.env.PB_EMAIL || '';
|
||
const PB_PWD = process.env.PB_PASSWORD || '';
|
||
|
||
if (!PB_EMAIL || !PB_PWD) {
|
||
console.error('Fehler: PB_EMAIL und PB_PASSWORD setzen.');
|
||
process.exit(1);
|
||
}
|
||
|
||
let token = '';
|
||
|
||
async function pb(method, path, body) {
|
||
const res = await fetch(`${PB_URL}/api/${path}`, {
|
||
method,
|
||
headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: token } : {}) },
|
||
body: body ? JSON.stringify(body) : undefined,
|
||
});
|
||
const json = await res.json().catch(() => ({}));
|
||
if (!res.ok) throw new Error(`${method} /${path} → ${res.status}: ${JSON.stringify(json)}`);
|
||
return json;
|
||
}
|
||
|
||
async function find(collection, filter) {
|
||
const r = await pb('GET', `collections/${collection}/records?filter=${encodeURIComponent(filter)}&perPage=1`);
|
||
return r.items?.[0] ?? null;
|
||
}
|
||
|
||
async function create(collection, data) {
|
||
return pb('POST', `collections/${collection}/records`, data);
|
||
}
|
||
|
||
async function update(collection, id, data) {
|
||
return pb('PATCH', `collections/${collection}/records/${id}`, data);
|
||
}
|
||
|
||
// ── 1. Admin-Login ──────────────────────────────────────────────────────────
|
||
console.log('→ Admin-Login…');
|
||
const auth = await pb('POST', 'collections/_superusers/auth-with-password', {
|
||
identity: PB_EMAIL, password: PB_PWD,
|
||
});
|
||
token = auth.token;
|
||
console.log(' ✓ Eingeloggt als', PB_EMAIL);
|
||
|
||
// ── 2. Verein ────────────────────────────────────────────────────────────────
|
||
console.log('→ Verein anlegen…');
|
||
let verein = await find('vereine', 'name = "TSV Musterstadt 1983 e.V."');
|
||
if (!verein) {
|
||
verein = await create('vereine', {
|
||
name: 'TSV Musterstadt 1983 e.V.',
|
||
adresse: 'Vereinsstraße 12',
|
||
plz: '80333',
|
||
ort: 'Musterstadt',
|
||
bundesland: 'BY',
|
||
plan: 'starter',
|
||
dosb_mitglied: true,
|
||
email: 'info@tsv-musterstadt.de',
|
||
telefon: '089 123456',
|
||
website: 'https://tsv-musterstadt.de',
|
||
glaeubigerid: 'DE98ZZZ09999999999',
|
||
iban: 'DE89370400440532013000',
|
||
bic: 'COBADEFFXXX',
|
||
});
|
||
}
|
||
console.log(' ✓ Verein:', verein.name, `(${verein.id})`);
|
||
|
||
// ── 3. Admin-User ────────────────────────────────────────────────────────────
|
||
console.log('→ Admin-User…');
|
||
let adminUser = await find('users', 'email = "vorstand@tsv-musterstadt.de"');
|
||
if (!adminUser) {
|
||
adminUser = await create('users', {
|
||
email: 'vorstand@tsv-musterstadt.de',
|
||
password: 'Test123456!', passwordConfirm: 'Test123456!',
|
||
name: 'Max Mustermann', verein_id: verein.id, rolle: null, // null = admin
|
||
emailVisibility: true,
|
||
});
|
||
}
|
||
console.log(' ✓ Admin:', adminUser.email);
|
||
|
||
// ── 4. Gruppen ───────────────────────────────────────────────────────────────
|
||
console.log('→ Gruppen…');
|
||
const gruppenDef = ['Vorstand', 'Aktive Mitglieder', 'Jugend U15', 'Senioren'];
|
||
const gruppen = {};
|
||
for (const name of gruppenDef) {
|
||
let g = await find('gruppen', `name = "${name}" && verein_id = "${verein.id}"`);
|
||
if (!g) g = await create('gruppen', { verein_id: verein.id, name });
|
||
gruppen[name] = g.id;
|
||
}
|
||
console.log(' ✓', Object.keys(gruppen).join(', '));
|
||
|
||
// ── 5. Mitglieder ────────────────────────────────────────────────────────────
|
||
console.log('→ Mitglieder (18)…');
|
||
const mitgliederDef = [
|
||
// Vorstand
|
||
{ vorname: 'Max', nachname: 'Mustermann', email: 'max.mustermann@example.de', telefon: '0170 1111111', geburtsdatum: '1975-03-15', eintrittsdatum: '2000-01-01', strasse: 'Hauptstraße 1', plz: '80333', ort: 'Musterstadt', iban: 'DE89370400440532013000', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Vorstand', mandatsreferenz: 'MANDAT-001', mandatsdatum: '2020-01-10' },
|
||
{ vorname: 'Sabine', nachname: 'Richter', email: 'sabine.richter@example.de', telefon: '0171 2222222', geburtsdatum: '1980-07-22', eintrittsdatum: '2005-03-01', strasse: 'Birkenweg 5', plz: '80334', ort: 'Musterstadt', iban: 'DE27100777770209299700', bic: 'SSKMDEMMXXX', status: 'aktiv', gruppe: 'Vorstand', mandatsreferenz: 'MANDAT-002', mandatsdatum: '2020-01-10' },
|
||
// Aktive mit IBAN
|
||
{ vorname: 'Anna', nachname: 'Schmidt', email: 'anna.schmidt@example.de', telefon: '0172 3333333', geburtsdatum: '1990-11-05', eintrittsdatum: '2015-09-01', strasse: 'Rosenstraße 8', plz: '80333', ort: 'Musterstadt', iban: 'DE61500105179767440929', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-003', mandatsdatum: '2021-04-15' },
|
||
{ vorname: 'Peter', nachname: 'Wagner', email: 'peter.wagner@example.de', telefon: '0173 4444444', geburtsdatum: '1985-02-28', eintrittsdatum: '2010-01-15', strasse: 'Gartenweg 12', plz: '80334', ort: 'Musterstadt', iban: 'DE24500105171911148770', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-004', mandatsdatum: '2021-04-15' },
|
||
{ vorname: 'Thomas', nachname: 'Fischer', email: 'thomas.fischer@example.de', telefon: '0174 5555555', geburtsdatum: '1978-06-10', eintrittsdatum: '2008-04-01', strasse: 'Lindenallee 3', plz: '80335', ort: 'Musterstadt', iban: 'DE12500105176228935005', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-005', mandatsdatum: '2021-04-15' },
|
||
{ vorname: 'Claudia', nachname: 'König', email: 'claudia.koenig@example.de', telefon: '0175 6666666', geburtsdatum: '1992-09-17', eintrittsdatum: '2018-06-01', strasse: 'Feldweg 7', plz: '80333', ort: 'Musterstadt', iban: 'DE67200501001234567890', bic: 'HASPDEHHXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-006', mandatsdatum: '2022-01-20' },
|
||
{ vorname: 'Michael', nachname: 'Koch', email: 'michael.koch@example.de', telefon: '0176 7777777', geburtsdatum: '1983-04-03', eintrittsdatum: '2012-02-01', strasse: 'Bergstraße 22', plz: '80336', ort: 'Musterstadt', iban: 'DE86200400600526015800', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-007', mandatsdatum: '2021-04-15' },
|
||
{ vorname: 'Lisa', nachname: 'Zimmermann', email: 'lisa.zimm@example.de', telefon: '0177 8888888', geburtsdatum: '1995-12-25', eintrittsdatum: '2020-10-01', strasse: 'Blumenstraße 4', plz: '80333', ort: 'Musterstadt', iban: 'DE21700519950021267002', bic: 'BYLADEM1AUG', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-008', mandatsdatum: '2022-03-05' },
|
||
{ vorname: 'Petra', nachname: 'Schreiber', email: 'petra.schreiber@example.de', telefon: '0178 9999999', geburtsdatum: '1988-08-14', eintrittsdatum: '2014-07-01', strasse: 'Weinbergweg 9', plz: '80334', ort: 'Musterstadt', iban: 'DE36200400600532013004', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: 'MANDAT-009', mandatsdatum: '2021-04-15' },
|
||
// Aktive ohne IBAN
|
||
{ vorname: 'Maria', nachname: 'Becker', email: 'maria.becker@example.de', telefon: '0179 1010101', geburtsdatum: '1987-01-30', eintrittsdatum: '2016-03-01', strasse: 'Kirchgasse 6', plz: '80335', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null },
|
||
{ vorname: 'Julia', nachname: 'Müller', email: 'julia.mueller@example.de', telefon: '0151 1111222', geburtsdatum: '1993-05-19', eintrittsdatum: '2019-01-01', strasse: 'Parkstraße 11', plz: '80333', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null },
|
||
{ vorname: 'Markus', nachname: 'Schäfer', email: 'markus.schaefer@example.de', telefon: '0152 3333444', geburtsdatum: '1991-07-07', eintrittsdatum: '2021-05-01', strasse: 'Schulweg 2', plz: '80336', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null },
|
||
// Jugend
|
||
{ vorname: 'Lena', nachname: 'Bauer', email: null, telefon: '0160 1234567', geburtsdatum: '2012-03-22', eintrittsdatum: '2023-09-01', strasse: 'Schulstraße 14', plz: '80333', ort: 'Musterstadt', iban: 'DE47200400600128491600', bic: 'COBADEFFXXX', status: 'aktiv', gruppe: 'Jugend U15', mandatsreferenz: 'MANDAT-013', mandatsdatum: '2023-09-01' },
|
||
{ vorname: 'Kevin', nachname: 'Hoffmann', email: null, telefon: '0160 7654321', geburtsdatum: '2013-11-08', eintrittsdatum: '2023-09-01', strasse: 'Waldweg 3', plz: '80334', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Jugend U15', mandatsreferenz: null, mandatsdatum: null },
|
||
{ vorname: 'Emma', nachname: 'Klein', email: null, telefon: '0160 1122334', geburtsdatum: '2011-06-15', eintrittsdatum: '2022-09-01', strasse: 'Seeweg 7', plz: '80335', ort: 'Musterstadt', iban: null, bic: null, status: 'aktiv', gruppe: 'Jugend U15', mandatsreferenz: null, mandatsdatum: null },
|
||
// Senioren
|
||
{ vorname: 'Gertrude', nachname: 'Neumann', email: 'g.neumann@example.de', telefon: '089 9876543', geburtsdatum: '1948-04-12', eintrittsdatum: '1990-01-01', strasse: 'Ahornweg 1', plz: '80333', ort: 'Musterstadt', iban: 'DE43500105176118506698', bic: 'BELADEBEXXX', status: 'aktiv', gruppe: 'Senioren', mandatsreferenz: 'MANDAT-016', mandatsdatum: '2019-06-01' },
|
||
// Passiv / Ausgetreten
|
||
{ vorname: 'Hans', nachname: 'Schneider', email: 'hans.schneider@example.de', telefon: '0173 0000001', geburtsdatum: '1965-10-20', eintrittsdatum: '1995-04-01', strasse: 'Bergblick 5', plz: '80334', ort: 'Musterstadt', iban: null, bic: null, status: 'passiv', gruppe: 'Senioren', mandatsreferenz: null, mandatsdatum: null },
|
||
{ vorname: 'Horst', nachname: 'Braun', email: 'horst.braun@example.de', telefon: null, geburtsdatum: '1960-02-14', eintrittsdatum: '1988-01-01', strasse: 'Alte Gasse 3', plz: '80336', ort: 'Musterstadt', iban: null, bic: null, status: 'ausgetreten', gruppe: 'Aktive Mitglieder', mandatsreferenz: null, mandatsdatum: null, austrittsdatum: '2024-12-31' },
|
||
];
|
||
|
||
let mitgliederIds = [];
|
||
for (const m of mitgliederDef) {
|
||
let rec = await find('mitglieder', `vorname = "${m.vorname}" && nachname = "${m.nachname}" && verein_id = "${verein.id}"`);
|
||
if (!rec) {
|
||
rec = await create('mitglieder', {
|
||
verein_id: verein.id,
|
||
vorname: m.vorname,
|
||
nachname: m.nachname,
|
||
email: m.email || null,
|
||
telefon: m.telefon || null,
|
||
geburtsdatum: m.geburtsdatum,
|
||
eintrittsdatum: m.eintrittsdatum,
|
||
austrittsdatum: m.austrittsdatum || null,
|
||
strasse: m.strasse,
|
||
plz: m.plz,
|
||
ort: m.ort,
|
||
iban: m.iban || null,
|
||
bic: m.bic || null,
|
||
mandatsreferenz:m.mandatsreferenz || null,
|
||
mandatsdatum: m.mandatsdatum || null,
|
||
status: m.status,
|
||
gruppe_ids: [gruppen[m.gruppe]].filter(Boolean),
|
||
});
|
||
}
|
||
mitgliederIds.push(rec.id);
|
||
}
|
||
console.log(` ✓ ${mitgliederIds.length} Mitglieder`);
|
||
|
||
// ── 6. Veranstaltungsorte ────────────────────────────────────────────────────
|
||
console.log('→ Veranstaltungsorte…');
|
||
const orteDef = [
|
||
{ name: 'Turnhalle Grundschule Muster', adresse: 'Schulstraße 1, 80333 Musterstadt', typ: 'halle', aktiv: true },
|
||
{ name: 'Vereinsheim TSV', adresse: 'Vereinsstraße 12, 80333 Musterstadt', typ: 'gebaeude', aktiv: true },
|
||
{ name: 'Sportplatz West', adresse: 'Weststraße 99, 80335 Musterstadt', typ: 'platz', aktiv: true },
|
||
];
|
||
const orte = {};
|
||
for (const o of orteDef) {
|
||
let rec = await find('veranstaltungsorte', `name = "${o.name}" && verein_id = "${verein.id}"`);
|
||
if (!rec) rec = await create('veranstaltungsorte', { verein_id: verein.id, ...o });
|
||
orte[o.name] = rec.id;
|
||
}
|
||
// Ausfall: Turnhalle gesperrt wegen Schulveranstaltung
|
||
const turnhalleId = orte['Turnhalle Grundschule Muster'];
|
||
const existingAusfall = await find('ort_ausfaelle', `ort_id = "${turnhalleId}"`);
|
||
if (!existingAusfall) {
|
||
const nextWeek = new Date(); nextWeek.setDate(nextWeek.getDate() + 7);
|
||
const nextWeek2 = new Date(); nextWeek2.setDate(nextWeek2.getDate() + 9);
|
||
await create('ort_ausfaelle', {
|
||
ort_id: turnhalleId,
|
||
von: nextWeek.toISOString().slice(0,10),
|
||
bis: nextWeek2.toISOString().slice(0,10),
|
||
grund: 'Schulveranstaltung – Halle nicht verfügbar',
|
||
});
|
||
}
|
||
console.log(' ✓ Orte:', Object.keys(orte).join(', '));
|
||
|
||
// ── 7. Beitragsarten ─────────────────────────────────────────────────────────
|
||
console.log('→ Beitragsarten…');
|
||
const beitraegeDef = [
|
||
{ name: 'Jahresbeitrag Erwachsene', betrag: 48, rhythmus: 'jaehrlich', beschreibung: 'Für Mitglieder ab 18 Jahren' },
|
||
{ name: 'Jahresbeitrag Jugend', betrag: 24, rhythmus: 'jaehrlich', beschreibung: 'Für Mitglieder unter 18 Jahren' },
|
||
{ name: 'Aufnahmegebühr', betrag: 20, rhythmus: 'einmalig', beschreibung: 'Einmalig bei Vereinseintritt' },
|
||
{ name: 'Monatsbeitrag Fitness', betrag: 15, rhythmus: 'monatlich', beschreibung: 'Für die Fitnessgruppe (optional)' },
|
||
];
|
||
for (const b of beitraegeDef) {
|
||
const ex = await find('beitraege', `name = "${b.name}" && verein_id = "${verein.id}"`);
|
||
if (!ex) await create('beitraege', { verein_id: verein.id, ...b });
|
||
}
|
||
console.log(' ✓', beitraegeDef.map(b => b.name).join(', '));
|
||
|
||
// ── 8. Termine ───────────────────────────────────────────────────────────────
|
||
console.log('→ Termine…');
|
||
|
||
const d = (offsetDays, h = 0, m = 0) => {
|
||
const dt = new Date();
|
||
dt.setDate(dt.getDate() + offsetDays);
|
||
dt.setHours(h, m, 0, 0);
|
||
return dt.toISOString();
|
||
};
|
||
|
||
const termineDef = [
|
||
// Vergangen
|
||
{ titel: 'Jahreshauptversammlung 2026', beginn: d(-60, 19, 0), ende: d(-60, 21, 0), ort_id: orte['Vereinsheim TSV'], gruppe_ids: Object.values(gruppen), beschreibung: 'Jahresabrechnung, Vorstandswahl, Planung Sommerfest' },
|
||
{ titel: 'Trainingssession', beginn: d(-14, 18, 0), ende: d(-14, 20, 0), ort_id: orte['Turnhalle Grundschule Muster'], gruppe_ids: [gruppen['Aktive Mitglieder']] },
|
||
{ titel: 'Jugendtraining', beginn: d(-7, 16, 0), ende: d(-7, 17, 30), ort_id: orte['Turnhalle Grundschule Muster'], gruppe_ids: [gruppen['Jugend U15']] },
|
||
// Upcoming
|
||
{ titel: 'Vorstandssitzung', beginn: d(3, 19, 30), ende: d(3, 21, 0), ort_id: orte['Vereinsheim TSV'], gruppe_ids: [gruppen['Vorstand']], beschreibung: 'Vorbereitung Sommerfest, Kassenstand Q1' },
|
||
{ titel: 'Sommerfest TSV', beginn: d(32, 14, 0), ende: d(32, 22, 0), ort_id: orte['Sportplatz West'], gruppe_ids: Object.values(gruppen), beschreibung: 'Für Mitglieder und Familien – Grillen, Spiele, Live-Musik' },
|
||
{ titel: 'Auswärtsspiel Musterliga', beginn: d(18, 11, 0), ende: d(18, 13, 0), ort_id: null, gruppe_ids: [gruppen['Aktive Mitglieder']], ort: 'FC Gegner – Stadionstraße 1', beschreibung: 'Hinfahrt 10:00 Uhr am Vereinsheim' },
|
||
];
|
||
|
||
// Wöchentliches Training als Serie
|
||
const serie_id = 'seed-serie-dienstag-2026';
|
||
const trainingsTermine = [];
|
||
for (let i = 1; i <= 8; i++) {
|
||
// Nächster Dienstag + i Wochen
|
||
const dt = new Date();
|
||
const daysToTue = (2 - dt.getDay() + 7) % 7 || 7;
|
||
dt.setDate(dt.getDate() + daysToTue + (i - 1) * 7);
|
||
dt.setHours(18, 30, 0, 0);
|
||
trainingsTermine.push({
|
||
titel: 'Dienstags-Training',
|
||
beginn: dt.toISOString(),
|
||
ende: new Date(dt.getTime() + 90 * 60 * 1000).toISOString(),
|
||
ort_id: orte['Turnhalle Grundschule Muster'],
|
||
gruppe_ids:[gruppen['Aktive Mitglieder']],
|
||
rrule: 'FREQ=WEEKLY;BYDAY=TU',
|
||
serie_id,
|
||
});
|
||
}
|
||
|
||
const allTermine = [...termineDef, ...trainingsTermine];
|
||
let termineAngelegt = 0;
|
||
for (const t of allTermine) {
|
||
const ex = await find('termine', `titel = "${t.titel}" && verein_id = "${verein.id}" && beginn = "${t.beginn}"`);
|
||
if (!ex) {
|
||
await create('termine', { verein_id: verein.id, verfuegbarkeit: 'offen', ...t });
|
||
termineAngelegt++;
|
||
}
|
||
}
|
||
console.log(` ✓ ${termineAngelegt} Termine (inkl. ${trainingsTermine.length} in Dienstags-Serie)`);
|
||
|
||
// ── 9. Nachricht ─────────────────────────────────────────────────────────────
|
||
console.log('→ Beispiel-Nachricht…');
|
||
const exMsg = await find('nachrichten', `verein_id = "${verein.id}"`);
|
||
if (!exMsg) {
|
||
await create('nachrichten', {
|
||
verein_id: verein.id,
|
||
autor_id: adminUser.id,
|
||
betreff: 'Willkommen bei vereins.haus!',
|
||
text: 'Liebe Mitglieder,\n\nwir haben unsere Vereinsverwaltung auf vereins.haus umgestellt. Hier findet ihr alle Termine, Nachrichten und Informationen rund um unseren Verein.\n\nBei Fragen wendet euch an den Vorstand.\n\nEuer Vorstand\nTSV Musterstadt 1983 e.V.',
|
||
gruppe_ids: Object.values(gruppen),
|
||
gesendet_am: new Date().toISOString(),
|
||
});
|
||
}
|
||
|
||
// ── Zusammenfassung ──────────────────────────────────────────────────────────
|
||
console.log('\n✓ Seed abgeschlossen!\n');
|
||
console.log(' Verein: ', verein.name);
|
||
console.log(' Login: vorstand@tsv-musterstadt.de / Test123456!');
|
||
console.log(' Mitglieder:', mitgliederIds.length);
|
||
console.log(' PocketBase:', PB_URL);
|