From c28146407108780249e036e4450751a2cca92239 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 21 May 2026 17:31:37 +0200 Subject: [PATCH] feat(audit): P71 JC-vs-AVV Entscheidungsbaum jc_avv_decision.py: detect_ambiguous_jc_avv prueft ob DSE-Text sowohl JC-Signale (gemeinsame Auswertung, Schwesterunternehmen, Konzern...) als auch AVV-Signale (Auftragsverarbeiter, weisungsgebunden...) enthaelt. Bei Treffer rendert build_jc_avv_decision_html einen Block mit 4 EDPB- basierten Leitfragen + jeweiliger Empfehlung. Quellen: EDPB Guidelines 7/2020, EuGH C-25/17, C-40/17. In Mail-Render zwischen Solutions-Block und VVT eingehaengt. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../api/agent_compliance_check_routes.py | 11 ++ .../compliance/services/jc_avv_decision.py | 116 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 backend-compliance/compliance/services/jc_avv_decision.py diff --git a/backend-compliance/compliance/api/agent_compliance_check_routes.py b/backend-compliance/compliance/api/agent_compliance_check_routes.py index 7f3fe20a..a514711f 100644 --- a/backend-compliance/compliance/api/agent_compliance_check_routes.py +++ b/backend-compliance/compliance/api/agent_compliance_check_routes.py @@ -1220,6 +1220,16 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest): except Exception as e: logger.warning("P73 MC-Solution-Generator skipped: %s", e) + # P71: JC-vs-AVV Entscheidungsbaum (nur wenn DSE ambig) + jc_decision_html = "" + try: + from compliance.services.jc_avv_decision import ( + build_jc_avv_decision_html, + ) + jc_decision_html = build_jc_avv_decision_html(doc_texts.get("dse")) + except Exception as e: + logger.warning("P71 jc_avv_decision skipped: %s", e) + # P82: GF-1-Pager ganz oben in der Mail — 5-Bullet-Zusammenfassung # damit die GF nicht 124k Char lesen muss. gf_one_pager_html = "" @@ -1285,6 +1295,7 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest): + scorecard_html + redundancy_html + providers_html + banner_deep_html + library_mismatch_html + consistency_html + signals_html + solutions_html + + jc_decision_html + vvt_html + report_html ) diff --git a/backend-compliance/compliance/services/jc_avv_decision.py b/backend-compliance/compliance/services/jc_avv_decision.py new file mode 100644 index 00000000..599f3e84 --- /dev/null +++ b/backend-compliance/compliance/services/jc_avv_decision.py @@ -0,0 +1,116 @@ +""" +P71 — JC-vs-AVV Entscheidungsbaum. + +Hilft dem Nutzer zu bestimmen, ob ein bestimmtes Verarbeitungsverhaeltnis +gemeinsame Verantwortlichkeit (Art. 26 DSGVO) oder Auftragsverarbeitung +(Art. 28 DSGVO) ist. EDPB 7/2020 ist die Grundlage. + +Wird gerendert als kleiner Block am Ende der Mail, wenn im DSE-Text +Konstrukte vorkommen die ambivalent sind (z.B. 'gemeinsame Auswertung +mit Schwesterunternehmen', 'gemeinsame Plattform-Nutzung'). Liefert +3-4 Leitfragen + jeweilige Empfehlung. +""" + +from __future__ import annotations + +import logging + +logger = logging.getLogger(__name__) + +_JC_SIGNALS = ( + "schwesterunternehmen", "konzernschwester", "gemeinsame plattform", + "gemeinsame auswertung", "gemeinsame studie", "joint venture", + "konzernweite analyse", "gemeinsame zwecke", "gemeinsame ziele", + "konzernweit", "gemeinsamer kunde", "gemeinsamer datenpool", +) + +_AVV_SIGNALS = ( + "auftragsverarbeiter", "auftragsverarbeitung", "weisungsgebunden", + "im auftrag von", "im namen des verantwortlichen", + "art. 28 dsgvo", "art 28 dsgvo", "dpa (data processing agreement", +) + +_QUESTIONS = [ + { + "q": "Bestimmen beide Seiten gemeinsam Zweck UND Mittel der Verarbeitung?", + "yes": "JC (Art. 26)", + "no": "AVV-Indikator", + "explain": "EDPB 7/2020 Rn. 51-65: beidseitige Zweckbestimmung ist " + "das Hauptmerkmal der gemeinsamen Verantwortlichkeit.", + }, + { + "q": "Verfolgen die Parteien eigene, getrennte Zwecke (z.B. eigene " + "Kundenbeziehung) oder einen gemeinsamen Zweck?", + "yes": "Wenn getrennt: AVV (oder zwei getrennte Verantwortliche)", + "no": "Wenn gemeinsam: JC (Art. 26)", + "explain": "EuGH C-25/17 Zeugen Jehovas: getrennte Zwecke " + "schliessen JC aus.", + }, + { + "q": "Existiert eine schriftliche Weisungs-Hierarchie und Pflicht " + "zur Loeschung am Vertragsende?", + "yes": "AVV (Art. 28 Pflichten erfuellt)", + "no": "Pruefen ob JC vorliegt + Art. 26-Vereinbarung noetig", + "explain": "Art. 28 (3)(g) DSGVO + EDPB 7/2020 Rn. 88.", + }, + { + "q": "Haben Betroffene gegenueber beiden Stellen vollstaendige " + "Rechte (Art. 15-22)?", + "yes": "JC — Art. 26 (3) verlangt einheitliche Anlaufstelle", + "no": "AVV — Auftragsverarbeiter weist Rechtsausuebung an " + "Verantwortlichen zurueck", + "explain": "Art. 26 (3) DSGVO macht beide Stellen als gemeinsame " + "Anlaufstelle ansprechbar.", + }, +] + + +def detect_ambiguous_jc_avv(dse_text: str | None) -> bool: + """Heuristik: liegen sowohl JC- als auch AVV-Signale im DSE? Dann + ist die Konstellation typischerweise unklar und der Entscheidungsbaum + hilft.""" + if not dse_text: + return False + t = dse_text.lower() + has_jc = any(s in t for s in _JC_SIGNALS) + has_avv = any(s in t for s in _AVV_SIGNALS) + return has_jc and has_avv + + +def build_jc_avv_decision_html(dse_text: str | None) -> str: + if not detect_ambiguous_jc_avv(dse_text): + return "" + items = [] + for i, q in enumerate(_QUESTIONS, 1): + items.append( + f'
  • ' + f'{i}. {q["q"]}
    ' + f'Ja: {q["yes"]}  |  ' + f'Nein: {q["no"]}
    ' + f'' + f'{q["explain"]}' + f'
  • ' + ) + return ( + '
    ' + '
    ' + 'JC vs AVV — Entscheidungshilfe
    ' + '

    ' + 'Im DSE-Text gibt es sowohl gemeinsame-Verantwortlichkeits- als ' + 'auch Auftragsverarbeitungs-Hinweise

    ' + '

    ' + 'Pruefen Sie mit dem DSB die folgenden 4 Leitfragen aus EDPB 7/2020. ' + 'Das Ergebnis bestimmt ob eine Art. 26-Vereinbarung (JC) oder ein ' + 'Art. 28-AVV vorliegen muss.' + '

    ' + '
      ' + + "".join(items) + + '
    ' + '

    Quelle: EDPB Guidelines 7/2020 (Controller/Processor) ' + '+ EuGH C-25/17, C-40/17.

    ' + '
    ' + )