syncova-policies/scripts/fix-legal-citations.js
Weapie 328a32a7f2
Some checks failed
Container-Image bauen und veröffentlichen / build-and-push (push) Failing after 21s
Geprüfte Fachrechts-Zitate korrigieren
Gegen den geltenden Normbestand geprüft und berichtigt:

- Landesbauordnung SH, neu gefasst zum 01.09.2022 und dabei umnummeriert.
  Bauvoranfrage § 61 -> § 75 (Vorbescheid), Befreiungs-/Abweichungsantrag
  § 61 -> § 67 LBO und § 31 BauGB, gemeindliches Einvernehmen § 61 ->
  § 36 BauGB und § 71 LBO. Beim Bauantrag § 68 ergänzt, § 64 bleibt.
- Wohnberechtigungsschein: benannte das Dokument statt der Norm,
  jetzt § 27 WoFG i. V. m. § 5 WoBindG.
- Bildung und Teilhabe: "BKKG" berichtigt zu § 6b BKGG, § 34 SGB XII
  präzisiert (drei Erklärungen).
- Ausschreibungsunterlagen: überholte VOL/A entfernt.
- Beihilfe, Mutterschutz und Arbeitsmedizin verarbeiten Gesundheitsdaten
  und haben zusätzlich Art. 9 Abs. 2 lit. b DSGVO erhalten.

Nicht angefasst, weil ohne belegte Fassung: nicht zitierfähige Satzungen,
die Anschlussbescheinigung, die Angelerlaubnis sowie alle fehlenden
Fristen, Zwecke und Datenkategorien.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:46:13 +02:00

