feat: konfigurierbaren Freigabe-Workflow mit Genehmiger-Rollen einführen
Mandanten können jetzt selbst festlegen, wer bei welcher abgeleiteten Bedingung (Anforderung/Einstufung/Datenklasse) zusätzlich zur Fachebene-Entscheidung zustimmen muss (z. B. Datenschutzbeauftragter, Geschäftsführung) - Genehmiger-Rollen sind orthogonal zu den fünf bestehenden Zugriffsrollen. Alle ausgelösten Rollen müssen zustimmen, eine Ablehnung kippt kaskadierend den gesamten Antrag. Mandanten ohne Freigabe-Regeln (aktuell alle) sind unverändert vom alten Direktpfad betroffen, siehe TestOhneFreigabeRegelnVerhaeltSichWieVorher. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
462
internal/web/freigabe_handlers.go
Normal file
462
internal/web/freigabe_handlers.go
Normal file
@@ -0,0 +1,462 @@
|
||||
// Konfigurierbarer Mehrfach-Freigabe-Workflow — siehe Migration 0017
|
||||
// und CLAUDE.md. Drei Seitengruppen:
|
||||
// - Admin (Ebene 4): Genehmiger-Rollen anlegen/Mitglieder zuweisen,
|
||||
// Freigabe-Regeln definieren ("wenn Bedingung X, dann Freigabe
|
||||
// durch Rolle Y nötig").
|
||||
// - Jeder angemeldete Nutzer: "Meine Freigaben" — eigene ausstehende
|
||||
// Freigabeschritte, unabhängig von app_user.role (eine
|
||||
// Genehmiger-Rolle ist orthogonal zur Zugriffsrolle).
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/netcell-it/deklarix/internal/store"
|
||||
)
|
||||
|
||||
// ─── Admin: Genehmiger-Rollen ───────────────────────────────────────
|
||||
|
||||
type genehmigerRolleItem struct {
|
||||
ID string
|
||||
Name string
|
||||
Mitglieder []nutzerOption
|
||||
}
|
||||
|
||||
type nutzerOption struct {
|
||||
ID string
|
||||
Email string
|
||||
}
|
||||
|
||||
type genehmigerRollenListeData struct {
|
||||
Title string
|
||||
Nav navData
|
||||
Rollen []genehmigerRolleItem
|
||||
Nutzer []nutzerOption
|
||||
Error string
|
||||
}
|
||||
|
||||
func (s *Server) handleGenehmigerRollenListe(w http.ResponseWriter, r *http.Request) {
|
||||
accountID := currentUser(r).AccountID
|
||||
rollen, err := s.store.ListGenehmigerRollenForAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
http.Error(w, "Genehmiger-Rollen konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
users, err := s.store.ListUsersForAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
http.Error(w, "Nutzer konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data := genehmigerRollenListeData{Title: "Genehmiger-Rollen", Nav: navFor(r)}
|
||||
for _, u := range users {
|
||||
data.Nutzer = append(data.Nutzer, nutzerOption{ID: u.ID, Email: u.Email})
|
||||
}
|
||||
for _, g := range rollen {
|
||||
item := genehmigerRolleItem{ID: g.ID, Name: g.Name}
|
||||
mitglieder, err := s.store.ListNutzerForGenehmigerRolle(r.Context(), g.ID)
|
||||
if err == nil {
|
||||
for _, m := range mitglieder {
|
||||
item.Mitglieder = append(item.Mitglieder, nutzerOption{ID: m.UserID, Email: m.Email})
|
||||
}
|
||||
}
|
||||
data.Rollen = append(data.Rollen, item)
|
||||
}
|
||||
if err := s.templates.ExecuteTemplate(w, "genehmiger-rollen-liste", data); err != nil {
|
||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleGenehmigerRolleCreate(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "ungültiges Formular", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
http.Error(w, "Name ist Pflicht", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if _, err := s.store.CreateGenehmigerRolle(r.Context(), currentUser(r).AccountID, name); err != nil {
|
||||
http.Error(w, "Genehmiger-Rolle konnte nicht angelegt werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/verwaltung/genehmiger-rollen", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// handleGenehmigerRolleDelete entfernt eine Genehmiger-Rolle. Solange
|
||||
// noch Mitglieder zugewiesen sind oder eine Freigabe-Regel darauf
|
||||
// verweist, schlägt das per Fremdschlüssel fehl — bewusst kein CASCADE,
|
||||
// ein stillschweigendes Mit-Löschen wirksamer Freigabe-Regeln wäre
|
||||
// überraschend und sicherheitsrelevant.
|
||||
func (s *Server) handleGenehmigerRolleDelete(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
rolle, err := s.store.GetGenehmigerRolle(r.Context(), id)
|
||||
if err != nil || rolle.AccountID != currentUser(r).AccountID {
|
||||
http.Error(w, "Genehmiger-Rolle nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := s.store.DeleteGenehmigerRolle(r.Context(), id); err != nil {
|
||||
http.Error(w, "Genehmiger-Rolle wird noch verwendet (Mitglieder oder Freigabe-Regeln) und kann nicht gelöscht werden", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/verwaltung/genehmiger-rollen", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (s *Server) handleGenehmigerRolleMitgliedHinzufuegen(w http.ResponseWriter, r *http.Request) {
|
||||
accountID := currentUser(r).AccountID
|
||||
rolleID := r.PathValue("id")
|
||||
rolle, err := s.store.GetGenehmigerRolle(r.Context(), rolleID)
|
||||
if err != nil || rolle.AccountID != accountID {
|
||||
http.Error(w, "Genehmiger-Rolle nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "ungültiges Formular", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
userID := r.FormValue("user_id")
|
||||
target, err := s.store.GetUser(r.Context(), userID)
|
||||
if err != nil || target.AccountID != accountID {
|
||||
http.Error(w, "Nutzer nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := s.store.AddNutzerGenehmigerRolle(r.Context(), userID, rolleID); err != nil {
|
||||
http.Error(w, "Zuweisung fehlgeschlagen: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/verwaltung/genehmiger-rollen", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (s *Server) handleGenehmigerRolleMitgliedEntfernen(w http.ResponseWriter, r *http.Request) {
|
||||
accountID := currentUser(r).AccountID
|
||||
rolleID := r.PathValue("id")
|
||||
rolle, err := s.store.GetGenehmigerRolle(r.Context(), rolleID)
|
||||
if err != nil || rolle.AccountID != accountID {
|
||||
http.Error(w, "Genehmiger-Rolle nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
userID := r.PathValue("userID")
|
||||
if err := s.store.RemoveNutzerGenehmigerRolle(r.Context(), userID, rolleID); err != nil {
|
||||
http.Error(w, "Entfernen fehlgeschlagen: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/verwaltung/genehmiger-rollen", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// ─── Admin: Freigabe-Regeln ─────────────────────────────────────────
|
||||
|
||||
// bedingungOption ist ein Auswahlpunkt im Freigabe-Regel-Formular —
|
||||
// Value kodiert "typ:wert" (z. B. "anforderung:dsfa_erforderlich"),
|
||||
// damit ein einziges <select> ohne JavaScript reicht statt zweier
|
||||
// voneinander abhängiger Dropdowns.
|
||||
type bedingungOption struct {
|
||||
Value string
|
||||
Label string
|
||||
}
|
||||
|
||||
type bedingungGruppe struct {
|
||||
Label string
|
||||
Optionen []bedingungOption
|
||||
}
|
||||
|
||||
type freigabeRegelItem struct {
|
||||
ID string
|
||||
BedingungLabel string
|
||||
GenehmigerRolle string
|
||||
}
|
||||
|
||||
type freigabeRegelnListeData struct {
|
||||
Title string
|
||||
Nav navData
|
||||
Regeln []freigabeRegelItem
|
||||
BedingungGruppen []bedingungGruppe
|
||||
RollenOptionen []store.GenehmigerRolle
|
||||
Error string
|
||||
}
|
||||
|
||||
// bedingungGruppenAusRegelwerk baut die Freigabe-Regel-Bedingungsauswahl
|
||||
// aus dem tatsächlich geladenen Regelwerk (nicht frei erfunden) — jede
|
||||
// Anforderungs-/Einstufungs-/Datenklasse-ID, die das System ohnehin
|
||||
// kennt, ist eine mögliche Bedingung.
|
||||
func (s *Server) bedingungGruppenAusRegelwerk() []bedingungGruppe {
|
||||
var anforderungen []bedingungOption
|
||||
for _, a := range s.regelwerk.Anforderungen.Anforderungen {
|
||||
label := a.ID
|
||||
if a.Beschreibung != "" {
|
||||
label = a.Beschreibung
|
||||
}
|
||||
anforderungen = append(anforderungen, bedingungOption{Value: "anforderung:" + a.ID, Label: label})
|
||||
}
|
||||
var einstufungen []bedingungOption
|
||||
for _, e := range s.regelwerk.Einstufung.Stufen {
|
||||
einstufungen = append(einstufungen, bedingungOption{Value: "einstufung:" + e.ID, Label: e.ID})
|
||||
}
|
||||
var datenklassen []bedingungOption
|
||||
for _, d := range s.regelwerk.Datenklasse.Stufen {
|
||||
datenklassen = append(datenklassen, bedingungOption{Value: "datenklasse:" + d.ID, Label: d.ID})
|
||||
}
|
||||
return []bedingungGruppe{
|
||||
{Label: "Anforderung", Optionen: anforderungen},
|
||||
{Label: "Einstufung", Optionen: einstufungen},
|
||||
{Label: "Datenklasse", Optionen: datenklassen},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleFreigabeRegelnListe(w http.ResponseWriter, r *http.Request) {
|
||||
accountID := currentUser(r).AccountID
|
||||
regeln, err := s.store.ListFreigabeRegelnForAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
http.Error(w, "Freigabe-Regeln konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rollen, err := s.store.ListGenehmigerRollenForAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
http.Error(w, "Genehmiger-Rollen konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rollenName := map[string]string{}
|
||||
for _, g := range rollen {
|
||||
rollenName[g.ID] = g.Name
|
||||
}
|
||||
data := freigabeRegelnListeData{
|
||||
Title: "Freigabe-Regeln", Nav: navFor(r),
|
||||
BedingungGruppen: s.bedingungGruppenAusRegelwerk(), RollenOptionen: rollen,
|
||||
}
|
||||
for _, regel := range regeln {
|
||||
data.Regeln = append(data.Regeln, freigabeRegelItem{
|
||||
ID: regel.ID,
|
||||
BedingungLabel: bedingungTypLabel(regel.BedingungTyp) + ": " + regel.BedingungWert,
|
||||
GenehmigerRolle: rollenName[regel.GenehmigerRolleID],
|
||||
})
|
||||
}
|
||||
if err := s.templates.ExecuteTemplate(w, "freigabe-regeln-liste", data); err != nil {
|
||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func bedingungTypLabel(typ string) string {
|
||||
switch typ {
|
||||
case "anforderung":
|
||||
return "Anforderung"
|
||||
case "einstufung":
|
||||
return "Einstufung"
|
||||
case "datenklasse":
|
||||
return "Datenklasse"
|
||||
default:
|
||||
return typ
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleFreigabeRegelCreate(w http.ResponseWriter, r *http.Request) {
|
||||
accountID := currentUser(r).AccountID
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "ungültiges Formular", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
teile := strings.SplitN(r.FormValue("bedingung"), ":", 2)
|
||||
rolleID := r.FormValue("genehmiger_rolle_id")
|
||||
if len(teile) != 2 || teile[0] == "" || teile[1] == "" || rolleID == "" {
|
||||
http.Error(w, "Bedingung und Genehmiger-Rolle sind Pflicht", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
typ, wert := teile[0], teile[1]
|
||||
if typ != "anforderung" && typ != "einstufung" && typ != "datenklasse" {
|
||||
http.Error(w, "ungültiger Bedingungstyp", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
rolle, err := s.store.GetGenehmigerRolle(r.Context(), rolleID)
|
||||
if err != nil || rolle.AccountID != accountID {
|
||||
http.Error(w, "Genehmiger-Rolle nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if _, err := s.store.CreateFreigabeRegel(r.Context(), accountID, typ, wert, rolleID); err != nil {
|
||||
http.Error(w, "Freigabe-Regel konnte nicht angelegt werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/verwaltung/freigabe-regeln", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (s *Server) handleFreigabeRegelDelete(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
regel, err := s.store.GetFreigabeRegel(r.Context(), id)
|
||||
if err != nil || regel.AccountID != currentUser(r).AccountID {
|
||||
http.Error(w, "Freigabe-Regel nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := s.store.DeleteFreigabeRegel(r.Context(), id); err != nil {
|
||||
http.Error(w, "Freigabe-Regel konnte nicht gelöscht werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/verwaltung/freigabe-regeln", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// ─── Meine Freigaben (jeder angemeldete Nutzer) ─────────────────────
|
||||
|
||||
type freigabeItem struct {
|
||||
ID string
|
||||
AntragID string
|
||||
AntragTitel string
|
||||
RolleName string
|
||||
CreatedAt string
|
||||
}
|
||||
|
||||
type freigabenListeData struct {
|
||||
Title string
|
||||
Nav navData
|
||||
Freigaben []freigabeItem
|
||||
}
|
||||
|
||||
// handleFreigabenListe zeigt die eigenen ausstehenden Freigabeschritte
|
||||
// — unabhängig von app_user.role, denn eine Genehmiger-Rolle ist eine
|
||||
// orthogonale Freigabe-Funktion, keine Zugriffsrolle.
|
||||
func (s *Server) handleFreigabenListe(w http.ResponseWriter, r *http.Request) {
|
||||
user := currentUser(r)
|
||||
schritte, err := s.store.ListAusstehendeFreigabeschritteForUser(r.Context(), user.ID)
|
||||
if err != nil {
|
||||
http.Error(w, "Freigaben konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data := freigabenListeData{Title: "Meine Freigaben", Nav: navFor(r)}
|
||||
for _, schritt := range schritte {
|
||||
antrag, err := s.store.GetAntrag(r.Context(), schritt.AntragID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
rolleName := ""
|
||||
if rolle, err := s.store.GetGenehmigerRolle(r.Context(), schritt.GenehmigerRolleID); err == nil {
|
||||
rolleName = rolle.Name
|
||||
}
|
||||
data.Freigaben = append(data.Freigaben, freigabeItem{
|
||||
ID: schritt.ID, AntragID: antrag.ID, AntragTitel: antrag.Titel,
|
||||
RolleName: rolleName, CreatedAt: schritt.CreatedAt.Format("02.01.2006 15:04"),
|
||||
})
|
||||
}
|
||||
if err := s.templates.ExecuteTemplate(w, "freigaben-liste", data); err != nil {
|
||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// handleFreigabeEntscheiden trägt die Entscheidung einer Person zu
|
||||
// einem einzelnen Freigabeschritt ein. Genehmigt sie den letzten noch
|
||||
// ausstehenden Schritt eines Antrags, wird die ursprüngliche Genehmigung
|
||||
// jetzt final abgeschlossen (Registereintrag). Lehnt sie ab, kippt das
|
||||
// den gesamten Antrag — alle anderen offenen Schritte desselben Antrags
|
||||
// werden automatisch mit abgelehnt (KaskadiereAblehnung), und eine neue
|
||||
// Entscheidung "abgelehnt" übersteuert die ursprüngliche Genehmigung
|
||||
// (entscheidung ist append-only, siehe CLAUDE.md).
|
||||
func (s *Server) handleFreigabeEntscheiden(w http.ResponseWriter, r *http.Request) {
|
||||
user := currentUser(r)
|
||||
id := r.PathValue("id")
|
||||
|
||||
schritt, err := s.store.GetFreigabeschritt(r.Context(), id)
|
||||
if err != nil {
|
||||
http.Error(w, "Freigabeschritt nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
rollen, err := s.store.ListGenehmigerRollenForUser(r.Context(), user.ID)
|
||||
if err != nil {
|
||||
http.Error(w, "Genehmiger-Rollen konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
hatRolle := false
|
||||
for _, rl := range rollen {
|
||||
if rl.ID == schritt.GenehmigerRolleID {
|
||||
hatRolle = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hatRolle {
|
||||
http.Error(w, "Freigabeschritt nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if schritt.Status != "ausstehend" {
|
||||
http.Error(w, "dieser Freigabeschritt wurde bereits entschieden", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "ungültiges Formular", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
entscheidungTyp := r.FormValue("entscheidung")
|
||||
kommentar := r.FormValue("kommentar")
|
||||
if entscheidungTyp != "genehmigt" && entscheidungTyp != "abgelehnt" {
|
||||
http.Error(w, "ungültige Entscheidung", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := s.store.EntscheideFreigabeschritt(r.Context(), id, entscheidungTyp, user.ID, kommentar); err != nil {
|
||||
http.Error(w, "Freigabeschritt konnte nicht gespeichert werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
antrag, err := s.store.GetAntrag(r.Context(), schritt.AntragID)
|
||||
if err != nil {
|
||||
http.Error(w, "Antrag nicht gefunden", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rolle, _ := s.store.GetGenehmigerRolle(r.Context(), schritt.GenehmigerRolleID)
|
||||
if _, err := s.store.CreateAuditEntry(r.Context(), user.ID, "freigabeschritt_entschieden", "antrag", antrag.ID, rolle.Name+": "+entscheidungTyp); err != nil {
|
||||
http.Error(w, "Audit-Log konnte nicht geschrieben werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
bewertung, err := s.store.GetLatestBewertungForAntrag(r.Context(), antrag.ID)
|
||||
if err != nil {
|
||||
http.Error(w, "Bewertung nicht gefunden", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if entscheidungTyp == "abgelehnt" {
|
||||
if err := s.store.KaskadiereAblehnung(r.Context(), antrag.ID, id); err != nil {
|
||||
http.Error(w, "Kaskadierung fehlgeschlagen: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := s.store.SetAntragStatus(r.Context(), antrag.ID, "entschieden"); err != nil {
|
||||
http.Error(w, "Antrag konnte nicht aktualisiert werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
begruendung := "Freigabe durch " + rolle.Name + " wurde verweigert."
|
||||
if kommentar != "" {
|
||||
begruendung += " Kommentar: " + kommentar
|
||||
}
|
||||
if _, err := s.store.CreateEntscheidung(r.Context(), store.EntscheidungInput{
|
||||
AntragID: antrag.ID, BewertungID: bewertung.ID, EntscheiderUserID: user.ID,
|
||||
Entscheidung: "abgelehnt", Begruendung: begruendung,
|
||||
}); err != nil {
|
||||
http.Error(w, "Entscheidung konnte nicht gespeichert werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
alle, err := s.store.ListFreigabeschritteForAntrag(r.Context(), antrag.ID)
|
||||
if err != nil {
|
||||
http.Error(w, "Freigabeschritte konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
vollstaendig := true
|
||||
for _, fs := range alle {
|
||||
if fs.Status != "genehmigt" {
|
||||
vollstaendig = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if vollstaendig {
|
||||
letzteEntscheidung, err := s.store.GetLatestEntscheidungForAntrag(r.Context(), antrag.ID)
|
||||
if err != nil {
|
||||
http.Error(w, "Entscheidung nicht gefunden", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := s.store.SetAntragStatus(r.Context(), antrag.ID, "entschieden"); err != nil {
|
||||
http.Error(w, "Antrag konnte nicht aktualisiert werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := s.registriereGenehmigung(r.Context(), antrag, letzteEntscheidung, bewertung); err != nil {
|
||||
http.Error(w, "Registereintrag konnte nicht angelegt werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/freigaben", http.StatusSeeOther)
|
||||
}
|
||||
Reference in New Issue
Block a user