diff --git a/backend/routers/legal_documents.py b/backend/routers/legal_documents.py index 98d2c45..60942dd 100644 --- a/backend/routers/legal_documents.py +++ b/backend/routers/legal_documents.py @@ -384,6 +384,70 @@ def archive_legal_document( return r2d(updated) +@router.post("/api/admin/legal-documents/{doc_id}/copy-as-draft", status_code=201) +def copy_legal_document_as_draft( + doc_id: int, + session: dict = Depends(require_auth), +): + """ + Kopiert ein beliebiges Dokument (egal welcher Status) als neuen Entwurf mit + nächster Versionsnummer. Inhalt und Titel werden übernommen. + """ + _require_superadmin(session) + + profile_id = session["profile_id"] + + with get_db() as conn: + cur = get_cursor(conn) + cur.execute( + "SELECT id, document_type, title, content_sections FROM legal_documents WHERE id = %s", + (doc_id,), + ) + row = cur.fetchone() + + if not row: + raise HTTPException(status_code=404, detail="Dokument nicht gefunden") + + src = r2d(row) + + import json as _json + sections_json = _json.dumps(src["content_sections"], ensure_ascii=False) + + with get_db() as conn: + cur = get_cursor(conn) + cur.execute( + "SELECT COALESCE(MAX(version), 0) FROM legal_documents WHERE document_type = %s", + (src["document_type"],), + ) + row2 = cur.fetchone() + next_version = list(row2.values())[0] + 1 + + cur.execute( + """ + INSERT INTO legal_documents + (document_type, version, title, content_sections, status, change_note, created_by_profile_id) + VALUES (%s, %s, %s, %s::jsonb, 'draft', NULL, %s) + RETURNING id, document_type, version, title, content_sections, + status, change_note, created_at, updated_at + """, + (src["document_type"], next_version, src["title"], sections_json, profile_id), + ) + new_row = cur.fetchone() + new_id = list(new_row.values())[0] + + cur.execute( + """ + INSERT INTO legal_document_audit + (legal_document_id, action, changed_by_profile_id, change_note) + VALUES (%s, 'created', %s, %s) + """, + (new_id, profile_id, f"Kopie von Version {src.get('version', '?')} (ID {doc_id})"), + ) + conn.commit() + + return r2d(new_row) + + @router.get("/api/admin/legal-documents/{doc_id}/audit") def get_legal_document_audit(doc_id: int, session: dict = Depends(require_auth)): """Änderungslog für ein Dokument.""" diff --git a/backend/version.py b/backend/version.py index 608b78a..f20f322 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,11 +1,11 @@ # Shinkan Jinkendo Version Information -APP_VERSION = "0.8.71" +APP_VERSION = "0.8.72" BUILD_DATE = "2026-05-10" DB_SCHEMA_VERSION = "20260510047" MODULE_VERSIONS = { - "legal_documents": "1.0.0", # P-01c: Admin-konfigurierbare Rechtstexte (legal_documents + legal_document_audit) + "legal_documents": "1.1.0", # Als-Entwurf-kopieren: POST /api/admin/legal-documents/{id}/copy-as-draft "auth": "1.2.3", # P-05b: reset-password min_length=8 via Pydantic PasswordResetConfirm "profiles": "1.7.0", # exercise_list_prefs JSONB (Standard Übungsfilter); Patch via ProfileUpdate + Json() "tenant_context": "1.0.5", # Plattform-Admin: effective_club ohne Header aus Profil active_club_id wenn Verein existiert @@ -30,6 +30,13 @@ MODULE_VERSIONS = { } CHANGELOG = [ + { + "version": "0.8.72", + "date": "2026-05-10", + "changes": [ + "Rechtstexte: Als-Entwurf-kopieren — POST /api/admin/legal-documents/{id}/copy-as-draft; Inhalt und Titel werden uebernommen, Versionsnummer inkrementiert", + ], + }, { "version": "0.8.71", "date": "2026-05-10", diff --git a/frontend/src/pages/AdminLegalDocumentsPage.jsx b/frontend/src/pages/AdminLegalDocumentsPage.jsx index 10ba9b8..87697af 100644 --- a/frontend/src/pages/AdminLegalDocumentsPage.jsx +++ b/frontend/src/pages/AdminLegalDocumentsPage.jsx @@ -1,5 +1,5 @@ import { useState, useEffect, useCallback } from 'react' -import { FileText, Plus, Eye, Edit2, Archive, CheckCircle, Clock, ChevronDown, ChevronUp } from 'lucide-react' +import { FileText, Plus, Edit2, Archive, CheckCircle, Clock, Copy } from 'lucide-react' import api from '../utils/api' const DOC_TYPES = [ @@ -95,7 +95,7 @@ function DocTypeTab({ docType, active, onClick }) { ) } -function DocumentRow({ doc, onPublish, onArchive, onEdit, onViewAudit }) { +function DocumentRow({ doc, onPublish, onArchive, onEdit, onCopy, onViewAudit }) { return (
)} +