214 lines
7.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Korrigiert einzelne fachrechtliche Zitate, die gegen den geltenden
* Normbestand geprüft wurden.
*
* node scripts/fix-legal-citations.js --dry-run
* node scripts/fix-legal-citations.js
*
* Geändert wird ausschließlich, wofür eine belegte, eindeutige Fassung
* vorliegt. Alles Übrige – nicht zitierfähige Satzungen, unklare Zuordnungen,
* fehlende Fristen – bleibt unangetastet und geht an die Datenschutz-
* beauftragte.
*
* Der Lauf ist wiederholbar und setzt nur an, wo der alte Wortlaut noch steht.
*/
const fs = require("fs")
const path = require("path")
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()
const JSON_DIR = path.join(process.cwd(), "data", "policies")
/** Trennzeile, hinter der das Fachrecht in `legalBasis` steht. */
const MARKER = "Konkrete Rechtsgrundlage: "
/**
* Belegte Korrekturen. `von` muss exakt dem heutigen Fachrechts-Text
* entsprechen, sonst wird der Datensatz übersprungen.
*/
const KORREKTUREN = [
// --- Landesbauordnung SH, neu gefasst am 6.12.2021, gültig ab 01.09.2022 ---
// Die Paragraphen wurden dabei umnummeriert.
{
titel: "Bauantrag",
von: "§ 64 LBO",
nach: "§§ 64, 68 LBO",
grund:
"§ 64 LBO regelt das Baugenehmigungsverfahren, der Bauantrag selbst steht in § 68 LBO (Bauantrag, Bauvorlagen). Ergänzt statt ersetzt, da beide einschlägig sind.",
},
{
titel: "Bauvoranfrage",
von: "§ 61 LBO",
nach: "§ 75 LBO",
grund:
"§ 61 LBO ist seit 01.09.2022 „Verfahrensfreie Bauvorhaben“. Der Vorbescheid – die Bauvoranfrage – steht in § 75 LBO.",
},
{
titel: "Befreiungsantrag /Abweichungsantrag/Ausnahmeantrag",
von: "§ 61 LBO",
nach: "§ 67 LBO, § 31 BauGB",
grund:
"Abweichungen, Ausnahmen und Befreiungen stehen in § 67 LBO; bauplanungsrechtliche Befreiungen zusätzlich in § 31 BauGB.",
},
{
titel: "Stellungnahme Bauantrag",
von: "§ 61 LBO",
nach: "§ 36 BauGB, § 71 LBO",
grund:
"Das gemeindliche Einvernehmen regelt § 36 BauGB, seine Ersetzung § 71 LBO. § 61 LBO betrifft verfahrensfreie Vorhaben.",
},
// --- Wohnberechtigungsschein ---
{
titel: "Antrag auf Ausstellung eines Wohnberechtigungsscheines",
von: "Wohnberechtigungsschein",
nach: "§ 27 WoFG i. V. m. § 5 WoBindG",
grund:
"„Wohnberechtigungsschein“ benennt das Dokument, keine Norm. Der Schein wird nach § 27 WoFG i. V. m. § 5 WoBindG erteilt.",
},
// --- Bildung und Teilhabe ---
{
titel: "Antrag auf Hilfe für Bildung u. Teilhabe",
von: "SGB XII, BKKG",
nach: "§ 34 SGB XII, § 6b BKGG",
grund:
"„BKKG“ ist ein Schreibfehler für BKGG. Leistungen für Bildung und Teilhabe stehen in § 34 SGB XII bzw. § 6b BKGG.",
},
{
titel: "Antrag auf Hilfe für Bildung u. Teilhabe: Hilfe f. mehrtägige Klassenfahrten",
von: "SGB XII",
nach: "§ 34 SGB XII",
grund: "Präzisierung: Leistungen für Bildung und Teilhabe stehen in § 34 SGB XII.",
},
{
titel: "Änderungsmitteilung auf Hilfe für Bildung u. Teilhabe",
von: "SGB XII",
nach: "§ 34 SGB XII",
grund: "Präzisierung: Leistungen für Bildung und Teilhabe stehen in § 34 SGB XII.",
},
// --- Vergaberecht ---
{
titel: "Ausschreibungsunterlagen",
von: "VOL/VgV/UVGO/VGO S-H",
nach: "VgV/UVgO/VGO S-H",
grund:
"Die VOL/A ist für Liefer- und Dienstleistungen durch die UVgO abgelöst. Nur das überholte Kürzel entfernt; die Landesvorschrift sollte noch mit vollem Titel benannt werden.",
},
]
/**
* Zusätzliche Erlaubnisnorm, wo besondere Kategorien personenbezogener Daten
* verarbeitet werden. Art. 9 Abs. 1 DSGVO verbietet das grundsätzlich; die
* Verarbeitung braucht deshalb eine eigene Grundlage nach Art. 9 Abs. 2.
*/
const ART9_ZUSATZ =
"Soweit Gesundheitsdaten verarbeitet werden: **Art. 9 Abs. 2 lit. b DSGVO** i. V. m. **§ 15 LDSG SH** – Verarbeitung besonderer Kategorien personenbezogener Daten zur Erfüllung von Pflichten aus dem Arbeits- und Sozialrecht."
const ART9_TITEL = [
"Beihilferecht",
"Mutterschutz, Elternzeit",
"Arbeitsmedizinische Untersuchungen, Unfallfürsorge",
]
function fachrechtErsetzen(legalBasis, von, nach) {
const stelle = legalBasis.indexOf(MARKER)
if (stelle === -1) return null
const kopf = legalBasis.slice(0, stelle + MARKER.length)
const rest = legalBasis.slice(stelle + MARKER.length)
if (rest.trim() !== von) return null
return kopf + nach
}
async function main() {
const dryRun = process.argv.includes("--dry-run")
const policies = await prisma.privacyPolicy.findMany()
const nachTitel = new Map(policies.map((p) => [p.title, p]))
const geplant = []
const uebersprungen = []
for (const korrektur of KORREKTUREN) {
const policy = nachTitel.get(korrektur.titel)
if (!policy) {
uebersprungen.push(`${korrektur.titel} – Erklärung nicht gefunden`)
continue
}
const neu = fachrechtErsetzen(policy.legalBasis || "", korrektur.von, korrektur.nach)
if (!neu) {
uebersprungen.push(
`${korrektur.titel} – Wortlaut weicht ab (erwartet „${korrektur.von}“), bereits geändert?`
)
continue
}
geplant.push({ policy, korrektur, neu })
}
const art9 = []
for (const titel of ART9_TITEL) {
const policy = nachTitel.get(titel)
if (!policy) {
uebersprungen.push(`${titel} – Erklärung nicht gefunden`)
continue
}
if ((policy.legalBasis || "").includes("Art. 9 Abs. 2")) {
uebersprungen.push(`${titel} – Art. 9 bereits ergänzt`)
continue
}
art9.push({ policy, neu: `${policy.legalBasis}\n\n${ART9_ZUSATZ}` })
}
console.log(`=== Fachrechtliche Zitate (${geplant.length}) ===`)
for (const { korrektur } of geplant) {
console.log(`\n ${korrektur.titel}`)
console.log(` vorher : ${korrektur.von}`)
console.log(` nachher: ${korrektur.nach}`)
console.log(` Grund : ${korrektur.grund}`)
}
console.log(`\n=== Besondere Datenkategorien, Art. 9 Abs. 2 lit. b (${art9.length}) ===`)
art9.forEach(({ policy }) => console.log(` ${policy.title}`))
if (uebersprungen.length) {
console.log(`\n=== Übersprungen (${uebersprungen.length}) ===`)
uebersprungen.forEach((z) => console.log(` ${z}`))
}
if (dryRun) {
console.log("\n--dry-run: nichts geschrieben.")
return
}
const schreiben = async (policy, legalBasis) => {
await prisma.privacyPolicy.update({ where: { id: policy.id }, data: { legalBasis } })
const datei = path.join(JSON_DIR, `${policy.slug}.json`)
try {
if (fs.existsSync(datei)) {
const inhalt = JSON.parse(fs.readFileSync(datei, "utf8"))
inhalt.legalBasis = legalBasis
fs.writeFileSync(datei, JSON.stringify(inhalt, null, 2), "utf8")
}
} catch {
// Die Datenbank ist maßgeblich; die Ablage ist nur Nachweis.
}
}
for (const { policy, neu } of geplant) await schreiben(policy, neu)
for (const { policy, neu } of art9) await schreiben(policy, neu)
console.log(`\nZitate korrigiert: ${geplant.length}`)
console.log(`Art. 9 ergänzt: ${art9.length}`)
}
main()
.catch((e) => {
console.error(e)
process.exitCode = 1
})
.finally(() => prisma.$disconnect())