""" Geschäftslogik für die Entität Kontaktadresse. Diese Funktionen kapseln den DB-Zugriff und die fachlichen Validierungen. Aufgerufen werden sie aus crm/routes.py; die Autorisierungsprüfung erfolgt dort über den Decorator authorisation_required. :author: Mistral Vibe :date: 2026-09-04 :version: 1.0 """ from db import dbapp from crm.model import Kontaktadresse, Projekt, ProjektUser from auth.model.errors import ApplicationError, HtmlStatusCode session = dbapp.session def get_kontaktadressen(projekt_id: int | None = None, user_id: int | None = None): """ Liefert Kontaktadressen, sortiert nach Name. Ist projekt_id gesetzt, werden nur die Kontaktadressen dieses Projekts zurückgegeben (serverseitige Filterung). Ohne projekt_id werden alle Kontaktadressen geliefert. Ist user_id gesetzt, werden nur Kontaktadressen von Projekten geliefert, denen der Nutzer über projekt_users zugeordnet ist (Sichtbarkeits- Filterung). SysAdmin und AppAdmin übergeben user_id=None und sehen alle. """ query = Kontaktadresse.query if projekt_id is not None: query = query.filter(Kontaktadresse.projekt_id == projekt_id) if user_id is not None: # Nur Kontaktadressen von Projekten, denen der Nutzer zugeordnet ist. query = query.filter( Kontaktadresse.projekt_id.in_( dbapp.session.query(ProjektUser.projekt_id).filter( ProjektUser.user_id == user_id ) ) ) return query.order_by(Kontaktadresse.name).all() def get_kontaktadresse(kontaktadresse_id: int, user_id: int | None = None) -> Kontaktadresse: """ Liefert eine einzelne Kontaktadresse anhand ihrer ID. Wirft ApplicationError (404), falls die Kontaktadresse nicht existiert. Ist user_id gesetzt, wird zusätzlich geprüft, ob der Nutzer dem Projekt der Kontaktadresse zugeordnet ist; sonst wird 404 geliefert. """ kontaktadresse = Kontaktadresse.query.get(kontaktadresse_id) if kontaktadresse is None: raise ApplicationError( "Kontaktadresse mit der ID {} wurde nicht gefunden.".format(kontaktadresse_id), htmlstatus=HtmlStatusCode.Not_Found, ) if user_id is not None: zuordnung = ProjektUser.query.filter( ProjektUser.projekt_id == kontaktadresse.projekt_id, ProjektUser.user_id == user_id, ).first() if zuordnung is None: raise ApplicationError( "Kontaktadresse mit der ID {} wurde nicht gefunden.".format(kontaktadresse_id), htmlstatus=HtmlStatusCode.Not_Found, ) return kontaktadresse def create_kontaktadresse(data: dict) -> Kontaktadresse: """ Legt eine neue Kontaktadresse an. Die vom Client übergebene ID wird ignoriert; die ID wird serverseitig per Auto-Increment vergeben. - data: dict mit den Feldern projekt_id, anrede, name, firma, telefon, email, strasse, plz, ort, land, notiz """ projekt_id = data.get("projekt_id") if projekt_id is None: raise ApplicationError( "Es muss ein Projekt ausgewählt werden.", htmlstatus=HtmlStatusCode.Bad_Request, ) projekt = Projekt.query.get(int(projekt_id)) if projekt is None: raise ApplicationError( "Das ausgewählte Projekt existiert nicht.", htmlstatus=HtmlStatusCode.Bad_Request, ) if not data.get("name") or not str(data["name"]).strip(): raise ApplicationError( "Der Name darf nicht leer sein.", htmlstatus=HtmlStatusCode.Bad_Request, ) kontaktadresse = Kontaktadresse() kontaktadresse.projekt_id = int(projekt_id) kontaktadresse.anrede = (data.get("anrede") or "").strip() or None kontaktadresse.name = data["name"].strip() kontaktadresse.firma = (data.get("firma") or "").strip() or None kontaktadresse.position = (data.get("position") or "").strip() or None kontaktadresse.telefon = (data.get("telefon") or "").strip() or None kontaktadresse.email = (data.get("email") or "").strip() or None kontaktadresse.strasse = (data.get("strasse") or "").strip() or None kontaktadresse.plz = (data.get("plz") or "").strip() or None kontaktadresse.ort = (data.get("ort") or "").strip() or None kontaktadresse.land = (data.get("land") or "").strip() or None kontaktadresse.notiz = (data.get("notiz") or "").strip() or None session.add(kontaktadresse) session.commit() return kontaktadresse def update_kontaktadresse(data: dict) -> Kontaktadresse: """ Aktualisiert eine bestehende Kontaktadresse. Die ID im data-Dict bestimmt den zu ändernden Datensatz. """ kontaktadresse = get_kontaktadresse(data["id"]) projekt_id = data.get("projekt_id") if projekt_id is None: raise ApplicationError( "Es muss ein Projekt ausgewählt werden.", htmlstatus=HtmlStatusCode.Bad_Request, ) projekt = Projekt.query.get(int(projekt_id)) if projekt is None: raise ApplicationError( "Das ausgewählte Projekt existiert nicht.", htmlstatus=HtmlStatusCode.Bad_Request, ) if not data.get("name") or not str(data["name"]).strip(): raise ApplicationError( "Der Name darf nicht leer sein.", htmlstatus=HtmlStatusCode.Bad_Request, ) kontaktadresse.projekt_id = int(projekt_id) kontaktadresse.anrede = (data.get("anrede") or "").strip() or None kontaktadresse.name = data["name"].strip() kontaktadresse.firma = (data.get("firma") or "").strip() or None kontaktadresse.position = (data.get("position") or "").strip() or None kontaktadresse.telefon = (data.get("telefon") or "").strip() or None kontaktadresse.email = (data.get("email") or "").strip() or None kontaktadresse.strasse = (data.get("strasse") or "").strip() or None kontaktadresse.plz = (data.get("plz") or "").strip() or None kontaktadresse.ort = (data.get("ort") or "").strip() or None kontaktadresse.land = (data.get("land") or "").strip() or None kontaktadresse.notiz = (data.get("notiz") or "").strip() or None session.commit() return kontaktadresse def delete_kontaktadresse(kontaktadresse_id: int): """ Löscht eine Kontaktadresse anhand ihrer ID. Wirft ApplicationError (404), falls die Kontaktadresse nicht existiert. """ kontaktadresse = get_kontaktadresse(kontaktadresse_id) session.delete(kontaktadresse) session.commit()