DSGVO-Compliance: Einwilligung, Datenschutzerklärung & Consent-Logging im Upload-Portal (STI-89)
- Datenschutzerklärung unter /portal/datenschutz/ öffentlich erreichbar - Link zur Datenschutzerklärung in Nachweis-Aufforderungs-E-Mails (HTML + TXT) - Einwilligungs-Checkbox vor Upload mit Server-Side-Validierung - Consent-Logging: einwilligung_erteilt_am auf UploadToken (Art. 7 Abs. 1 DSGVO) - Regelsatz-Korrektur: 449€→563€ in Onboarding-Template (Stand 01/2024) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.6 on 2026-03-21 22:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stiftung', '0063_add_anrede_to_destinataer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='uploadtoken',
|
||||
name='einwilligung_erteilt_am',
|
||||
field=models.DateTimeField(blank=True, help_text='Zeitpunkt der DSGVO-Einwilligung beim Upload (Art. 7 Abs. 1 DSGVO)', null=True, verbose_name='Einwilligung erteilt am'),
|
||||
),
|
||||
]
|
||||
@@ -1362,6 +1362,10 @@ class UploadToken(models.Model):
|
||||
erinnerung_gesendet = models.BooleanField(
|
||||
default=False, verbose_name="Erinnerung gesendet"
|
||||
)
|
||||
einwilligung_erteilt_am = models.DateTimeField(
|
||||
null=True, blank=True, verbose_name="Einwilligung erteilt am",
|
||||
help_text="Zeitpunkt der DSGVO-Einwilligung beim Upload (Art. 7 Abs. 1 DSGVO)"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Upload-Token"
|
||||
|
||||
@@ -6,6 +6,7 @@ Diese URLs sind ohne Login zugänglich (tokenbasierte Authentifizierung).
|
||||
from django.urls import path
|
||||
|
||||
from stiftung.views.portal import (
|
||||
datenschutzerklaerung,
|
||||
onboarding_danke,
|
||||
onboarding_schritt,
|
||||
upload_danke,
|
||||
@@ -15,6 +16,12 @@ from stiftung.views.portal import (
|
||||
app_name = "portal"
|
||||
|
||||
urlpatterns = [
|
||||
# Datenschutzerklärung (öffentlich, kein Token erforderlich)
|
||||
path(
|
||||
"datenschutz/",
|
||||
datenschutzerklaerung,
|
||||
name="datenschutzerklaerung",
|
||||
),
|
||||
# Upload-Portal (bestehende Destinatäre – Token-basiert)
|
||||
path(
|
||||
"upload/<str:token>/",
|
||||
|
||||
@@ -547,6 +547,7 @@ def send_nachweis_aufforderung(self, destinataer_id, nachweis_id, base_url=None)
|
||||
"gueltig_bis": gueltig_bis,
|
||||
"halbjahr_label": halbjahr_label,
|
||||
"quartal_label": quartal_label,
|
||||
"datenschutz_url": f"{base_url}/portal/datenschutz/",
|
||||
}
|
||||
|
||||
subject = f"Nachweis-Aufforderung: {quartal_label} ({halbjahr_label}) – vHTV-Stiftung"
|
||||
@@ -618,6 +619,7 @@ def send_nachweis_erinnerung(self, token_id, base_url=None):
|
||||
"gueltig_bis": upload_token.gueltig_bis,
|
||||
"halbjahr_label": halbjahr_label,
|
||||
"ist_erinnerung": True,
|
||||
"datenschutz_url": f"{base_url}/portal/datenschutz/",
|
||||
}
|
||||
|
||||
subject = f"Erinnerung: Nachweis-Upload noch ausstehend – {halbjahr_label}"
|
||||
|
||||
@@ -33,6 +33,11 @@ from django.views.decorators.http import require_http_methods
|
||||
|
||||
from stiftung.models import DokumentDatei, OnboardingEinladung, UploadToken, VierteljahresNachweis
|
||||
|
||||
|
||||
def datenschutzerklaerung(request):
|
||||
"""Datenschutzerklärung für das öffentliche Portal."""
|
||||
return render(request, "portal/datenschutzerklaerung.html")
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Erlaubte Dateitypen für Uploads
|
||||
@@ -105,6 +110,19 @@ def upload_formular(request, token):
|
||||
if request.method == "GET":
|
||||
return render(request, "portal/upload_formular.html", base_context)
|
||||
|
||||
# POST: Einwilligung prüfen
|
||||
einwilligung = request.POST.get("einwilligung")
|
||||
if not einwilligung:
|
||||
ctx = {
|
||||
**base_context,
|
||||
"einwilligung_fehler": "Bitte erteilen Sie Ihre Einwilligung zur Datenverarbeitung, um fortzufahren.",
|
||||
}
|
||||
for kat in [
|
||||
"studiennachweis", "einkommenssituation", "vermogenssituation", "weitere_dokumente"
|
||||
]:
|
||||
ctx[f"{kat}_text"] = request.POST.get(f"{kat}_text", "")
|
||||
return render(request, "portal/upload_formular.html", ctx)
|
||||
|
||||
# POST: Kategorisierte Dateien und Texte verarbeiten
|
||||
# Kategorien mit ihren DMS-Kontext-Werten und FK-Feldern auf VierteljahresNachweis
|
||||
KATEGORIEN = [
|
||||
@@ -228,6 +246,10 @@ def upload_formular(request, token):
|
||||
if nachweis_update_fields:
|
||||
nachweis.save(update_fields=list(set(nachweis_update_fields)))
|
||||
|
||||
# DSGVO-Einwilligung protokollieren (Art. 7 Abs. 1 DSGVO)
|
||||
upload_token.einwilligung_erteilt_am = timezone.now()
|
||||
upload_token.save(update_fields=["einwilligung_erteilt_am"])
|
||||
|
||||
# Token einlösen
|
||||
ip = _get_client_ip(request)
|
||||
upload_token.einloesen(ip_address=ip)
|
||||
|
||||
Reference in New Issue
Block a user