Erstimport. Fachliche Grundlage ist die EIU-Ablagestruktur des Auftraggebers (2-EIU-Ablagestruktur.xlsx): 1.106 Zeilen, aufbereitet zu 159 Ordnern und 944 Soll-Unterlagen mit Fachbereichsrelevanz, Aufbewahrungsfrist, Medium und Übergabezeitpunkt. Module - Dokumentationsstand: Soll-Ist je Projekt, Fristen aus dem Übergabezeitpunkt und den Projektterminen, Ampel, CSV-Rückexport im Format der Ursprungsdatei - Dokumentenmanagement mit Versionen, Freigabelauf, SHA-256-Prüfsummen - Bautagebuch nach Ril 809.0301, nach Abschluss gesperrt - Meldungen und Mängel (Behinderung, Bedenken, Mangel, AvL, Unfall) - Verträge, Nachträge, Aufmaße, Stundenlohnzettel mit zeilenweiser Anerkennung - Rechnungsprüfung: automatischer Abgleich gegen Vertrag, anerkanntes Aufmaß, bestätigte Stundenzettel und Vorrechnungen; getrennte Bescheinigung sachlich richtig / rechnerisch richtig / Zahlungsfreigabe - Kommunikation mit Projektkanälen, Erwähnungen und E-Mail-Benachrichtigung - Revisionssicheres Protokoll aller Vorgänge Rechte Rollen nach dem Vorgabeprozess Bauüberwachung (PL, BHV, BÜB, FBÜ, kaufmännische Steuerung, Planung, Auftragnehmer, Prüfer, Betreiber), Zuordnung je Projekt. Auftragnehmer sehen ausschließlich Daten der eigenen Firma; diese Schranke hängt allein an der Rolle. Welche Bereiche ihnen überhaupt angezeigt werden, entscheidet der Bauherr je Projekt über Schalter in den Einstellungen. Betrieb Docker Compose mit Anwendung, PostgreSQL und Mailpit. Keine Named Volumes – alle persistenten Daten liegen als Bind-Mount unter data/ im Compose- Verzeichnis, damit ein Backup ein Kopiervorgang bleibt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
import type { Project, Uebergabezeitpunkt } from "@prisma/client";
|
||
|
||
export const UEBERGABE_LABEL: Record<Uebergabezeitpunkt, string> = {
|
||
INBETRIEBNAHME: "zur Inbetriebnahme",
|
||
BAUAKTE_II_IBN: "Bauakte Teil II (IBN-Akte)",
|
||
IBN_PLUS_3M: "3 Monate nach IBN",
|
||
IBN_PLUS_6M: "6 Monate nach IBN",
|
||
BAUAKTE_I_BAUENDE_6M: "Bauakte Teil I – 6 Monate nach Bauende",
|
||
PROJEKTABSCHLUSS: "zum Projektabschluss",
|
||
};
|
||
|
||
function plusMonths(d: Date, m: number) {
|
||
const x = new Date(d);
|
||
x.setMonth(x.getMonth() + m);
|
||
return x;
|
||
}
|
||
|
||
/**
|
||
* Rechnet den Übergabezeitpunkt aus der Ablagestruktur gegen die
|
||
* Projekttermine und liefert die konkrete Frist.
|
||
* Es gilt die früheste erreichbare Frist – so entsteht kein Stau am Projektende.
|
||
*/
|
||
export function berechneFrist(
|
||
uebergabe: Uebergabezeitpunkt[],
|
||
projekt: Pick<Project, "inbetriebnahme" | "bauende" | "projektabschluss" | "bauschlussmeldung">,
|
||
): Date | null {
|
||
const kandidaten: Date[] = [];
|
||
for (const u of uebergabe) {
|
||
switch (u) {
|
||
case "INBETRIEBNAHME":
|
||
case "BAUAKTE_II_IBN":
|
||
if (projekt.inbetriebnahme) kandidaten.push(projekt.inbetriebnahme);
|
||
break;
|
||
case "IBN_PLUS_3M":
|
||
if (projekt.inbetriebnahme) kandidaten.push(plusMonths(projekt.inbetriebnahme, 3));
|
||
break;
|
||
case "IBN_PLUS_6M":
|
||
if (projekt.inbetriebnahme) kandidaten.push(plusMonths(projekt.inbetriebnahme, 6));
|
||
break;
|
||
case "BAUAKTE_I_BAUENDE_6M":
|
||
if (projekt.bauende) kandidaten.push(plusMonths(projekt.bauende, 6));
|
||
break;
|
||
case "PROJEKTABSCHLUSS":
|
||
if (projekt.projektabschluss) kandidaten.push(projekt.projektabschluss);
|
||
break;
|
||
}
|
||
}
|
||
if (kandidaten.length === 0) return null;
|
||
return new Date(Math.min(...kandidaten.map((d) => d.getTime())));
|
||
}
|
||
|
||
/** Aufbewahrungsende gemäß Ablagestruktur (Jahre nach Bauschlussmeldung). */
|
||
export function berechneAufbewahrung(
|
||
jahre: string | null,
|
||
nutzungsdauer: boolean,
|
||
bauschlussmeldung: Date | null,
|
||
): { bis: Date | null; hinweis: string } {
|
||
if (nutzungsdauer) return { bis: null, hinweis: "Nutzungsdauer der Anlage" };
|
||
if (!jahre) return { bis: null, hinweis: "nicht festgelegt" };
|
||
const m = jahre.match(/(\d+)/);
|
||
if (!m) return { bis: null, hinweis: jahre };
|
||
const n = parseInt(m[1], 10);
|
||
if (!bauschlussmeldung) return { bis: null, hinweis: `${n} Jahre nach Bauschlussmeldung` };
|
||
const bis = new Date(bauschlussmeldung);
|
||
bis.setFullYear(bis.getFullYear() + n);
|
||
return { bis, hinweis: `${n} Jahre nach Bauschlussmeldung` };
|
||
}
|
||
|
||
export type Ampel = "GRUEN" | "GELB" | "ROT" | "GRAU";
|
||
|
||
export function fristAmpel(frist: Date | null, erledigt: boolean, heute = new Date()): Ampel {
|
||
if (erledigt) return "GRUEN";
|
||
if (!frist) return "GRAU";
|
||
const tage = Math.floor((frist.getTime() - heute.getTime()) / 864e5);
|
||
if (tage < 0) return "ROT";
|
||
if (tage <= 30) return "GELB";
|
||
return "GRUEN";
|
||
}
|
||
|
||
export const FACHBEREICHE_NETZ = [
|
||
"Oberbau",
|
||
"Tiefbau",
|
||
"Konstruktiver Ingenieurbau",
|
||
"Hochbau",
|
||
"Elektrotechnik 50 Hz",
|
||
"LST",
|
||
"Tk (inkl. HOA, FBOA)",
|
||
"Oberleitung 16,7 Hz",
|
||
"Maschinentechnik",
|
||
];
|
||
|
||
export const FACHBEREICHE_BAHNHOF = [
|
||
"Hoch-, Tief- und Ingenieurbau/Verkehrsanlagen inkl. baulichem Brandschutz",
|
||
"HLS (KG 410/420/430/435)",
|
||
"Starkstromanlagen 50 Hz, Erdung (KG 440)",
|
||
"Blitzschutz (KG 446)",
|
||
"TK-Anlagen (KG 450)",
|
||
"Fördertechnik (KG 460)",
|
||
"Gebäudeautomation (KG 480)",
|
||
];
|