feat(dokumente): DOC-001 Duplikat-Erkennung + Fix Online-Ansicht (Popup-Blocker)
CI / backend-tests (push) Failing after 2m11s
CI / frontend-build (push) Successful in 20s

Duplikat-Erkennung: SHA-256-Hash je Dokument (Migration 0024), Upload wird
mit 409 abgelehnt, wenn dieselbe Datei bereits an derselben Entität liegt
(Vergleich bewusst pro Entität, nicht global - gleiche Datei an zwei
Objekten ist kein Duplikat). Frontend zeigt die Backend-Fehlermeldung
(Dateiname/Datum des bestehenden Dokuments) statt generischem Text.

Nebenbei gefunden+gefixt: "Ansehen" (PDF/JPG/PNG online statt Download)
funktionierte nicht zuverlässig, weil window.open() erst nach einem await
fetch() aufgerufen wurde - Popup-Blocker werten das nicht mehr als direkte
Nutzeraktion. Jetzt öffnet der Tab sofort synchron, die Blob-URL wird
nachgeladen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-08 00:59:15 +02:00
co-authored by Claude Sonnet 5
parent 307914ccf7
commit 707701d897
7 changed files with 155 additions and 11 deletions
+53
View File
@@ -94,3 +94,56 @@ async def test_mitarbeiter_darf_dokument_nicht_loeschen(client, objekt_mit_posit
response = await client.delete(f"/api/v1/dokumente/{dokument_id}", headers=auth_header(token))
assert response.status_code == 403
@pytest.mark.asyncio
async def test_identische_datei_am_gleichen_objekt_wird_abgelehnt(client, objekt_mit_position, mitarbeiter_user):
"""DOC-001: gleiche Bytes, gleiche Entität -> 409 statt zweitem Dokument."""
objekt, _material = objekt_mit_position
token = await login(client, "mitarbeiter1")
inhalt = b"%PDF-1.4 identischer inhalt"
erster = await client.post(
"/api/v1/dokumente",
data={"entitaet_typ": "objekt", "entitaet_id": str(objekt.id)},
files={"datei": ("original.pdf", inhalt, "application/pdf")},
headers=auth_header(token),
)
assert erster.status_code == 201
zweiter = await client.post(
"/api/v1/dokumente",
data={"entitaet_typ": "objekt", "entitaet_id": str(objekt.id)},
files={"datei": ("kopie.pdf", inhalt, "application/pdf")},
headers=auth_header(token),
)
assert zweiter.status_code == 409
assert "original.pdf" in zweiter.json()["detail"]
liste = await client.get(
f"/api/v1/dokumente?entitaet_typ=objekt&entitaet_id={objekt.id}", headers=auth_header(token)
)
assert len(liste.json()) == 1
@pytest.mark.asyncio
async def test_identische_datei_an_anderer_entitaet_ist_kein_duplikat(client, objekt_mit_position, mitarbeiter_user):
"""Gleiche Datei an ZWEI verschiedenen Objekten ist kein Duplikat - Hash-
Vergleich ist bewusst pro Entität, nicht global."""
objekt, _material = objekt_mit_position
token = await login(client, "mitarbeiter1")
inhalt = b"%PDF-1.4 gleiche datei anderes objekt"
await client.post(
"/api/v1/dokumente",
data={"entitaet_typ": "objekt", "entitaet_id": str(objekt.id)},
files={"datei": ("anleitung.pdf", inhalt, "application/pdf")},
headers=auth_header(token),
)
andere_entitaet = await client.post(
"/api/v1/dokumente",
data={"entitaet_typ": "mangel", "entitaet_id": "irgendein-mangel-id"},
files={"datei": ("anleitung.pdf", inhalt, "application/pdf")},
headers=auth_header(token),
)
assert andere_entitaet.status_code == 201