Richtlinien-Bearbeitungsmodus am Mockup ausgerichtet: Variablen + Vier-Augen-Freigabe
Korrektur nach Mockup-Abgleich: Der Bearbeitungsmodus editiert NICHT den Rohtext, sondern (wie im Mockup und Akzeptanzkriterium „im Bearbeiten nur dokumentbezogene Variablen") die dokument-bezogenen Variablen — freie Blocktext-Änderungen sind dem KI-Wizard vorbehalten. - Edit-Seite neu: gerendertes Dokument links, rechts Karte „Dokument-Variablen" (nur die im Dokument vorkommenden, inkl. BL-gebundene; zentrale Pflegestelle §8) + „Freigabe"-Leiste. Rohtext-Textarea entfernt. - Freigabe-Workflow (Vier-Augen, §8): Status Entwurf → In Freigabe → Freigegeben; submitForApproval/approvePolicy/rejectPolicy; Freigabe erfordert policy:approve UND Freigebender ≠ Einreichender; Ablehnung mit Begründung → zurück auf Entwurf; alles im Audit-Log. Neue Felder submitted_by/approved_by/approved_at. - updateScopedVariables: gebündeltes Speichern der Dokument-Variablen. Verifiziert: Editor zeigt Variablen-Karte + Freigabe-Leiste (kein Rohtext); R08 „Erneut einreichen" → In Freigabe; Vier-Augen-Sperre greift (Anna = Autor). Offen (nächste Ausbaustufe): echte Versionierung mit Entwurf-neben-Freigegeben + block-/klauselgenauer Diff; KI-Wizard für Blocktext; DOCX/PDF-Export; Word-Upload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -146,3 +146,63 @@ export async function updatePolicyVariable(key: string, formData: FormData) {
|
||||
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "policy_variable", after: { key, value } });
|
||||
revalidatePath("/policies", "layout");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dokument-bezogene Variablen gebündelt speichern (Bearbeitungsmodus). Nur die
|
||||
* übergebenen Schlüssel (Feld `keys` = kommagetrennt) werden aus dem Formular
|
||||
* gelesen; Werte gelten zentral für alle Dokumente (eine Pflegestelle, §8).
|
||||
*/
|
||||
export async function updateScopedVariables(code: string, formData: FormData) {
|
||||
const { session, db } = await guard();
|
||||
const keys = String(formData.get("keys") ?? "").split(",").map((k) => k.trim()).filter(Boolean);
|
||||
for (const key of keys) {
|
||||
if (!formData.has(`var_${key}`)) continue;
|
||||
const value = String(formData.get(`var_${key}`) ?? "");
|
||||
await db.policyVariable.updateMany({ where: { key }, data: { value } });
|
||||
}
|
||||
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "policy_variables", entityId: code, after: { keys } });
|
||||
revalidatePath("/policies", "layout");
|
||||
redirect(`/policies/${encodeURIComponent(code)}/edit`);
|
||||
}
|
||||
|
||||
/* ── Freigabe-Workflow (Vier-Augen, §8) ── */
|
||||
|
||||
export async function submitForApproval(code: string) {
|
||||
const { session, db } = await guard();
|
||||
const doc = await db.policyDocument.findFirst({ where: { code } });
|
||||
if (!doc) throw new Error("Dokument nicht gefunden");
|
||||
await db.policyDocument.update({ where: { id: doc.id }, data: { status: "IN_FREIGABE", submittedBy: session.user.id, approvedBy: null, approvedAt: null } });
|
||||
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "policy_document", entityId: doc.id, after: { status: "IN_FREIGABE" } });
|
||||
revalidatePath("/policies", "layout");
|
||||
redirect(`/policies/${encodeURIComponent(code)}/edit`);
|
||||
}
|
||||
|
||||
export async function approvePolicy(code: string) {
|
||||
const session = await requireSession();
|
||||
requirePermission(session, "policy:approve");
|
||||
const db = dbForTenant(session.user.tenantId);
|
||||
const doc = await db.policyDocument.findFirst({ where: { code } });
|
||||
if (!doc) throw new Error("Dokument nicht gefunden");
|
||||
if (doc.status !== "IN_FREIGABE") throw new Error("Dokument ist nicht in Freigabe");
|
||||
// Vier-Augen: Freigebender ≠ Einreichender
|
||||
if (doc.submittedBy && doc.submittedBy === session.user.id) {
|
||||
throw new Error("Vier-Augen-Prinzip: Freigabe muss durch eine andere Person als den Einreichenden erfolgen.");
|
||||
}
|
||||
await db.policyDocument.update({ where: { id: doc.id }, data: { status: "FREIGEGEBEN", approvedBy: session.user.id, approvedAt: new Date() } });
|
||||
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "policy_document", entityId: doc.id, after: { status: "FREIGEGEBEN" } });
|
||||
revalidatePath("/policies", "layout");
|
||||
redirect(`/policies/${encodeURIComponent(code)}`);
|
||||
}
|
||||
|
||||
export async function rejectPolicy(code: string, formData: FormData) {
|
||||
const session = await requireSession();
|
||||
requirePermission(session, "policy:approve");
|
||||
const db = dbForTenant(session.user.tenantId);
|
||||
const doc = await db.policyDocument.findFirst({ where: { code } });
|
||||
if (!doc) throw new Error("Dokument nicht gefunden");
|
||||
const reason = str(formData.get("reason"));
|
||||
await db.policyDocument.update({ where: { id: doc.id }, data: { status: "ENTWURF", submittedBy: null } });
|
||||
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "policy_document", entityId: doc.id, after: { status: "ENTWURF", rejected: true, reason } });
|
||||
revalidatePath("/policies", "layout");
|
||||
redirect(`/policies/${encodeURIComponent(code)}/edit`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user