Phase 3: Einsatz- und Uebungserfassung
Schnellerfassung im Alarmfall, sechs Zeitstempel mit Ausrueckzeit, Anfahrt und Hilfsfrist, Staerke nach FwDV 3, Personalvorschlag mit Qualifikationsstand, Einsatzbericht als PDF, Jahresexport fuer die Gemeinde, Statistik und Uebungen mit Anwesenheitsliste. Dazu das Einsatzportal fuer Fremdwehren: ein signierter Link ohne Anmeldung, zeitlich und mengenbegrenzt, der Stichwort, Ort, Zeiten und Lagemeldungen zeigt und Meldungen mit Foto annimmt. Es sind die ersten oeffentlich erreichbaren Endpunkte in FLORIAN. Acht neue Tabellen, alle mit organisationId und Row Level Security. Ein freigegebener Einsatzbericht ist unveraenderlich; ein Datenbanktrigger setzt das durch. Koordinaten liegen doppelt — als Dezimalgrad und als PostGIS-Punkt, zusammengehalten von einem Trigger. 57 neue Tests (32 Unit, 14 gegen eine echte Datenbank, 11 E2E). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bc62a9c1e8
commit
91954c402b
+442
-1
@@ -7,7 +7,8 @@
|
||||
* Inventarnummern, 14 Qualifikationstypen und die Qualifikationen der
|
||||
* Mannschaft. Dazu ab Phase 2: 5 Wartungsvorlagen mit Checklisten, die daraus
|
||||
* fälligen Prüftermine, ein abgeschlossenes Protokoll samt Mangel und ein
|
||||
* Beispielrückruf.
|
||||
* Beispielrückruf. Ab Phase 3 dazu: acht Einsätze über das Jahr verteilt,
|
||||
* sechs Übungen und die Beteiligung der Mannschaft.
|
||||
*
|
||||
* Das Skript läuft über DIRECT_DATABASE_URL, also mit dem Eigentümer-Zugang,
|
||||
* der Row Level Security umgehen darf. Anders ginge es nicht: der Seed legt
|
||||
@@ -20,8 +21,12 @@
|
||||
*/
|
||||
import "dotenv/config";
|
||||
import {
|
||||
Alarmierungsart,
|
||||
AusweisTokenTyp,
|
||||
ChecklistenpunktTyp,
|
||||
EinsatzFunktion,
|
||||
EinsatzKategorie,
|
||||
EinsatzStatus,
|
||||
Dienstgrad,
|
||||
FahrzeugArt,
|
||||
GeraeteKategorie,
|
||||
@@ -38,6 +43,7 @@ import {
|
||||
RolleSchluessel,
|
||||
SignaturRolle,
|
||||
StandortArt,
|
||||
UebungsArt,
|
||||
} from "@prisma/client";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
@@ -1512,6 +1518,437 @@ async function rueckrufAnlegen(organisationId: string) {
|
||||
log("Rückruf: 1 Beispiel");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Phase 3 — Einsätze und Übungen
|
||||
//
|
||||
// Ein Jahr im Leben einer Wehr: Brände, technische Hilfeleistungen, ein
|
||||
// Fehlalarm einer Brandmeldeanlage und die Übungen dazwischen. Die Zahlen sind
|
||||
// so gewählt, dass die Statistik etwas zeigt — auch eine Hilfsfrist, die
|
||||
// gerade noch passt.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
interface EinsatzVorlage {
|
||||
/** Tage vor heute. */
|
||||
vorTagen: number;
|
||||
uhrzeit: [number, number];
|
||||
stichwort: string;
|
||||
kategorie: EinsatzKategorie;
|
||||
strasse: string;
|
||||
hausnummer?: string;
|
||||
objekt?: string;
|
||||
/** Minuten nach der Alarmierung: ausgerückt, eingetroffen, Ende, eingerückt. */
|
||||
ablauf: [number, number, number, number];
|
||||
fahrzeuge: number[];
|
||||
/** Wie viele Kräfte, und wer davon führt. */
|
||||
mannschaft: number;
|
||||
gerettet?: number;
|
||||
verletzt?: number;
|
||||
sachschaden?: number;
|
||||
beschreibung: string;
|
||||
bericht?: string;
|
||||
meldungen?: Array<{ nachMinuten: number; text: string; wichtig?: boolean }>;
|
||||
material?: Array<{ bezeichnung: string; menge: number; einheit: string }>;
|
||||
status?: EinsatzStatus;
|
||||
}
|
||||
|
||||
const EINSAETZE: EinsatzVorlage[] = [
|
||||
{
|
||||
vorTagen: 12,
|
||||
uhrzeit: [3, 42],
|
||||
stichwort: "B 3 Wohnhausbrand",
|
||||
kategorie: EinsatzKategorie.BRAND_GROSS,
|
||||
strasse: "Bahnhofstraße",
|
||||
hausnummer: "14",
|
||||
objekt: "Reihenhaus, zwei Vollgeschosse",
|
||||
ablauf: [5, 9, 143, 168],
|
||||
fahrzeuge: [0, 1],
|
||||
mannschaft: 14,
|
||||
gerettet: 2,
|
||||
verletzt: 1,
|
||||
sachschaden: 85000,
|
||||
beschreibung:
|
||||
"Bei Eintreffen Rauch aus dem Dachgeschoss, zwei Personen im Obergeschoss gemeldet.",
|
||||
bericht:
|
||||
"Menschenrettung über die Steckleiter durch den Angriffstrupp. Zwei Personen unverletzt gerettet, eine Person mit Rauchgasintoxikation an den Rettungsdienst übergeben.\n\nBrandbekämpfung mit einem C-Rohr unter Atemschutz vom Treppenraum aus. Nachlöscharbeiten und Kontrolle mit der Wärmebildkamera bis 06:05 Uhr. Einsatzstelle an die Polizei übergeben.",
|
||||
meldungen: [
|
||||
{ nachMinuten: 10, text: "Rauch aus Dachgeschoss, zwei Personen vermisst.", wichtig: true },
|
||||
{ nachMinuten: 22, text: "Beide Personen gerettet, Menschenrettung abgeschlossen.", wichtig: true },
|
||||
{ nachMinuten: 48, text: "Feuer unter Kontrolle." },
|
||||
{ nachMinuten: 140, text: "Nachlöscharbeiten beendet, Kontrolle mit Wärmebildkamera." },
|
||||
],
|
||||
material: [
|
||||
{ bezeichnung: "Atemluftflaschen 300 bar", menge: 6, einheit: "Stück" },
|
||||
{ bezeichnung: "Löschwasser", menge: 4500, einheit: "Liter" },
|
||||
],
|
||||
},
|
||||
{
|
||||
vorTagen: 26,
|
||||
uhrzeit: [14, 8],
|
||||
stichwort: "THL 1 Ölspur",
|
||||
kategorie: EinsatzKategorie.TECHNISCHE_HILFE_KLEIN,
|
||||
strasse: "Hauptstraße",
|
||||
ablauf: [6, 11, 55, 68],
|
||||
fahrzeuge: [1],
|
||||
mannschaft: 6,
|
||||
beschreibung: "Ölspur über etwa 300 Meter, ausgehend von einem Traktor.",
|
||||
bericht: "Ölspur mit Bindemittel abgestreut und aufgenommen. Straßenmeisterei verständigt.",
|
||||
material: [{ bezeichnung: "Ölbindemittel", menge: 40, einheit: "kg" }],
|
||||
},
|
||||
{
|
||||
vorTagen: 41,
|
||||
uhrzeit: [19, 55],
|
||||
stichwort: "THL 2 Verkehrsunfall",
|
||||
kategorie: EinsatzKategorie.TECHNISCHE_HILFE_MITTEL,
|
||||
strasse: "Kreisstraße MÜ 12",
|
||||
objekt: "zwischen Musterhausen und Nachbarhausen",
|
||||
ablauf: [4, 12, 78, 96],
|
||||
fahrzeuge: [0],
|
||||
mannschaft: 9,
|
||||
verletzt: 2,
|
||||
sachschaden: 24000,
|
||||
beschreibung: "Zwei Fahrzeuge, eine Person eingeklemmt.",
|
||||
bericht:
|
||||
"Person mit dem hydraulischen Rettungssatz befreit und an den Rettungsdienst übergeben. Brandschutz sichergestellt, Fahrzeuge gegen Wegrollen gesichert, Betriebsstoffe aufgenommen.",
|
||||
meldungen: [
|
||||
{ nachMinuten: 14, text: "Eine Person eingeklemmt, Rettungsdienst vor Ort.", wichtig: true },
|
||||
{ nachMinuten: 39, text: "Person befreit und übergeben.", wichtig: true },
|
||||
],
|
||||
material: [{ bezeichnung: "Ölbindemittel", menge: 15, einheit: "kg" }],
|
||||
},
|
||||
{
|
||||
vorTagen: 58,
|
||||
uhrzeit: [9, 12],
|
||||
stichwort: "BMA Auslösung",
|
||||
kategorie: EinsatzKategorie.BRANDMELDEANLAGE,
|
||||
strasse: "Schulstraße",
|
||||
hausnummer: "3",
|
||||
objekt: "Grundschule",
|
||||
ablauf: [7, 13, 32, 44],
|
||||
fahrzeuge: [0],
|
||||
mannschaft: 8,
|
||||
beschreibung: "Auslösung eines Rauchmelders im Werkraum.",
|
||||
bericht: "Kein Schadenfeuer. Auslösung durch Staub bei Reinigungsarbeiten. Anlage zurückgestellt.",
|
||||
},
|
||||
{
|
||||
vorTagen: 73,
|
||||
uhrzeit: [16, 30],
|
||||
stichwort: "B 1 Kleinbrand",
|
||||
kategorie: EinsatzKategorie.BRAND_KLEIN,
|
||||
strasse: "Am Anger",
|
||||
hausnummer: "7",
|
||||
ablauf: [5, 8, 41, 52],
|
||||
fahrzeuge: [1],
|
||||
mannschaft: 6,
|
||||
sachschaden: 1200,
|
||||
beschreibung: "Brennende Mülltonne an der Hauswand.",
|
||||
bericht: "Ablöschen mit Schnellangriff, Kontrolle der Fassade mit der Wärmebildkamera.",
|
||||
},
|
||||
{
|
||||
vorTagen: 95,
|
||||
uhrzeit: [21, 47],
|
||||
stichwort: "THL 1 Sturmschaden",
|
||||
kategorie: EinsatzKategorie.TECHNISCHE_HILFE_KLEIN,
|
||||
strasse: "Lindenweg",
|
||||
ablauf: [8, 16, 62, 74],
|
||||
fahrzeuge: [0],
|
||||
mannschaft: 7,
|
||||
beschreibung: "Baum auf Fahrbahn nach Gewitterfront.",
|
||||
bericht: "Baum mit der Motorkettensäge zerlegt und beiseitegeräumt. Fahrbahn gereinigt.",
|
||||
material: [{ bezeichnung: "Kettensägenbenzin", menge: 2, einheit: "Liter" }],
|
||||
},
|
||||
{
|
||||
vorTagen: 118,
|
||||
uhrzeit: [11, 20],
|
||||
stichwort: "Sicherheitswache Pfarrfest",
|
||||
kategorie: EinsatzKategorie.SICHERHEITSWACHE,
|
||||
strasse: "Kirchplatz",
|
||||
ablauf: [10, 20, 300, 310],
|
||||
fahrzeuge: [2],
|
||||
mannschaft: 4,
|
||||
beschreibung: "Brandsicherheitswache beim Pfarrfest.",
|
||||
bericht: "Ohne besondere Vorkommnisse.",
|
||||
},
|
||||
{
|
||||
vorTagen: 140,
|
||||
uhrzeit: [6, 5],
|
||||
stichwort: "THL 1 Türöffnung",
|
||||
kategorie: EinsatzKategorie.RETTUNGSDIENST,
|
||||
strasse: "Rosenstraße",
|
||||
hausnummer: "22",
|
||||
ablauf: [6, 10, 28, 38],
|
||||
fahrzeuge: [1],
|
||||
mannschaft: 5,
|
||||
gerettet: 1,
|
||||
beschreibung: "Hilflose Person hinter verschlossener Tür, Rettungsdienst vor Ort.",
|
||||
bericht: "Tür schonend geöffnet, Person an den Rettungsdienst übergeben.",
|
||||
},
|
||||
];
|
||||
|
||||
/** Funktionen einer Gruppe in der Reihenfolge, in der sie besetzt werden. */
|
||||
const FUNKTIONSFOLGE: EinsatzFunktion[] = [
|
||||
EinsatzFunktion.GRUPPENFUEHRER,
|
||||
EinsatzFunktion.MASCHINIST,
|
||||
EinsatzFunktion.MELDER,
|
||||
EinsatzFunktion.TRUPPFUEHRER,
|
||||
EinsatzFunktion.TRUPPMANN,
|
||||
EinsatzFunktion.TRUPPFUEHRER,
|
||||
EinsatzFunktion.TRUPPMANN,
|
||||
EinsatzFunktion.TRUPPFUEHRER,
|
||||
EinsatzFunktion.TRUPPMANN,
|
||||
];
|
||||
|
||||
function minutenSpaeter(basis: Date, minuten: number): Date {
|
||||
return new Date(basis.getTime() + minuten * 60_000);
|
||||
}
|
||||
|
||||
async function einsaetzeAnlegen(
|
||||
organisationId: string,
|
||||
fahrzeuge: Array<{ id: string; funkrufname: string }>,
|
||||
) {
|
||||
const vorhanden = await prisma.einsatz.count({ where: { organisationId } });
|
||||
if (vorhanden > 0) {
|
||||
log(`Einsätze: bereits ${vorhanden} vorhanden`);
|
||||
return;
|
||||
}
|
||||
|
||||
const personen = await prisma.person.findMany({
|
||||
where: { organisationId, aktiv: true, geloeschtAm: null },
|
||||
select: { id: true, vorname: true, nachname: true },
|
||||
orderBy: { mitgliedsnummer: "asc" },
|
||||
});
|
||||
|
||||
if (personen.length === 0 || fahrzeuge.length === 0) return;
|
||||
|
||||
let laufend = 0;
|
||||
|
||||
for (const vorlage of EINSAETZE) {
|
||||
laufend += 1;
|
||||
|
||||
const alarmiert = new Date();
|
||||
alarmiert.setUTCDate(alarmiert.getUTCDate() - vorlage.vorTagen);
|
||||
alarmiert.setUTCHours(vorlage.uhrzeit[0], vorlage.uhrzeit[1], 0, 0);
|
||||
|
||||
const jahr = alarmiert.getUTCFullYear();
|
||||
const [ausgerueckt, eingetroffen, ende, eingerueckt] = vorlage.ablauf;
|
||||
|
||||
const einsatz = await prisma.einsatz.create({
|
||||
data: {
|
||||
organisationId,
|
||||
einsatznummer: `${jahr}-${String(laufend).padStart(4, "0")}`,
|
||||
stichwort: vorlage.stichwort,
|
||||
kategorie: vorlage.kategorie,
|
||||
status: vorlage.status ?? EinsatzStatus.ABGESCHLOSSEN,
|
||||
alarmierungsart: Alarmierungsart.MELDEEMPFAENGER,
|
||||
strasse: vorlage.strasse,
|
||||
hausnummer: vorlage.hausnummer ?? null,
|
||||
plz: "84543",
|
||||
ort: "Musterhausen",
|
||||
objekt: vorlage.objekt ?? null,
|
||||
alarmiertUm: alarmiert,
|
||||
ausgeruecktUm: minutenSpaeter(alarmiert, ausgerueckt),
|
||||
eingetroffenUm: minutenSpaeter(alarmiert, eingetroffen),
|
||||
einsatzendeUm: minutenSpaeter(alarmiert, ende),
|
||||
eingeruecktUm: minutenSpaeter(alarmiert, eingerueckt),
|
||||
einsatzbereitUm: minutenSpaeter(alarmiert, eingerueckt + 30),
|
||||
einsatzleiterId: personen[0]?.id ?? null,
|
||||
beschreibung: vorlage.beschreibung,
|
||||
bericht: vorlage.bericht ?? null,
|
||||
geretteteePersonen: vorlage.gerettet ?? 0,
|
||||
verletztePersonen: vorlage.verletzt ?? 0,
|
||||
sachschaden: vorlage.sachschaden ?? null,
|
||||
fahrzeuge: {
|
||||
create: vorlage.fahrzeuge
|
||||
.map((index) => fahrzeuge[index])
|
||||
.filter((fahrzeug): fahrzeug is { id: string; funkrufname: string } => Boolean(fahrzeug))
|
||||
.map((fahrzeug, position) => ({
|
||||
organisationId,
|
||||
fahrzeugId: fahrzeug.id,
|
||||
ausgeruecktUm: minutenSpaeter(alarmiert, ausgerueckt + position),
|
||||
eingetroffenUm: minutenSpaeter(alarmiert, eingetroffen + position),
|
||||
eingeruecktUm: minutenSpaeter(alarmiert, eingerueckt),
|
||||
kilometerAbfahrt: 12000 + laufend * 40,
|
||||
kilometerRueckkehr: 12000 + laufend * 40 + 8 + position * 3,
|
||||
wasserabgabe: vorlage.kategorie.startsWith("BRAND") ? 1500 : null,
|
||||
})),
|
||||
},
|
||||
kraefte: {
|
||||
create: Array.from({ length: Math.min(vorlage.mannschaft, personen.length) }).map(
|
||||
(_, index) => ({
|
||||
organisationId,
|
||||
// Die Mannschaft rotiert, damit die Beteiligung je Person
|
||||
// unterschiedlich ausfällt.
|
||||
personId: personen[(index + laufend * 3) % personen.length]!.id,
|
||||
funktion: FUNKTIONSFOLGE[index % FUNKTIONSFOLGE.length]!,
|
||||
atemschutz: vorlage.kategorie.startsWith("BRAND") && index >= 3 && index <= 6,
|
||||
vonUm: minutenSpaeter(alarmiert, ausgerueckt),
|
||||
bisUm: minutenSpaeter(alarmiert, eingerueckt),
|
||||
}),
|
||||
),
|
||||
},
|
||||
lagemeldungen: {
|
||||
create: (vorlage.meldungen ?? []).map((meldung) => ({
|
||||
organisationId,
|
||||
zeitpunkt: minutenSpaeter(alarmiert, meldung.nachMinuten),
|
||||
text: meldung.text,
|
||||
wichtig: meldung.wichtig ?? false,
|
||||
verfasserId: personen[0]?.id ?? null,
|
||||
verfasserName: personen[0]
|
||||
? `${personen[0].vorname} ${personen[0].nachname}`
|
||||
: "Einsatzleitung",
|
||||
})),
|
||||
},
|
||||
material: {
|
||||
create: (vorlage.material ?? []).map((posten) => ({
|
||||
organisationId,
|
||||
bezeichnung: posten.bezeichnung,
|
||||
menge: posten.menge,
|
||||
einheit: posten.einheit,
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Der älteste Einsatz ist bereits freigegeben — damit die Ansicht eines
|
||||
// festgeschriebenen Berichts nicht leer bleibt.
|
||||
if (vorlage.vorTagen >= 140) {
|
||||
await prisma.einsatz.update({
|
||||
where: { id: einsatz.id },
|
||||
data: {
|
||||
status: EinsatzStatus.FREIGEGEBEN,
|
||||
freigegebenAm: minutenSpaeter(alarmiert, 2880),
|
||||
freigegebenVonId: personen[0]?.id ?? null,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
log(`Einsätze: ${EINSAETZE.length} mit Fahrzeugen, Mannschaft und Lagemeldungen`);
|
||||
}
|
||||
|
||||
interface UebungVorlage {
|
||||
vorTagen: number;
|
||||
thema: string;
|
||||
art: UebungsArt;
|
||||
ort: string;
|
||||
lernziele: string;
|
||||
teilnehmer: number;
|
||||
dauer: number;
|
||||
}
|
||||
|
||||
const UEBUNGEN: UebungVorlage[] = [
|
||||
{
|
||||
vorTagen: 7,
|
||||
thema: "Innenangriff mit Atemschutz",
|
||||
art: UebungsArt.ATEMSCHUTZ,
|
||||
ort: "Übungscontainer Nachbarhausen",
|
||||
lernziele:
|
||||
"Vorgehen im Trupp unter Nullsicht, Türöffnungsprozedur, Rückweg sichern, Kommunikation mit dem Gruppenführer.",
|
||||
teilnehmer: 9,
|
||||
dauer: 150,
|
||||
},
|
||||
{
|
||||
vorTagen: 21,
|
||||
thema: "Wasserförderung über lange Wegstrecke",
|
||||
art: UebungsArt.PRAXIS,
|
||||
ort: "Löschwasserentnahmestelle Mühlbach",
|
||||
lernziele: "Aufbau einer B-Leitung über 600 m, Verstärkerpumpe setzen, Druckverluste erkennen.",
|
||||
teilnehmer: 12,
|
||||
dauer: 120,
|
||||
},
|
||||
{
|
||||
vorTagen: 35,
|
||||
thema: "Technische Hilfeleistung nach Verkehrsunfall",
|
||||
art: UebungsArt.PRAXIS,
|
||||
ort: "Übungsgelände am Bauhof",
|
||||
lernziele:
|
||||
"Fahrzeug sichern, Glasmanagement, patientengerechte Rettung, Zusammenarbeit mit dem Rettungsdienst.",
|
||||
teilnehmer: 11,
|
||||
dauer: 180,
|
||||
},
|
||||
{
|
||||
vorTagen: 49,
|
||||
thema: "Funkausbildung nach BOS-Richtlinie",
|
||||
art: UebungsArt.FUNK,
|
||||
ort: "Gerätehaus, Schulungsraum",
|
||||
lernziele: "Funkdisziplin, Meldeschema, Umgang mit dem Handsprechfunkgerät.",
|
||||
teilnehmer: 8,
|
||||
dauer: 90,
|
||||
},
|
||||
{
|
||||
vorTagen: 63,
|
||||
thema: "Maschinistenausbildung Tragkraftspritze",
|
||||
art: UebungsArt.MASCHINIST,
|
||||
ort: "Gerätehaus",
|
||||
lernziele: "Inbetriebnahme, Entlüften, Störungssuche, Wartung nach dem Einsatz.",
|
||||
teilnehmer: 5,
|
||||
dauer: 120,
|
||||
},
|
||||
{
|
||||
vorTagen: 84,
|
||||
thema: "Gemeinschaftsübung mit der FF Nachbarhausen",
|
||||
art: UebungsArt.GEMEINSCHAFTSUEBUNG,
|
||||
ort: "Sägewerk Nachbarhausen",
|
||||
lernziele:
|
||||
"Zusammenarbeit zweier Wehren, gemeinsame Einsatzleitung, Wasserversorgung aus offenem Gewässer.",
|
||||
teilnehmer: 14,
|
||||
dauer: 210,
|
||||
},
|
||||
];
|
||||
|
||||
async function uebungenAnlegen(organisationId: string) {
|
||||
const vorhanden = await prisma.uebung.count({ where: { organisationId } });
|
||||
if (vorhanden > 0) {
|
||||
log(`Übungen: bereits ${vorhanden} vorhanden`);
|
||||
return;
|
||||
}
|
||||
|
||||
const personen = await prisma.person.findMany({
|
||||
where: { organisationId, aktiv: true, geloeschtAm: null },
|
||||
select: { id: true },
|
||||
orderBy: { mitgliedsnummer: "asc" },
|
||||
});
|
||||
|
||||
if (personen.length === 0) return;
|
||||
|
||||
let laufend = 0;
|
||||
|
||||
for (const vorlage of UEBUNGEN) {
|
||||
laufend += 1;
|
||||
|
||||
const datum = new Date();
|
||||
datum.setUTCDate(datum.getUTCDate() - vorlage.vorTagen);
|
||||
datum.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
const beginn = new Date(datum);
|
||||
beginn.setUTCHours(19, 0, 0, 0);
|
||||
|
||||
await prisma.uebung.create({
|
||||
data: {
|
||||
organisationId,
|
||||
nummer: `${datum.getUTCFullYear()}-${String(laufend).padStart(4, "0")}`,
|
||||
thema: vorlage.thema,
|
||||
art: vorlage.art,
|
||||
datum,
|
||||
beginnUm: beginn,
|
||||
endeUm: minutenSpaeter(beginn, vorlage.dauer),
|
||||
ort: vorlage.ort,
|
||||
leiterId: personen[0]?.id ?? null,
|
||||
lernziele: vorlage.lernziele,
|
||||
teilnahmen: {
|
||||
create: Array.from({ length: Math.min(vorlage.teilnehmer, personen.length) }).map(
|
||||
(_, index) => ({
|
||||
organisationId,
|
||||
personId: personen[(index + laufend * 2) % personen.length]!.id,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
log(`Übungen: ${UEBUNGEN.length} mit Teilnahmen`);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Ablauf
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -1539,6 +1976,10 @@ async function main(): Promise<void> {
|
||||
await beispielPruefungAnlegen(organisation.id, wartungsvorlagen);
|
||||
await rueckrufAnlegen(organisation.id);
|
||||
|
||||
// Phase 3 — Einsätze und Übungen
|
||||
await einsaetzeAnlegen(organisation.id, fahrzeuge);
|
||||
await uebungenAnlegen(organisation.id);
|
||||
|
||||
log("Fertig.");
|
||||
log("");
|
||||
log("Anmeldung für den Erststart:");
|
||||
|
||||
Reference in New Issue
Block a user