feat: auto-update fall_id when KVNR is entered on a case

When a KVNR is entered on a case that has a random-suffix or legacy
Nachname-based fall_id, the fall_id is automatically rebuilt using
the new KVNR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-02-26 17:09:50 +00:00
parent 7bbe501bfa
commit c302a91411
3 changed files with 55 additions and 2 deletions

View file

@ -24,6 +24,7 @@ from app.schemas.case import (
from app.schemas.disclosure import DisclosureRequestCreate, DisclosureRequestResponse from app.schemas.disclosure import DisclosureRequestCreate, DisclosureRequestResponse
from app.config import get_settings from app.config import get_settings
from app.services.audit_service import log_action from app.services.audit_service import log_action
from app.services.import_service import has_random_suffix
from app.services.disclosure_service import ( from app.services.disclosure_service import (
create_disclosure_request, has_active_disclosure, has_pending_request, create_disclosure_request, has_active_disclosure, has_pending_request,
) )
@ -433,6 +434,12 @@ def set_case_kvnr(
old_kvnr = case.kvnr old_kvnr = case.kvnr
new_kvnr = payload.get("kvnr") new_kvnr = payload.get("kvnr")
case.kvnr = new_kvnr case.kvnr = new_kvnr
# Auto-update fall_id if it currently has a non-KVNR suffix
old_fall_id = case.fall_id
if new_kvnr and case.fall_id and has_random_suffix(case.fall_id):
case.fall_id = f"{case.jahr}-{case.kw:02d}-{case.fallgruppe}-{new_kvnr}"
case.updated_by = user.id case.updated_by = user.id
db.commit() db.commit()
db.refresh(case) db.refresh(case)
@ -443,8 +450,8 @@ def set_case_kvnr(
action="kvnr_updated", action="kvnr_updated",
entity_type="case", entity_type="case",
entity_id=case.id, entity_id=case.id,
old_values={"kvnr": old_kvnr}, old_values={"kvnr": old_kvnr, "fall_id": old_fall_id},
new_values={"kvnr": new_kvnr}, new_values={"kvnr": new_kvnr, "fall_id": case.fall_id},
ip_address=request.client.host if request.client else None, ip_address=request.client.host if request.client else None,
user_agent=request.headers.get("user-agent"), user_agent=request.headers.get("user-agent"),
) )

View file

@ -9,6 +9,7 @@ Handles:
import logging import logging
import random import random
import re
import string import string
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@ -41,6 +42,28 @@ def generate_fall_id(parsed: ParsedCase) -> str:
return f"{parsed.jahr}-{parsed.kw:02d}-{parsed.fallgruppe}-{suffix}" return f"{parsed.jahr}-{parsed.kw:02d}-{parsed.fallgruppe}-{suffix}"
# KVNR format: letter followed by 9 digits (e.g. A123456789)
KVNR_PATTERN = re.compile(r'^[A-Z]\d{9}$')
def has_random_suffix(fall_id: str | None) -> bool:
"""Check if a fall_id ends with a non-KVNR suffix.
Returns True for random suffixes (6-char alphanumeric) and legacy
Nachname-based suffixes. Returns False for KVNR suffixes.
"""
if not fall_id:
return False
parts = fall_id.rsplit("-", 1)
if len(parts) < 2:
return False
suffix = parts[1]
# If it matches KVNR pattern, it's NOT a random suffix
if KVNR_PATTERN.match(suffix):
return False
return True
def check_duplicate(db: Session, parsed: ParsedCase) -> bool: def check_duplicate(db: Session, parsed: ParsedCase) -> bool:
"""Check if a case already exists in the database. """Check if a case already exists in the database.

View file

@ -17,6 +17,7 @@ from app.services.import_service import (
confirm_import, confirm_import,
generate_fall_id, generate_fall_id,
generate_random_suffix, generate_random_suffix,
has_random_suffix,
preview_import, preview_import,
) )
@ -354,3 +355,25 @@ class TestPreviewImportMocked:
assert result.rows[0].row_number == 1 assert result.rows[0].row_number == 1
assert result.rows[1].row_number == 2 assert result.rows[1].row_number == 2
assert result.rows[2].row_number == 3 assert result.rows[2].row_number == 3
# ── has_random_suffix tests ───────────────────────────────────────────
class TestHasRandomSuffix:
def test_kvnr_suffix(self):
"""Fall_id with KVNR is NOT random."""
assert has_random_suffix("2026-06-onko-A123456789") is False
def test_random_suffix(self):
"""Fall_id with 6-char random suffix IS random."""
assert has_random_suffix("2026-06-onko-X7K9M2") is True
def test_legacy_nachname_suffix(self):
"""Fall_id with legacy Nachname suffix IS treated as non-KVNR."""
assert has_random_suffix("2020-32-onko-Bartl-Zimmermann") is True
assert has_random_suffix("2026-06-kardio-Tonn") is True
def test_empty_fall_id(self):
assert has_random_suffix("") is False
assert has_random_suffix(None) is False