// Import der Umsetzungshinweise (Story B5-1) aus dem Fachcontent C6 in das globale // Modell ImplementationHint. Ein Block je Teilanforderung: // // ### — [] // **Anforderung:** … // - **Organisatorisch:** … // - **Technisch:** … // - **Typische Nachweise:** … // - **Vorlage:** L00 // - **Ressourcen:** … // - **AL-Filter:** AL2 + AL3 // // Idempotent (Upsert über reqId). Kein tenantId (globaler Katalog). import { readFileSync } from "node:fs"; import { join } from "node:path"; import type { PrismaClient } from "@prisma/client"; const C6_PATH = join(__dirname, "..", "docs", "wizard-uebergabe", "02_Fachcontent_C1-C9", "C6_Umsetzungshinweise.md"); const field = (block: string, label: string): string => { // Feldzeile "- **Label:** Wert" oder "**Label:** Wert" bis Zeilenende. const re = new RegExp(`\\*\\*${label}:\\*\\*\\s*(.+)`); const m = block.match(re); return m ? m[1].trim() : ""; }; const isProcurement = (resources: string): boolean => { const r = resources.toLowerCase(); if (/kein\s+toolbudget|kein\s+budget|ohne\s+budget|keine\s+beschaffung/.test(r)) return false; return /beschaffung|toolbudget|budget|lizenz|invest|anschaffung|€|eur\b/.test(r); }; export interface ParsedHint { reqId: string; control: string; stufe: string; requirement: string; organisational: string; technical: string; evidence: string; template: string | null; resources: string; alFilter: string; procurement: boolean; } /** Parst den C6-Text zu Hinweis-Objekten (ohne DB-Zugriff — testbar). */ export function parseC6Hints(text: string): ParsedHint[] { const hints: ParsedHint[] = []; // Blöcke ab "### — [STUFE]"; nächster "### "/"## "/"# " beendet den Block. const re = /^###\s+(\S+)\s+—\s+\[([^\]]+)\]\s*\n([\s\S]*?)(?=^#{1,3}\s|$(?![\s\S]))/gm; let m: RegExpExecArray | null; while ((m = re.exec(text)) !== null) { const reqId = m[1].trim(); if (!/^\d+\.\d+\.\d+-/.test(reqId)) continue; // nur Anforderungs-IDs const stufe = m[2].trim(); const block = m[3]; const resources = field(block, "Ressourcen"); const template = field(block, "Vorlage") || null; hints.push({ reqId, control: reqId.split("-")[0], stufe, requirement: field(block, "Anforderung"), organisational: field(block, "Organisatorisch"), technical: field(block, "Technisch"), evidence: field(block, "Typische Nachweise"), template, resources, alFilter: field(block, "AL-Filter"), procurement: isProcurement(resources), }); } return hints; } /** Liest C6 und schreibt die Hinweise (Upsert je reqId). Liefert die Anzahl. */ export async function importImplementationHints(prisma: PrismaClient): Promise { const text = readFileSync(C6_PATH, "utf-8"); const hints = parseC6Hints(text); for (const h of hints) { await prisma.implementationHint.upsert({ where: { reqId: h.reqId }, update: { control: h.control, stufe: h.stufe, requirement: h.requirement, organisational: h.organisational, technical: h.technical, evidence: h.evidence, template: h.template, resources: h.resources, alFilter: h.alFilter, procurement: h.procurement }, create: h, }); } return hints.length; }