feat: Dokumentengenerator — Vollständige Vorlage-Bibliothek + Frontend-Redesign
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 18s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 18s
- Migration 020: Typ-Renames (data_processing_agreement→dpa, withdrawal_policy→widerruf) + 11 neue MIT-Templates (NDA DE/EN, SLA, AUP, Community Guidelines, Copyright Policy, Cloud Service Agreement, Data Usage Clause, Cookie Banner, AGB, Liability Clause) - Backend: VALID_DOCUMENT_TYPES auf 16 Typen erweitert; /legal-templates/status nutzt jetzt dynamisches GROUP BY statt Hard-coded Felder - searchTemplates.ts: loadAllTemplates() für Library-First UX - page.tsx: Vollständig-Rewrite — Template-Bibliothek (immer sichtbar) mit Kategorie-Pills, Sprache-Toggle, optionaler Suche, Inline-Preview-Expand und Kachel-Grid; Generator-Section erscheint per Scroll wenn Vorlage gewählt - Tests: 52/52 bestanden, TestLegalTemplateNewTypes (19 neue Tests) + aktualisierte Typ-Checks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -120,6 +120,24 @@ function mapTemplateToResult(r: any): LegalTemplateResult {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all published templates without requiring a search query.
|
||||
* Used for the library-first UX (show all cards on initial load).
|
||||
*/
|
||||
export async function loadAllTemplates(limit = 200): Promise<LegalTemplateResult[]> {
|
||||
try {
|
||||
const url = new URL(TEMPLATES_API, window.location.origin)
|
||||
url.searchParams.set('limit', String(limit))
|
||||
url.searchParams.set('status', 'published')
|
||||
const res = await fetch(url.toString(), { signal: AbortSignal.timeout(5000) })
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
return (data.templates || []).map(mapTemplateToResult)
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
return []
|
||||
}
|
||||
|
||||
export async function getTemplatesStatus(): Promise<any> {
|
||||
try {
|
||||
const res = await fetch(`${TEMPLATES_API}/status`, { signal: AbortSignal.timeout(5000) })
|
||||
|
||||
@@ -32,12 +32,25 @@ router = APIRouter(prefix="/legal-templates", tags=["legal-templates"])
|
||||
DEFAULT_TENANT_ID = "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e"
|
||||
|
||||
VALID_DOCUMENT_TYPES = {
|
||||
# Original types
|
||||
"privacy_policy",
|
||||
"terms_of_service",
|
||||
"impressum",
|
||||
"data_processing_agreement",
|
||||
"withdrawal_policy",
|
||||
"cookie_policy",
|
||||
# Renamed from data_processing_agreement / withdrawal_policy (Migration 020)
|
||||
"dpa",
|
||||
"widerruf",
|
||||
# New types (Migration 020)
|
||||
"nda",
|
||||
"sla",
|
||||
"acceptable_use",
|
||||
"community_guidelines",
|
||||
"copyright_policy",
|
||||
"cloud_service_agreement",
|
||||
"data_usage_clause",
|
||||
"cookie_banner",
|
||||
"agb",
|
||||
"clause",
|
||||
}
|
||||
VALID_STATUSES = {"published", "draft", "archived"}
|
||||
|
||||
@@ -184,42 +197,38 @@ async def get_templates_status(
|
||||
"""Return template counts by document_type."""
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
|
||||
row = db.execute(text("""
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
COUNT(*) FILTER (WHERE status = 'published') AS published,
|
||||
COUNT(*) FILTER (WHERE status = 'draft') AS draft,
|
||||
COUNT(*) FILTER (WHERE status = 'archived') AS archived,
|
||||
COUNT(*) FILTER (WHERE document_type = 'privacy_policy') AS privacy_policy,
|
||||
COUNT(*) FILTER (WHERE document_type = 'terms_of_service') AS terms_of_service,
|
||||
COUNT(*) FILTER (WHERE document_type = 'impressum') AS impressum,
|
||||
COUNT(*) FILTER (WHERE document_type = 'data_processing_agreement') AS data_processing_agreement,
|
||||
COUNT(*) FILTER (WHERE document_type = 'withdrawal_policy') AS withdrawal_policy,
|
||||
COUNT(*) FILTER (WHERE document_type = 'cookie_policy') AS cookie_policy
|
||||
total_row = db.execute(
|
||||
text("SELECT COUNT(*) FROM compliance_legal_templates WHERE tenant_id = :tenant_id"),
|
||||
{"tenant_id": tenant_id},
|
||||
).fetchone()
|
||||
total = int(total_row[0] or 0) if total_row else 0
|
||||
|
||||
status_rows = db.execute(text("""
|
||||
SELECT status, COUNT(*) AS cnt
|
||||
FROM compliance_legal_templates
|
||||
WHERE tenant_id = :tenant_id
|
||||
"""), {"tenant_id": tenant_id}).fetchone()
|
||||
GROUP BY status
|
||||
"""), {"tenant_id": tenant_id}).fetchall()
|
||||
by_status: Dict[str, int] = {r[0]: int(r[1] or 0) for r in status_rows}
|
||||
|
||||
if row:
|
||||
d = dict(row._mapping)
|
||||
counts = {k: int(v or 0) for k, v in d.items()}
|
||||
return {
|
||||
"total": counts["total"],
|
||||
"by_status": {
|
||||
"published": counts["published"],
|
||||
"draft": counts["draft"],
|
||||
"archived": counts["archived"],
|
||||
},
|
||||
"by_type": {
|
||||
"privacy_policy": counts["privacy_policy"],
|
||||
"terms_of_service": counts["terms_of_service"],
|
||||
"impressum": counts["impressum"],
|
||||
"data_processing_agreement": counts["data_processing_agreement"],
|
||||
"withdrawal_policy": counts["withdrawal_policy"],
|
||||
"cookie_policy": counts["cookie_policy"],
|
||||
},
|
||||
}
|
||||
return {"total": 0, "by_status": {}, "by_type": {}}
|
||||
type_rows = db.execute(text("""
|
||||
SELECT document_type, COUNT(*) AS cnt
|
||||
FROM compliance_legal_templates
|
||||
WHERE tenant_id = :tenant_id
|
||||
GROUP BY document_type
|
||||
ORDER BY document_type
|
||||
"""), {"tenant_id": tenant_id}).fetchall()
|
||||
by_type: Dict[str, int] = {r[0]: int(r[1] or 0) for r in type_rows}
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"by_status": {
|
||||
"published": by_status.get("published", 0),
|
||||
"draft": by_status.get("draft", 0),
|
||||
"archived": by_status.get("archived", 0),
|
||||
},
|
||||
"by_type": by_type,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/sources")
|
||||
|
||||
291
backend-compliance/migrations/020_legal_templates_new_types.sql
Normal file
291
backend-compliance/migrations/020_legal_templates_new_types.sql
Normal file
@@ -0,0 +1,291 @@
|
||||
-- Migration 020: Legal Templates — Typ-Renames + 11 neue Templates
|
||||
--
|
||||
-- 1. Rename data_processing_agreement → dpa (Frontend-Typ-Alignment)
|
||||
-- 2. Rename withdrawal_policy → widerruf (Frontend-Typ-Alignment)
|
||||
-- 3. 11 neue selbst verfasste Templates (MIT-Lizenz)
|
||||
-- nda (DE+EN), sla (DE), acceptable_use (EN), community_guidelines (DE),
|
||||
-- copyright_policy (DE), cloud_service_agreement (DE), data_usage_clause (DE),
|
||||
-- cookie_banner (DE), agb (DE), clause (EN)
|
||||
--
|
||||
-- Alle Templates: MIT License, self-authored, BreakPilot Compliance 2026
|
||||
|
||||
-- ===========================================================================
|
||||
-- STEP 1: Typ-Renames
|
||||
-- ===========================================================================
|
||||
UPDATE compliance_legal_templates
|
||||
SET document_type = 'dpa',
|
||||
updated_at = NOW()
|
||||
WHERE document_type = 'data_processing_agreement';
|
||||
|
||||
UPDATE compliance_legal_templates
|
||||
SET document_type = 'widerruf',
|
||||
updated_at = NOW()
|
||||
WHERE document_type = 'withdrawal_policy';
|
||||
|
||||
-- ===========================================================================
|
||||
-- STEP 2: 11 neue Templates
|
||||
-- ===========================================================================
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- NDA — Geheimhaltungsvereinbarung (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'nda',
|
||||
'Geheimhaltungsvereinbarung (NDA) — DE',
|
||||
'Einseitige oder gegenseitige Geheimhaltungsvereinbarung nach deutschem Recht.',
|
||||
E'# Geheimhaltungsvereinbarung\n\nZwischen **{{DISCLOSING_PARTY}}** (nachfolgend „Offenbarende Partei") und\n**{{RECEIVING_PARTY}}** (nachfolgend „Empfangende Partei").\n\n## 1. Gegenstand\n\nDie Parteien beabsichtigen eine Zusammenarbeit in folgendem Bereich: {{PURPOSE}}. Im Rahmen dieser Zusammenarbeit kann die Offenbarende Partei vertrauliche Informationen an die Empfangende Partei übermitteln.\n\n## 2. Vertrauliche Informationen\n\nVertrauliche Informationen sind alle Informationen, die als „vertraulich" oder „geheim" gekennzeichnet sind oder die nach dem Kontext ihrer Offenbarung vernünftigerweise als vertraulich betrachtet werden sollten, einschließlich Geschäftsgeheimnisse, technische Daten, Kundenlisten, Preisstrukturen und Geschäftspläne.\n\n## 3. Geheimhaltungspflicht\n\nDie Empfangende Partei verpflichtet sich:\n\na) die vertraulichen Informationen streng vertraulich zu behandeln;\nb) sie ausschließlich zum vereinbarten Zweck zu verwenden;\nc) sie nur an Mitarbeiter weiterzugeben, die diese für den vereinbarten Zweck benötigen;\nd) diese Mitarbeiter in gleichem Umfang zur Geheimhaltung zu verpflichten.\n\n## 4. Ausnahmen\n\nDie Geheimhaltungspflicht gilt nicht für Informationen, die\n\na) der Empfangenden Partei bereits bekannt waren;\nb) allgemein zugänglich sind oder ohne Verschulden der Empfangenden Partei werden;\nc) der Empfangenden Partei von Dritten ohne Geheimhaltungspflicht mitgeteilt wurden;\nd) aufgrund gesetzlicher Pflicht oder behördlicher Anordnung offenbart werden müssen.\n\n## 5. Laufzeit\n\nDiese Vereinbarung gilt ab dem {{EFFECTIVE_DATE}} für einen Zeitraum von {{DURATION_YEARS}} Jahren. Die Verpflichtungen hinsichtlich besonders sensibler Informationen gelten unbefristet.\n\n## 6. Rückgabe von Unterlagen\n\nAuf Anforderung der Offenbarenden Partei sind alle vertraulichen Unterlagen und Kopien unverzüglich zurückzugeben oder zu vernichten.\n\n## 7. Vertragsstrafe\n\nBei schuldhafter Verletzung der Geheimhaltungspflicht ist die Empfangende Partei verpflichtet, eine Vertragsstrafe in Höhe von {{PENALTY_AMOUNT}} EUR zu zahlen. Weitergehende Schadensersatzansprüche bleiben vorbehalten.\n\n## 8. Anwendbares Recht und Gerichtsstand\n\nEs gilt deutsches Recht. Gerichtsstand ist {{JURISDICTION_CITY}}.\n\n---\n\n**{{DISCLOSING_PARTY}}**\nOrt, Datum: ________________________\nUnterschrift: ________________________\n\n**{{RECEIVING_PARTY}}**\nOrt, Datum: ________________________\nUnterschrift: ________________________',
|
||||
'["{{DISCLOSING_PARTY}}", "{{RECEIVING_PARTY}}", "{{PURPOSE}}", "{{EFFECTIVE_DATE}}", "{{DURATION_YEARS}}", "{{PENALTY_AMOUNT}}", "{{JURISDICTION_CITY}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- NDA — Non-Disclosure Agreement (EN)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'nda',
|
||||
'Non-Disclosure Agreement (NDA) — EN (EU)',
|
||||
'Mutual or one-sided NDA for EU-based business relationships.',
|
||||
E'# Non-Disclosure Agreement\n\nBetween **{{DISCLOSING_PARTY}}** ("Disclosing Party") and **{{RECEIVING_PARTY}}** ("Receiving Party"), collectively the "Parties".\n\n## 1. Purpose\n\nThe Parties intend to explore a potential collaboration regarding {{PURPOSE}}. In the course of this collaboration, the Disclosing Party may share Confidential Information with the Receiving Party.\n\n## 2. Confidential Information\n\n"Confidential Information" means any information disclosed by the Disclosing Party that is designated as confidential or that reasonably should be understood to be confidential given the nature of the information and circumstances of disclosure, including but not limited to business plans, technical data, trade secrets, financial information, and customer data.\n\n## 3. Obligations\n\nThe Receiving Party agrees to:\n\na) hold Confidential Information in strict confidence;\nb) use Confidential Information solely for the Purpose;\nc) not disclose Confidential Information to third parties without prior written consent;\nd) protect Confidential Information with at least the same degree of care used to protect its own confidential information, but no less than reasonable care.\n\n## 4. Exceptions\n\nThe obligations above shall not apply to information that:\n\na) is or becomes publicly known without breach of this Agreement;\nb) was rightfully known to the Receiving Party prior to disclosure;\nc) is rightfully received from a third party without restriction;\nd) must be disclosed pursuant to applicable law or court order.\n\n## 5. Term\n\nThis Agreement is effective as of {{EFFECTIVE_DATE}} and shall remain in force for {{DURATION_YEARS}} years thereafter.\n\n## 6. Return of Information\n\nUpon request, the Receiving Party shall promptly return or destroy all Confidential Information.\n\n## 7. Governing Law\n\nThis Agreement shall be governed by the laws of {{GOVERNING_LAW}}. Disputes shall be resolved exclusively in the courts of {{JURISDICTION_CITY}}.\n\n---\n\n**{{DISCLOSING_PARTY}}**\nDate & Place: ________________________\nSignature: ________________________\n\n**{{RECEIVING_PARTY}}**\nDate & Place: ________________________\nSignature: ________________________',
|
||||
'["{{DISCLOSING_PARTY}}", "{{RECEIVING_PARTY}}", "{{PURPOSE}}", "{{EFFECTIVE_DATE}}", "{{DURATION_YEARS}}", "{{GOVERNING_LAW}}", "{{JURISDICTION_CITY}}"]',
|
||||
'en', 'EU',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). No third-party content.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- SLA — Service Level Agreement (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'sla',
|
||||
'Service Level Agreement (SLA) — DE',
|
||||
'SLA für Software-as-a-Service-Dienste nach deutschem Recht.',
|
||||
E'# Service Level Agreement\n\nZwischen **{{SERVICE_PROVIDER}}** (nachfolgend „Anbieter") und **{{CUSTOMER}}** (nachfolgend „Kunde").\n\n## 1. Gegenstand\n\nDieser SLA regelt die Verfügbarkeits- und Qualitätszusagen des Anbieters für den Dienst **{{SERVICE_NAME}}** (nachfolgend „Dienst").\n\n## 2. Verfügbarkeit\n\nDer Anbieter garantiert eine monatliche Systemverfügbarkeit von **{{AVAILABILITY_PERCENT}}%** (gemessen in einem Kalendermonat, exklusive geplanter Wartungsfenster).\n\n### 2.1 Geplante Wartung\n\nGeplante Wartungsarbeiten werden mindestens {{MAINTENANCE_NOTICE_HOURS}} Stunden im Voraus angekündigt und werden, wenn möglich, außerhalb der Geschäftszeiten (Mo–Fr, 08:00–18:00 Uhr MEZ) durchgeführt.\n\n### 2.2 Messung\n\nDie Verfügbarkeit wird durch automatisierte Monitoring-Systeme des Anbieters ermittelt.\n\n## 3. Reaktionszeiten\n\n| Priorität | Beschreibung | Reaktionszeit | Lösungszeit |\n|-----------|-------------|---------------|-------------|\n| Kritisch | Dienst vollständig nicht verfügbar | {{RESPONSE_CRITICAL_H}} Std. | {{RESOLUTION_CRITICAL_H}} Std. |\n| Hoch | Hauptfunktionen eingeschränkt | {{RESPONSE_HIGH_H}} Std. | {{RESOLUTION_HIGH_H}} Std. |\n| Mittel | Nicht-kritische Fehler | {{RESPONSE_MEDIUM_H}} Std. | {{RESOLUTION_MEDIUM_H}} Std. |\n| Niedrig | Fragen, Verbesserungsanfragen | {{RESPONSE_LOW_H}} Std. | – |\n\n## 4. Service Credits\n\nWird die vereinbarte Verfügbarkeit in einem Monat unterschritten, erhält der Kunde Service Credits gemäß folgender Staffel:\n\n| Verfügbarkeit | Service Credit |\n|---------------|----------------|\n| 99,0 % – < {{AVAILABILITY_PERCENT}} % | 10 % der monatlichen Vergütung |\n| 95,0 % – < 99,0 % | 25 % der monatlichen Vergütung |\n| < 95,0 % | 50 % der monatlichen Vergütung |\n\nService Credits müssen innerhalb von 30 Tagen nach dem betroffenen Monat schriftlich beantragt werden.\n\n## 5. Ausschlüsse\n\nVerfügbarkeitsunterbrechungen durch folgende Ursachen werden nicht angerechnet:\n\na) höhere Gewalt;\nb) Handlungen oder Unterlassungen des Kunden;\nc) Probleme außerhalb des Einflussbereichs des Anbieters (Internet-Backbone, Endgeräte des Kunden);\nd) geplante Wartungsfenster.\n\n## 6. Support-Kanäle\n\nDer Support ist erreichbar unter: {{SUPPORT_EMAIL}} / {{SUPPORT_PHONE}} ({{SUPPORT_HOURS}}).\n\n## 7. Laufzeit und Kündigung\n\nDieser SLA gilt ab {{EFFECTIVE_DATE}} und verlängert sich automatisch um jeweils ein Jahr, sofern er nicht mit einer Frist von {{TERMINATION_NOTICE_DAYS}} Tagen gekündigt wird.\n\n## 8. Anwendbares Recht\n\nEs gilt deutsches Recht. Gerichtsstand ist {{JURISDICTION_CITY}}.',
|
||||
'["{{SERVICE_PROVIDER}}", "{{CUSTOMER}}", "{{SERVICE_NAME}}", "{{AVAILABILITY_PERCENT}}", "{{MAINTENANCE_NOTICE_HOURS}}", "{{RESPONSE_CRITICAL_H}}", "{{RESOLUTION_CRITICAL_H}}", "{{RESPONSE_HIGH_H}}", "{{RESOLUTION_HIGH_H}}", "{{RESPONSE_MEDIUM_H}}", "{{RESOLUTION_MEDIUM_H}}", "{{RESPONSE_LOW_H}}", "{{SUPPORT_EMAIL}}", "{{SUPPORT_PHONE}}", "{{SUPPORT_HOURS}}", "{{EFFECTIVE_DATE}}", "{{TERMINATION_NOTICE_DAYS}}", "{{JURISDICTION_CITY}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Acceptable Use Policy (EN)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'acceptable_use',
|
||||
'Acceptable Use Policy (AUP) — EN',
|
||||
'Acceptable Use Policy for online platforms and SaaS services.',
|
||||
E'# Acceptable Use Policy\n\n**{{COMPANY_NAME}}** ("we", "us", "our") operates **{{SERVICE_NAME}}** (the "Service"). This Acceptable Use Policy ("AUP") governs your use of the Service.\n\n**Effective Date:** {{EFFECTIVE_DATE}}\n\n## 1. Permitted Use\n\nYou may use the Service solely for lawful purposes and in accordance with these terms. You agree to comply with all applicable laws and regulations.\n\n## 2. Prohibited Activities\n\nYou must not use the Service to:\n\na) upload, transmit, or distribute content that is unlawful, harmful, defamatory, obscene, or fraudulent;\nb) infringe any intellectual property rights;\nc) distribute malware, viruses, or other harmful code;\nd) conduct unauthorised access, penetration testing, or security probing of third-party systems;\ne) send unsolicited commercial messages (spam);\nf) harass, threaten, or harm other users;\ng) violate privacy rights or process personal data without a lawful basis;\nh) circumvent technical protection measures;\ni) use the Service to operate competing services.\n\n## 3. Content Standards\n\nAll content you upload or share must:\n\n- be accurate and not misleading;\n- comply with applicable data protection law (including GDPR);\n- not violate third-party rights.\n\n## 4. Monitoring\n\n{{COMPANY_NAME}} reserves the right to monitor use of the Service to ensure compliance with this AUP. We may suspend or terminate access for violations without prior notice.\n\n## 5. Reporting Violations\n\nTo report violations of this AUP, contact: {{ABUSE_EMAIL}}\n\n## 6. Consequences of Violations\n\nViolation of this AUP may result in immediate suspension or termination of access, legal action, and liability for damages.\n\n## 7. Changes\n\nWe may update this AUP at any time. Continued use of the Service after changes constitutes acceptance.\n\n## 8. Contact\n\n{{COMPANY_NAME}}\n{{COMPANY_ADDRESS}}\n{{CONTACT_EMAIL}}',
|
||||
'["{{COMPANY_NAME}}", "{{SERVICE_NAME}}", "{{EFFECTIVE_DATE}}", "{{ABUSE_EMAIL}}", "{{COMPANY_ADDRESS}}", "{{CONTACT_EMAIL}}"]',
|
||||
'en', 'EU',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). No third-party content.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Community Guidelines (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'community_guidelines',
|
||||
'Community-Richtlinien — DE',
|
||||
'Community-Richtlinien für Online-Plattformen und Community-Dienste.',
|
||||
E'# Community-Richtlinien\n\nWillkommen in der Community von **{{PLATFORM_NAME}}**! Damit alle Mitglieder sicher und respektvoll miteinander interagieren können, gelten folgende Richtlinien.\n\n**Stand:** {{EFFECTIVE_DATE}}\n\n## 1. Respektvoller Umgang\n\nWir erwarten von allen Mitgliedern einen respektvollen und wertschätzenden Umgang. Diskriminierung, Beleidigung, Belästigung oder Hass jeglicher Art — insbesondere aufgrund von Herkunft, Geschlecht, Religion, sexueller Orientierung, Behinderung oder Alter — sind nicht gestattet.\n\n## 2. Erlaubte Inhalte\n\nBeiträge müssen:\n\n- der Wahrheit entsprechen und nicht irreführend sein;\n- die Rechte Dritter (Urheberrecht, Persönlichkeitsrechte) respektieren;\n- im Einklang mit geltendem Recht stehen.\n\n## 3. Verbotene Inhalte\n\nNicht gestattet sind:\n\na) rechtswidrige, beleidigende, obszöne oder gewaltverherrlichende Inhalte;\nb) Spam oder wiederholte gleichartige Beiträge;\nc) Werbung ohne ausdrückliche Genehmigung;\nd) persönliche Daten anderer Nutzer ohne deren Zustimmung;\ne) Deepfakes oder anderweitig manipulierte Inhalte zum Zweck der Täuschung.\n\n## 4. Sicherheit\n\nDas Teilen von Malware, Phishing-Links oder anderweitig schädlichen Inhalten ist streng verboten.\n\n## 5. Moderation\n\n{{PLATFORM_NAME}} behält sich das Recht vor, Inhalte, die gegen diese Richtlinien verstoßen, ohne Vorankündigung zu entfernen und betroffene Konten zu sperren.\n\n## 6. Melden von Verstößen\n\nVerstöße können unter {{REPORT_EMAIL}} oder über die Melde-Funktion in der Plattform gemeldet werden.\n\n## 7. Änderungen\n\nWir behalten uns vor, diese Richtlinien jederzeit anzupassen. Über wesentliche Änderungen werden Mitglieder informiert.\n\n## 8. Kontakt\n\n{{COMPANY_NAME}}\n{{CONTACT_EMAIL}}',
|
||||
'["{{PLATFORM_NAME}}", "{{EFFECTIVE_DATE}}", "{{REPORT_EMAIL}}", "{{COMPANY_NAME}}", "{{CONTACT_EMAIL}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Copyright Policy / Urheberrechtsrichtlinie (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'copyright_policy',
|
||||
'Urheberrechtsrichtlinie — DE',
|
||||
'Urheberrechtsrichtlinie und DMCA/UrhG-Meldeverfahren.',
|
||||
E'# Urheberrechtsrichtlinie\n\n**{{COMPANY_NAME}}** respektiert geistige Eigentumsrechte und erwartet von allen Nutzern der Plattform **{{PLATFORM_NAME}}** dasselbe.\n\n**Stand:** {{EFFECTIVE_DATE}}\n\n## 1. Eigentumsrechte von {{COMPANY_NAME}}\n\nAlle Inhalte auf dieser Plattform — einschließlich Texte, Grafiken, Logos, Software und Datenbankstrukturen — sind urheberrechtlich geschützt und Eigentum von {{COMPANY_NAME}} oder deren Lizenzgebern. Eine Nutzung ohne ausdrückliche Genehmigung ist untersagt.\n\n## 2. Nutzergenerierte Inhalte\n\nNutzer behalten das Urheberrecht an von ihnen hochgeladenen Inhalten. Mit dem Hochladen räumen Nutzer **{{COMPANY_NAME}}** eine nicht-exklusive, weltweite, kostenlose Lizenz ein, diese Inhalte im Rahmen des Dienstes anzuzeigen und zu verbreiten.\n\n## 3. Meldung von Urheberrechtsverletzungen\n\nWenn Sie glauben, dass Inhalte auf der Plattform Ihre Urheberrechte verletzen, wenden Sie sich bitte schriftlich an:\n\n**{{COPYRIGHT_CONTACT_NAME}}**\nE-Mail: {{COPYRIGHT_EMAIL}}\nAdresse: {{COMPANY_ADDRESS}}\n\nIhre Meldung muss folgende Informationen enthalten:\n\na) Identifikation des geschützten Werks;\nb) Beschreibung und Fundstelle des vermeintlich verletzenden Inhalts;\nc) Ihre Kontaktdaten;\nd) eine Versicherung, dass die Angaben korrekt sind und Sie der Rechteinhaber oder bevollmächtigter Vertreter sind.\n\n## 4. Reaktion auf Meldungen\n\nNach Prüfung einer begründeten Meldung werden wir den gemeldeten Inhalt entfernen oder den Zugang dazu sperren.\n\n## 5. Wiederholungstäter\n\nNutzer, die wiederholt Urheberrechte verletzen, werden dauerhaft gesperrt.\n\n## 6. Kontakt\n\n{{COMPANY_NAME}}\n{{CONTACT_EMAIL}}',
|
||||
'["{{COMPANY_NAME}}", "{{PLATFORM_NAME}}", "{{EFFECTIVE_DATE}}", "{{COPYRIGHT_CONTACT_NAME}}", "{{COPYRIGHT_EMAIL}}", "{{COMPANY_ADDRESS}}", "{{CONTACT_EMAIL}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Cloud Service Agreement / Cloud-Dienstevertrag (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'cloud_service_agreement',
|
||||
'Cloud-Dienstevertrag — DE',
|
||||
'Vertrag über die Erbringung von Cloud-Dienstleistungen (SaaS/PaaS/IaaS) nach deutschem Recht.',
|
||||
E'# Cloud-Dienstevertrag\n\nZwischen **{{PROVIDER_NAME}}** (nachfolgend „Anbieter") und **{{CUSTOMER_NAME}}** (nachfolgend „Kunde").\n\n## 1. Vertragsgegenstand\n\nDer Anbieter stellt dem Kunden den Cloud-Dienst **{{SERVICE_NAME}}** in der gebuchten Leistungsstufe über das Internet zur Verfügung. Der Dienst wird als {{SERVICE_MODEL}} (Software/Platform/Infrastructure-as-a-Service) erbracht.\n\n## 2. Leistungsumfang\n\nDer Leistungsumfang ergibt sich aus der jeweils aktuellen Leistungsbeschreibung und dem Service Level Agreement (SLA) des Anbieters. Die gebuchte Leistungsstufe ist: **{{SERVICE_TIER}}**.\n\n## 3. Vergütung\n\nDer Kunde zahlt die vereinbarte monatliche Vergütung von **{{MONTHLY_FEE}} EUR** (zzgl. gesetzlicher MwSt.) zum {{PAYMENT_DUE_DAY}}. des jeweiligen Monats. Die Abrechnung erfolgt per {{PAYMENT_METHOD}}.\n\n## 4. Datenschutz und Auftragsverarbeitung\n\nSoweit der Anbieter im Rahmen der Leistungserbringung personenbezogene Daten des Kunden verarbeitet, schließen die Parteien einen Auftragsverarbeitungsvertrag (AVV) gemäß Art. 28 DSGVO ab.\n\nDie Daten werden ausschließlich in Rechenzentren innerhalb des {{DATA_LOCATION}} gespeichert.\n\n## 5. Datensicherheit\n\nDer Anbieter trifft technische und organisatorische Maßnahmen (TOM) gemäß Art. 32 DSGVO, um die Sicherheit der Kundendaten zu gewährleisten. Details sind im aktuellen TOM-Dokument festgehalten.\n\n## 6. Verfügbarkeit\n\nDie garantierte monatliche Systemverfügbarkeit beträgt **{{AVAILABILITY_PERCENT}}%**. Einzelheiten regelt das SLA.\n\n## 7. Laufzeit und Kündigung\n\nDer Vertrag beginnt am **{{START_DATE}}** und hat eine Mindestlaufzeit von **{{MIN_TERM_MONTHS}} Monaten**. Nach Ablauf verlängert er sich automatisch um jeweils einen Monat, sofern er nicht mit einer Frist von **{{TERMINATION_NOTICE_DAYS}} Tagen** zum Monatsende gekündigt wird.\n\n## 8. Haftung\n\nDie Haftung des Anbieters ist auf den vorhersehbaren, vertragstypischen Schaden begrenzt. Für leichte Fahrlässigkeit haftet der Anbieter nur bei Verletzung wesentlicher Vertragspflichten.\n\n## 9. Anwendbares Recht\n\nEs gilt deutsches Recht unter Ausschluss des UN-Kaufrechts. Gerichtsstand ist **{{JURISDICTION_CITY}}**.\n\n## 10. Kontakt\n\n{{PROVIDER_NAME}}\n{{PROVIDER_ADDRESS}}\n{{PROVIDER_EMAIL}}',
|
||||
'["{{PROVIDER_NAME}}", "{{CUSTOMER_NAME}}", "{{SERVICE_NAME}}", "{{SERVICE_MODEL}}", "{{SERVICE_TIER}}", "{{MONTHLY_FEE}}", "{{PAYMENT_DUE_DAY}}", "{{PAYMENT_METHOD}}", "{{DATA_LOCATION}}", "{{AVAILABILITY_PERCENT}}", "{{START_DATE}}", "{{MIN_TERM_MONTHS}}", "{{TERMINATION_NOTICE_DAYS}}", "{{JURISDICTION_CITY}}", "{{PROVIDER_ADDRESS}}", "{{PROVIDER_EMAIL}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Data Usage Clause / Datennutzungsklausel (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'data_usage_clause',
|
||||
'Datennutzungsklausel für AGB — DE',
|
||||
'Klausel zur Erhebung und Nutzung von Nutzerdaten, geeignet für AGB und Datenschutzhinweise.',
|
||||
E'# Datennutzungsklausel\n\n## Erhebung und Nutzung von Nutzungsdaten\n\n**{{COMPANY_NAME}}** erhebt im Rahmen der Nutzung von **{{SERVICE_NAME}}** Nutzungsdaten (nachfolgend „Daten") zur Verbesserung des Dienstes und zur Erfüllung vertraglicher Pflichten.\n\n### Welche Daten werden erhoben?\n\nWir erheben folgende Kategorien von Daten:\n\n- **Technische Daten:** IP-Adresse (anonymisiert), Browsertyp, Betriebssystem, Zugriffszeiten\n- **Nutzungsdaten:** aufgerufene Seiten, Klickpfade, Sitzungsdauer, Funktionsnutzung\n- **Gerätedaten:** Gerätetyp, Bildschirmauflösung (nur aggregiert)\n{{ADDITIONAL_DATA_CATEGORIES}}\n\n### Zweck der Verarbeitung\n\nDie erhobenen Daten werden verwendet für:\n\na) den ordnungsgemäßen Betrieb und die Optimierung des Dienstes;\nb) die Analyse von Nutzungsmustern zur Weiterentwicklung;\nc) die Erkennung und Abwehr von Missbrauch und Sicherheitsbedrohungen;\nd) die Erfüllung gesetzlicher Aufbewahrungspflichten.\n\n### Rechtsgrundlage\n\nDie Verarbeitung erfolgt auf Basis von Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung) und Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse) an der Sicherstellung des Dienstbetriebs.\n\n### Speicherdauer\n\nTechnische Protokolldaten werden nach **{{LOG_RETENTION_DAYS}} Tagen** automatisch gelöscht. Aggregierte Analysen ohne Personenbezug werden für **{{ANALYTICS_RETENTION_MONTHS}} Monate** aufbewahrt.\n\n### Weitergabe an Dritte\n\nEine Weitergabe an Dritte erfolgt ausschließlich in folgendem Umfang:\n\n- an Auftragsverarbeiter (z. B. Hosting-Anbieter) auf Basis eines AVV gemäß Art. 28 DSGVO;\n- wenn gesetzlich vorgeschrieben.\n\nEine Übermittlung in Drittländer außerhalb der EU/EWR erfolgt {{DATA_TRANSFER_THIRD_COUNTRIES}}.\n\n### Ihre Rechte\n\nSie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung und Datenübertragbarkeit. Kontakt: **{{PRIVACY_CONTACT_EMAIL}}**.',
|
||||
'["{{COMPANY_NAME}}", "{{SERVICE_NAME}}", "{{ADDITIONAL_DATA_CATEGORIES}}", "{{LOG_RETENTION_DAYS}}", "{{ANALYTICS_RETENTION_MONTHS}}", "{{DATA_TRANSFER_THIRD_COUNTRIES}}", "{{PRIVACY_CONTACT_EMAIL}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, FALSE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Cookie Banner Text (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'cookie_banner',
|
||||
'Cookie-Banner Texte (DSGVO-konform) — DE',
|
||||
'Texte für Cookie-Einwilligungs-Banner gemäß DSGVO und ePrivacy-Richtlinie.',
|
||||
E'# Cookie-Banner Texte\n\n## Haupttext (Kurz)\n\nWir verwenden Cookies und ähnliche Technologien, um Ihnen die bestmögliche Nutzungserfahrung auf **{{WEBSITE_NAME}}** zu bieten und unsere Dienste zu verbessern.\n\nMit Klick auf **„Alle akzeptieren"** stimmen Sie der Verwendung aller Cookies zu. Über **„Einstellungen"** können Sie Ihre Präferenzen individuell anpassen.\n\nMehr Informationen in unserer [Cookie-Richtlinie]({{COOKIE_POLICY_URL}}) und [Datenschutzerklärung]({{PRIVACY_POLICY_URL}}).\n\n---\n\n## Kategorien-Beschreibungen\n\n### Notwendig (immer aktiv)\n\nDiese Cookies sind für den Betrieb der Website unbedingt erforderlich. Sie ermöglichen grundlegende Funktionen wie Seitennavigation und Zugriff auf geschützte Bereiche. Die Website kann ohne diese Cookies nicht ordnungsgemäß funktionieren.\n\n### Funktional\n\nFunktionale Cookies ermöglichen es der Website, erweiterte Funktionalität und Personalisierung anzubieten, z. B. das Merken Ihrer Sprachpräferenz oder Ihres Warenkorbs.\n\n### Analyse\n\nAnalyse-Cookies helfen uns zu verstehen, wie Besucher mit unserer Website interagieren. Alle Daten werden anonymisiert erhoben. Wir verwenden: {{ANALYTICS_TOOLS}}.\n\n### Marketing\n\nMarketing-Cookies werden verwendet, um Besuchern relevante Werbung zu zeigen. Sie werden eingesetzt von: {{MARKETING_PARTNERS}}.\n\n---\n\n## Button-Texte\n\n- **Alle akzeptieren**\n- **Nur notwendige Cookies**\n- **Einstellungen anpassen**\n- **Auswahl bestätigen**\n- **Ablehnen**\n\n---\n\n## Einstellungs-Overlay Titel\n\n**Cookie-Einstellungen** — Datenschutz ist uns wichtig. Passen Sie hier Ihre Cookie-Präferenzen an. Notwendige Cookies können nicht deaktiviert werden.\n\n## Wiederaufruf-Hinweis\n\nSie können Ihre Cookie-Einstellungen jederzeit über den Link **„Cookie-Einstellungen"** im Footer dieser Website ändern.\n\n**{{COMPANY_NAME}}** | {{CONTACT_EMAIL}} | {{PRIVACY_POLICY_URL}}',
|
||||
'["{{WEBSITE_NAME}}", "{{COOKIE_POLICY_URL}}", "{{PRIVACY_POLICY_URL}}", "{{ANALYTICS_TOOLS}}", "{{MARKETING_PARTNERS}}", "{{COMPANY_NAME}}", "{{CONTACT_EMAIL}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, FALSE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- AGB — Allgemeine Geschäftsbedingungen (DE)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'agb',
|
||||
'AGB — Allgemeine Geschäftsbedingungen (DE)',
|
||||
'Vollständige AGB für B2B/B2C SaaS- und Online-Dienste nach deutschem Recht.',
|
||||
E'# Allgemeine Geschäftsbedingungen\n\n**{{COMPANY_NAME}}**, {{COMPANY_ADDRESS}}\n\n**Stand:** {{VERSION_DATE}}\n\n## § 1 Geltungsbereich\n\n(1) Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für alle Verträge zwischen **{{COMPANY_NAME}}** (nachfolgend „Anbieter") und den Nutzern (nachfolgend „Kunde") über die Nutzung des Dienstes **{{SERVICE_NAME}}**.\n\n(2) Abweichende Bedingungen des Kunden gelten nur, wenn der Anbieter ihrer Geltung ausdrücklich schriftlich zugestimmt hat.\n\n## § 2 Leistungsbeschreibung\n\nDer Anbieter stellt dem Kunden **{{SERVICE_DESCRIPTION}}** bereit. Der genaue Leistungsumfang ergibt sich aus der jeweils aktuellen Leistungsbeschreibung auf der Website des Anbieters.\n\n## § 3 Vertragsschluss\n\nDer Vertrag kommt durch Registrierung des Kunden und Aktivierung des Kundenkontos durch den Anbieter zustande.\n\n## § 4 Preise und Zahlung\n\n(1) Es gelten die zum Zeitpunkt der Bestellung auf der Website ausgewiesenen Preise.\n\n(2) {{PRICING_SECTION}}\n\n(3) Rechnungen werden elektronisch übermittelt. Die Zahlung ist innerhalb von **{{PAYMENT_DAYS}} Tagen** nach Rechnungsdatum fällig. Bei Zahlungsverzug werden Verzugszinsen in Höhe von 9 Prozentpunkten über dem Basiszinssatz berechnet.\n\n## § 5 Pflichten des Kunden\n\nDer Kunde ist verpflichtet:\n\na) nur wahrheitsgemäße Registrierungsdaten anzugeben;\nb) Zugangsdaten geheim zu halten;\nc) den Dienst nur im Rahmen der vereinbarten Nutzungsrechte zu verwenden;\nd) die Nutzungsbedingungen und anwendbares Recht einzuhalten.\n\n## § 6 Nutzungsrechte\n\nDer Anbieter räumt dem Kunden ein nicht-ausschließliches, nicht übertragbares Nutzungsrecht an dem Dienst für die Vertragslaufzeit ein. Eine Weitervermietung oder Übertragung an Dritte ist ohne schriftliche Zustimmung nicht erlaubt.\n\n## § 7 Datenschutz\n\nDie Erhebung und Verarbeitung personenbezogener Daten erfolgt gemäß der Datenschutzerklärung des Anbieters unter {{PRIVACY_POLICY_URL}}.\n\n## § 8 Haftung\n\n(1) Der Anbieter haftet unbeschränkt für Vorsatz und grobe Fahrlässigkeit.\n\n(2) Bei leichter Fahrlässigkeit haftet der Anbieter nur bei Verletzung wesentlicher Vertragspflichten. Die Haftung ist in diesem Fall auf den vertragstypischen, vorhersehbaren Schaden begrenzt.\n\n(3) Die Haftung für mittelbare Schäden und entgangenen Gewinn ist ausgeschlossen, soweit gesetzlich zulässig.\n\n## § 9 Laufzeit und Kündigung\n\n(1) Der Vertrag wird auf unbestimmte Zeit geschlossen. Er kann von beiden Seiten mit einer Frist von **{{TERMINATION_NOTICE_DAYS}} Tagen** zum Monatsende gekündigt werden.\n\n(2) Das Recht zur außerordentlichen Kündigung aus wichtigem Grund bleibt unberührt.\n\n## § 10 Änderungen der AGB\n\nDer Anbieter behält sich vor, diese AGB mit einer Ankündigungsfrist von **30 Tagen** zu ändern. Widerspricht der Kunde nicht innerhalb dieser Frist, gelten die neuen AGB als angenommen.\n\n## § 11 Schlussbestimmungen\n\nEs gilt deutsches Recht unter Ausschluss des UN-Kaufrechts. Gerichtsstand für kaufmännische Kunden ist **{{JURISDICTION_CITY}}**. Sollten einzelne Bestimmungen dieser AGB unwirksam sein, bleibt die Wirksamkeit der übrigen Bestimmungen unberührt.',
|
||||
'["{{COMPANY_NAME}}", "{{COMPANY_ADDRESS}}", "{{VERSION_DATE}}", "{{SERVICE_NAME}}", "{{SERVICE_DESCRIPTION}}", "{{PRICING_SECTION}}", "{{PAYMENT_DAYS}}", "{{PRIVACY_POLICY_URL}}", "{{TERMINATION_NOTICE_DAYS}}", "{{JURISDICTION_CITY}}"]',
|
||||
'de', 'DE',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, TRUE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). Kein Drittquellen-Material.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Liability Limitation Clause (EN)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO compliance_legal_templates (
|
||||
tenant_id, document_type, title, description, content,
|
||||
placeholders, language, jurisdiction,
|
||||
license_id, license_name, source_name,
|
||||
attribution_required, is_complete_document,
|
||||
version, status, attribution_text, inspiration_sources
|
||||
) VALUES (
|
||||
'9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
|
||||
'clause',
|
||||
'Limitation of Liability Clause — EN',
|
||||
'Standard limitation of liability clause for contracts and terms of service.',
|
||||
E'# Limitation of Liability\n\n## {{CLAUSE_NUMBER}}. Limitation of Liability\n\n### {{CLAUSE_NUMBER}}.1 General\n\nTo the maximum extent permitted by applicable law, **{{COMPANY_NAME}}** and its affiliates, officers, employees, agents, and licensors shall not be liable for:\n\na) any indirect, incidental, special, consequential, or punitive damages;\nb) any loss of profits, revenue, data, goodwill, or other intangible losses;\nc) any damages arising out of or related to your use or inability to use **{{SERVICE_NAME}}**;\n\nwhether based on warranty, contract, tort (including negligence), statute, or any other legal theory, and whether or not **{{COMPANY_NAME}}** has been informed of the possibility of such damage.\n\n### {{CLAUSE_NUMBER}}.2 Cap on Liability\n\nThe aggregate liability of **{{COMPANY_NAME}}** to you for all claims arising out of or related to your use of **{{SERVICE_NAME}}** shall not exceed the greater of:\n\na) the amounts you have paid to **{{COMPANY_NAME}}** in the **{{LIABILITY_PERIOD}}** months preceding the claim; or\nb) **{{MINIMUM_LIABILITY_AMOUNT}} EUR**.\n\n### {{CLAUSE_NUMBER}}.3 Essential Basis\n\nThe parties acknowledge that the limitations of liability in this section reflect a reasonable allocation of risk and are an essential element of the basis of the bargain between the parties. **{{COMPANY_NAME}}** would not provide **{{SERVICE_NAME}}** without these limitations.\n\n### {{CLAUSE_NUMBER}}.4 Exceptions\n\nNothing in this clause shall limit or exclude liability for:\n\na) death or personal injury caused by negligence;\nb) fraud or fraudulent misrepresentation;\nc) any other liability that cannot be excluded or limited by applicable law.\n\n*Note: In Germany and the EU, consumer rights cannot be waived and certain limitations may not apply to consumers (Verbraucher).*',
|
||||
'["{{CLAUSE_NUMBER}}", "{{COMPANY_NAME}}", "{{SERVICE_NAME}}", "{{LIABILITY_PERIOD}}", "{{MINIMUM_LIABILITY_AMOUNT}}"]',
|
||||
'en', 'EU',
|
||||
'mit', 'MIT License', 'BreakPilot Compliance',
|
||||
FALSE, FALSE,
|
||||
'1.0.0', 'published',
|
||||
'Self-authored by BreakPilot Compliance (MIT License, 2026). No third-party content.',
|
||||
'[{"source": "Self-authored", "license": "MIT", "year": 2026}]'
|
||||
);
|
||||
@@ -121,19 +121,35 @@ class TestLegalTemplateSchemas:
|
||||
assert d == {"status": "archived", "title": "Neue DSE"}
|
||||
|
||||
def test_valid_document_types_constant(self):
|
||||
"""VALID_DOCUMENT_TYPES contains all 6 expected types."""
|
||||
"""VALID_DOCUMENT_TYPES contains all 16 expected types (post-Migration 020)."""
|
||||
# Original types
|
||||
assert "privacy_policy" in VALID_DOCUMENT_TYPES
|
||||
assert "terms_of_service" in VALID_DOCUMENT_TYPES
|
||||
assert "impressum" in VALID_DOCUMENT_TYPES
|
||||
assert "data_processing_agreement" in VALID_DOCUMENT_TYPES
|
||||
assert "withdrawal_policy" in VALID_DOCUMENT_TYPES
|
||||
assert "cookie_policy" in VALID_DOCUMENT_TYPES
|
||||
assert len(VALID_DOCUMENT_TYPES) == 6
|
||||
# Renamed types (Migration 020)
|
||||
assert "dpa" in VALID_DOCUMENT_TYPES
|
||||
assert "widerruf" in VALID_DOCUMENT_TYPES
|
||||
# New types (Migration 020)
|
||||
assert "nda" in VALID_DOCUMENT_TYPES
|
||||
assert "sla" in VALID_DOCUMENT_TYPES
|
||||
assert "acceptable_use" in VALID_DOCUMENT_TYPES
|
||||
assert "community_guidelines" in VALID_DOCUMENT_TYPES
|
||||
assert "copyright_policy" in VALID_DOCUMENT_TYPES
|
||||
assert "cloud_service_agreement" in VALID_DOCUMENT_TYPES
|
||||
assert "data_usage_clause" in VALID_DOCUMENT_TYPES
|
||||
assert "cookie_banner" in VALID_DOCUMENT_TYPES
|
||||
assert "agb" in VALID_DOCUMENT_TYPES
|
||||
assert "clause" in VALID_DOCUMENT_TYPES
|
||||
assert len(VALID_DOCUMENT_TYPES) == 16
|
||||
# Old names must NOT be present after rename
|
||||
assert "data_processing_agreement" not in VALID_DOCUMENT_TYPES
|
||||
assert "withdrawal_policy" not in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_create_schema_attribution_fields(self):
|
||||
"""LegalTemplateCreate accepts attribution fields."""
|
||||
"""LegalTemplateCreate accepts attribution fields (uses dpa, post-Migration 020)."""
|
||||
payload = LegalTemplateCreate(
|
||||
document_type="data_processing_agreement",
|
||||
document_type="dpa",
|
||||
title="AVV Test",
|
||||
content="# AVV\n{{CONTROLLER_NAME}}",
|
||||
source_url="https://github.com/example/legal",
|
||||
@@ -302,23 +318,38 @@ class TestLegalTemplateSearch:
|
||||
assert res.status_code == 404
|
||||
|
||||
def test_status_endpoint_returns_200(self):
|
||||
"""GET /legal-templates/status returns count structure with all 6 types."""
|
||||
row = MagicMock()
|
||||
row._mapping = {
|
||||
"total": 9, "published": 9, "draft": 0, "archived": 0,
|
||||
"privacy_policy": 2, "terms_of_service": 2, "impressum": 1,
|
||||
"data_processing_agreement": 2, "withdrawal_policy": 1, "cookie_policy": 1,
|
||||
}
|
||||
self.db.execute.return_value.fetchone.return_value = row
|
||||
"""GET /legal-templates/status returns dynamic count structure."""
|
||||
# Mock 3 sequential execute calls: total, by_status, by_type
|
||||
total_mock = MagicMock()
|
||||
total_mock.__getitem__ = lambda self, i: 20
|
||||
|
||||
status_rows = [
|
||||
MagicMock(**{"__getitem__": lambda s, i: ["published", 18][i]}),
|
||||
MagicMock(**{"__getitem__": lambda s, i: ["draft", 2][i]}),
|
||||
]
|
||||
type_rows = [
|
||||
MagicMock(**{"__getitem__": lambda s, i: ["privacy_policy", 2][i]}),
|
||||
MagicMock(**{"__getitem__": lambda s, i: ["dpa", 2][i]}),
|
||||
MagicMock(**{"__getitem__": lambda s, i: ["nda", 2][i]}),
|
||||
MagicMock(**{"__getitem__": lambda s, i: ["widerruf", 1][i]}),
|
||||
]
|
||||
|
||||
call1 = MagicMock(); call1.fetchone.return_value = total_mock
|
||||
call2 = MagicMock(); call2.fetchall.return_value = status_rows
|
||||
call3 = MagicMock(); call3.fetchall.return_value = type_rows
|
||||
self.db.execute.side_effect = [call1, call2, call3]
|
||||
|
||||
res = self.client.get("/legal-templates/status")
|
||||
assert res.status_code == 200
|
||||
data = res.json()
|
||||
assert data["total"] == 9
|
||||
assert "total" in data
|
||||
assert "by_type" in data
|
||||
assert "by_status" in data
|
||||
assert "data_processing_agreement" in data["by_type"]
|
||||
assert "withdrawal_policy" in data["by_type"]
|
||||
assert "cookie_policy" in data["by_type"]
|
||||
assert "dpa" in data["by_type"]
|
||||
assert "nda" in data["by_type"]
|
||||
# Old names must not appear (they were renamed)
|
||||
assert "data_processing_agreement" not in data["by_type"]
|
||||
assert "withdrawal_policy" not in data["by_type"]
|
||||
|
||||
def test_sources_endpoint_returns_list(self):
|
||||
"""GET /legal-templates/sources returns sources list."""
|
||||
@@ -396,7 +427,7 @@ class TestLegalTemplateSeed:
|
||||
def test_dpa_template_has_controller_processor_placeholders(self):
|
||||
"""AVV/DPA template has CONTROLLER_NAME and PROCESSOR_NAME placeholders."""
|
||||
row = make_template_row({
|
||||
"document_type": "data_processing_agreement",
|
||||
"document_type": "dpa",
|
||||
"placeholders": [
|
||||
"{{CONTROLLER_NAME}}", "{{CONTROLLER_ADDRESS}}", "{{CONTROLLER_CITY}}",
|
||||
"{{PROCESSOR_NAME}}", "{{PROCESSOR_ADDRESS}}", "{{PROCESSOR_CITY}}",
|
||||
@@ -412,7 +443,7 @@ class TestLegalTemplateSeed:
|
||||
def test_withdrawal_policy_has_public_domain_license(self):
|
||||
"""Widerrufsbelehrung uses Public Domain license (based on §5 UrhG source)."""
|
||||
row = make_template_row({
|
||||
"document_type": "withdrawal_policy",
|
||||
"document_type": "widerruf",
|
||||
"license_id": "public_domain",
|
||||
"license_name": "Public Domain / Gemeinfrei",
|
||||
"source_url": "https://www.gesetze-im-internet.de/egbgb/anlage_1.html",
|
||||
@@ -420,7 +451,7 @@ class TestLegalTemplateSeed:
|
||||
"inspiration_sources": [{"source": "EGBGB Anlage 1", "license": "Public Domain gemäß §5 UrhG"}],
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "withdrawal_policy"
|
||||
assert result["document_type"] == "widerruf"
|
||||
assert result["license_id"] == "public_domain"
|
||||
assert result["source_url"] == "https://www.gesetze-im-internet.de/egbgb/anlage_1.html"
|
||||
assert result["attribution_text"] is not None
|
||||
@@ -455,10 +486,201 @@ class TestLegalTemplateSeed:
|
||||
"""cookie_policy is accepted as valid document_type."""
|
||||
assert "cookie_policy" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_withdrawal_policy_type_valid(self):
|
||||
"""withdrawal_policy is accepted as valid document_type."""
|
||||
assert "withdrawal_policy" in VALID_DOCUMENT_TYPES
|
||||
def test_widerruf_type_valid(self):
|
||||
"""widerruf is accepted as valid document_type (renamed from withdrawal_policy)."""
|
||||
assert "widerruf" in VALID_DOCUMENT_TYPES
|
||||
assert "withdrawal_policy" not in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_dpa_type_valid(self):
|
||||
"""data_processing_agreement is accepted as valid document_type."""
|
||||
assert "data_processing_agreement" in VALID_DOCUMENT_TYPES
|
||||
"""dpa is accepted as valid document_type (renamed from data_processing_agreement)."""
|
||||
assert "dpa" in VALID_DOCUMENT_TYPES
|
||||
assert "data_processing_agreement" not in VALID_DOCUMENT_TYPES
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TestLegalTemplateNewTypes
|
||||
# =============================================================================
|
||||
|
||||
class TestLegalTemplateNewTypes:
|
||||
"""Validate new document types added in Migration 020."""
|
||||
|
||||
def test_all_16_types_present(self):
|
||||
"""VALID_DOCUMENT_TYPES has exactly 16 entries."""
|
||||
assert len(VALID_DOCUMENT_TYPES) == 16
|
||||
|
||||
def test_new_types_are_valid(self):
|
||||
"""All Migration 020 new types are accepted."""
|
||||
new_types = ["nda", "sla", "acceptable_use", "community_guidelines",
|
||||
"copyright_policy", "cloud_service_agreement",
|
||||
"data_usage_clause", "cookie_banner", "agb", "clause"]
|
||||
for t in new_types:
|
||||
assert t in VALID_DOCUMENT_TYPES, f"Type '{t}' missing from VALID_DOCUMENT_TYPES"
|
||||
|
||||
def test_nda_template_has_parties_placeholders(self):
|
||||
"""NDA template has DISCLOSING_PARTY and RECEIVING_PARTY placeholders."""
|
||||
row = make_template_row({
|
||||
"document_type": "nda",
|
||||
"title": "Geheimhaltungsvereinbarung (NDA) — DE",
|
||||
"language": "de",
|
||||
"placeholders": [
|
||||
"{{DISCLOSING_PARTY}}", "{{RECEIVING_PARTY}}", "{{PURPOSE}}",
|
||||
"{{EFFECTIVE_DATE}}", "{{DURATION_YEARS}}", "{{PENALTY_AMOUNT}}",
|
||||
"{{JURISDICTION_CITY}}"
|
||||
],
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "nda"
|
||||
assert "{{DISCLOSING_PARTY}}" in result["placeholders"]
|
||||
assert "{{RECEIVING_PARTY}}" in result["placeholders"]
|
||||
assert "{{PURPOSE}}" in result["placeholders"]
|
||||
|
||||
def test_nda_en_template_is_valid(self):
|
||||
"""NDA EN template has EU jurisdiction and English language."""
|
||||
row = make_template_row({
|
||||
"document_type": "nda",
|
||||
"title": "Non-Disclosure Agreement (NDA) — EN (EU)",
|
||||
"language": "en",
|
||||
"jurisdiction": "EU",
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "nda"
|
||||
assert result["language"] == "en"
|
||||
assert result["jurisdiction"] == "EU"
|
||||
|
||||
def test_sla_template_has_availability_placeholder(self):
|
||||
"""SLA template includes AVAILABILITY_PERCENT placeholder."""
|
||||
row = make_template_row({
|
||||
"document_type": "sla",
|
||||
"placeholders": [
|
||||
"{{SERVICE_PROVIDER}}", "{{CUSTOMER}}", "{{SERVICE_NAME}}",
|
||||
"{{AVAILABILITY_PERCENT}}", "{{SUPPORT_EMAIL}}", "{{JURISDICTION_CITY}}"
|
||||
],
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "sla"
|
||||
assert "{{AVAILABILITY_PERCENT}}" in result["placeholders"]
|
||||
|
||||
def test_acceptable_use_type_valid(self):
|
||||
"""acceptable_use is a valid document type."""
|
||||
assert "acceptable_use" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_acceptable_use_template_structure(self):
|
||||
"""AUP template has required placeholders."""
|
||||
row = make_template_row({
|
||||
"document_type": "acceptable_use",
|
||||
"language": "en",
|
||||
"jurisdiction": "EU",
|
||||
"placeholders": [
|
||||
"{{COMPANY_NAME}}", "{{SERVICE_NAME}}", "{{EFFECTIVE_DATE}}",
|
||||
"{{ABUSE_EMAIL}}", "{{COMPANY_ADDRESS}}", "{{CONTACT_EMAIL}}"
|
||||
],
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "acceptable_use"
|
||||
assert "{{ABUSE_EMAIL}}" in result["placeholders"]
|
||||
|
||||
def test_community_guidelines_type_valid(self):
|
||||
"""community_guidelines is a valid document type."""
|
||||
assert "community_guidelines" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_agb_type_valid(self):
|
||||
"""agb is a valid document type (German AGB, distinct from terms_of_service)."""
|
||||
assert "agb" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_agb_template_has_pricing_and_termination(self):
|
||||
"""AGB template has PRICING_SECTION and TERMINATION_NOTICE_DAYS placeholders."""
|
||||
row = make_template_row({
|
||||
"document_type": "agb",
|
||||
"placeholders": [
|
||||
"{{COMPANY_NAME}}", "{{COMPANY_ADDRESS}}", "{{VERSION_DATE}}",
|
||||
"{{SERVICE_NAME}}", "{{SERVICE_DESCRIPTION}}", "{{PRICING_SECTION}}",
|
||||
"{{PAYMENT_DAYS}}", "{{PRIVACY_POLICY_URL}}",
|
||||
"{{TERMINATION_NOTICE_DAYS}}", "{{JURISDICTION_CITY}}"
|
||||
],
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "agb"
|
||||
assert "{{PRICING_SECTION}}" in result["placeholders"]
|
||||
assert "{{TERMINATION_NOTICE_DAYS}}" in result["placeholders"]
|
||||
|
||||
def test_clause_type_is_modular(self):
|
||||
"""clause document type can be a non-complete document (is_complete_document=False)."""
|
||||
row = make_template_row({
|
||||
"document_type": "clause",
|
||||
"is_complete_document": False,
|
||||
"language": "en",
|
||||
})
|
||||
result = _row_to_dict(row)
|
||||
assert result["document_type"] == "clause"
|
||||
assert result["is_complete_document"] is False
|
||||
|
||||
def test_cloud_service_agreement_type_valid(self):
|
||||
"""cloud_service_agreement is a valid document type."""
|
||||
assert "cloud_service_agreement" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_data_usage_clause_is_non_complete(self):
|
||||
"""data_usage_clause is typically a clause (non-complete document)."""
|
||||
assert "data_usage_clause" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_cookie_banner_type_valid(self):
|
||||
"""cookie_banner is a valid document type."""
|
||||
assert "cookie_banner" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_copyright_policy_type_valid(self):
|
||||
"""copyright_policy is a valid document type."""
|
||||
assert "copyright_policy" in VALID_DOCUMENT_TYPES
|
||||
|
||||
def test_create_nda_with_new_type(self):
|
||||
"""POST /legal-templates accepts nda as document_type."""
|
||||
from fastapi.testclient import TestClient
|
||||
db = MagicMock()
|
||||
app2 = __import__('fastapi', fromlist=['FastAPI']).FastAPI()
|
||||
app2.include_router(router)
|
||||
from classroom_engine.database import get_db
|
||||
app2.dependency_overrides[get_db] = lambda: db
|
||||
|
||||
row = make_template_row({"document_type": "nda", "title": "NDA Test"})
|
||||
db.execute.return_value.fetchone.return_value = row
|
||||
db.commit = MagicMock()
|
||||
|
||||
client = TestClient(app2)
|
||||
res = client.post("/legal-templates", json={
|
||||
"document_type": "nda",
|
||||
"title": "NDA Test",
|
||||
"content": "# NDA\n{{DISCLOSING_PARTY}}"
|
||||
})
|
||||
assert res.status_code == 201
|
||||
|
||||
def test_create_with_old_dpa_name_rejected(self):
|
||||
"""POST /legal-templates rejects data_processing_agreement (old name)."""
|
||||
from fastapi.testclient import TestClient
|
||||
db = MagicMock()
|
||||
app2 = __import__('fastapi', fromlist=['FastAPI']).FastAPI()
|
||||
app2.include_router(router)
|
||||
from classroom_engine.database import get_db
|
||||
app2.dependency_overrides[get_db] = lambda: db
|
||||
|
||||
client = TestClient(app2)
|
||||
res = client.post("/legal-templates", json={
|
||||
"document_type": "data_processing_agreement",
|
||||
"title": "AVV Old Name",
|
||||
"content": "# AVV"
|
||||
})
|
||||
assert res.status_code == 400
|
||||
|
||||
def test_create_with_old_withdrawal_policy_name_rejected(self):
|
||||
"""POST /legal-templates rejects withdrawal_policy (old name)."""
|
||||
from fastapi.testclient import TestClient
|
||||
db = MagicMock()
|
||||
app2 = __import__('fastapi', fromlist=['FastAPI']).FastAPI()
|
||||
app2.include_router(router)
|
||||
from classroom_engine.database import get_db
|
||||
app2.dependency_overrides[get_db] = lambda: db
|
||||
|
||||
client = TestClient(app2)
|
||||
res = client.post("/legal-templates", json={
|
||||
"document_type": "withdrawal_policy",
|
||||
"title": "Widerruf Old Name",
|
||||
"content": "# Widerruf"
|
||||
})
|
||||
assert res.status_code == 400
|
||||
|
||||
49
scripts/apply_legal_templates_020_migration.sh
Executable file
49
scripts/apply_legal_templates_020_migration.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
# Apply Migration 020: Legal Templates — Typ-Renames + 11 neue Templates
|
||||
set -e
|
||||
|
||||
CONTAINER="bp-compliance-backend"
|
||||
MIGRATION_FILE="backend-compliance/migrations/020_legal_templates_new_types.sql"
|
||||
REPO="/Users/benjaminadmin/Projekte/breakpilot-compliance"
|
||||
|
||||
echo "=== Migration 020: Legal Templates Typ-Renames + neue Templates ==="
|
||||
|
||||
# 1. Copy migration file to container
|
||||
echo "[1/3] Kopiere Migration in Container..."
|
||||
ssh macmini "/usr/local/bin/docker cp ${REPO}/${MIGRATION_FILE} ${CONTAINER}:/tmp/020_legal_templates_new_types.sql"
|
||||
|
||||
# 2. Execute migration
|
||||
echo "[2/3] Fuehre Migration aus..."
|
||||
ssh macmini "/usr/local/bin/docker exec ${CONTAINER} bash -c '
|
||||
python3 -c \"
|
||||
import sys
|
||||
sys.path.insert(0, \\\"/app\\\")
|
||||
from classroom_engine.database import engine
|
||||
with engine.connect() as conn:
|
||||
with open(\\\"/tmp/020_legal_templates_new_types.sql\\\") as f:
|
||||
sql = f.read()
|
||||
conn.execute(__import__(\\\"sqlalchemy\\\").text(sql))
|
||||
conn.commit()
|
||||
print(\\\"Migration 020 erfolgreich ausgefuehrt\\\")
|
||||
\"
|
||||
'"
|
||||
|
||||
# 3. Verify
|
||||
echo "[3/3] Verifiziere..."
|
||||
ssh macmini "/usr/local/bin/docker exec ${CONTAINER} bash -c '
|
||||
python3 -c \"
|
||||
import sys
|
||||
sys.path.insert(0, \\\"/app\\\")
|
||||
from classroom_engine.database import engine
|
||||
from sqlalchemy import text
|
||||
with engine.connect() as conn:
|
||||
rows = conn.execute(text(\\\"SELECT document_type, COUNT(*) as cnt FROM compliance_legal_templates GROUP BY document_type ORDER BY document_type\\\")).fetchall()
|
||||
print(\\\"Typen in DB:\\\")
|
||||
for r in rows:
|
||||
print(f\\\" {r[0]}: {r[1]} Template(s)\\\")
|
||||
total = conn.execute(text(\\\"SELECT COUNT(*) FROM compliance_legal_templates\\\")).scalar()
|
||||
print(f\\\"Gesamt: {total} Templates\\\")
|
||||
\"
|
||||
'"
|
||||
|
||||
echo "=== Migration 020 abgeschlossen ==="
|
||||
Reference in New Issue
Block a user