feat: Wiedervorlage und Katalog-Benachrichtigung (Schritt 7 der Baureihenfolge)
GET /wiedervorlage (Ebene 3) zeigt aktive Genehmigungen, die erneut geprüft werden sollten: Ablaufdatum erreicht oder innerhalb von 30 Tagen, das zugesagte Werkzeug wurde aus dem Katalog entfernt, oder der aktuelle Katalogeintrag weicht bei AVV/Training-Standard/ Verarbeitungsort vom zum Entscheidungszeitpunkt eingefrorenen Snapshot ab. Keine E-Mail-Infrastruktur vorhanden — Benachrichtigung ist bewusst eine In-App-Liste. Damit ist die ursprüngliche Baureihenfolge (Schritt 1-7) vollständig umgesetzt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
104
internal/web/wiedervorlage_handlers.go
Normal file
104
internal/web/wiedervorlage_handlers.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Ebene 3 — Wiedervorlage (Schritt 7 der Baureihenfolge, siehe
|
||||
// CLAUDE.md). Eine aktive Genehmigung muss erneut geprüft werden, wenn
|
||||
// entweder ihr Ablaufdatum erreicht/nah ist, oder sich im Katalog eine
|
||||
// Eigenschaft geändert hat, auf der sie beruhte. Es gibt keine E-Mail-
|
||||
// Infrastruktur — "Benachrichtigung" ist v1 eine In-App-Liste, kein
|
||||
// Versand.
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/netcell-it/deklarix/internal/store"
|
||||
)
|
||||
|
||||
// wiedervorlageSchwelle: eine Genehmigung erscheint schon vor ihrem
|
||||
// Ablauf in der Wiedervorlage, damit der/die Verantwortliche Zeit zum
|
||||
// Reagieren hat.
|
||||
const wiedervorlageSchwelle = 30 * 24 * time.Hour
|
||||
|
||||
// werkzeugDiff vergleicht den zum Entscheidungszeitpunkt eingefrorenen
|
||||
// Werkzeug-Snapshot mit dem aktuellen Katalogstand und benennt jede für
|
||||
// die Bewertung relevante Eigenschaft, die sich seitdem geändert hat.
|
||||
func werkzeugDiff(snapshot, current store.Werkzeug) []string {
|
||||
var diffs []string
|
||||
if snapshot.AVVVerfuegbar != current.AVVVerfuegbar {
|
||||
diffs = append(diffs, fmt.Sprintf("AVV verfügbar: %v → %v", snapshot.AVVVerfuegbar, current.AVVVerfuegbar))
|
||||
}
|
||||
if snapshot.TrainingStandard != current.TrainingStandard {
|
||||
diffs = append(diffs, fmt.Sprintf("Training standardmäßig aus: %v → %v", snapshot.TrainingStandard, current.TrainingStandard))
|
||||
}
|
||||
if snapshot.Verarbeitungsort != current.Verarbeitungsort {
|
||||
diffs = append(diffs, fmt.Sprintf("Verarbeitungsort: %s → %s", snapshot.Verarbeitungsort, current.Verarbeitungsort))
|
||||
}
|
||||
return diffs
|
||||
}
|
||||
|
||||
type wiedervorlageItem struct {
|
||||
AntragID string
|
||||
Titel string
|
||||
Werkzeug string
|
||||
Gruende []string
|
||||
}
|
||||
|
||||
type wiedervorlageListeData struct {
|
||||
Title string
|
||||
Nav navData
|
||||
Eintraege []wiedervorlageItem
|
||||
}
|
||||
|
||||
// handleWiedervorlageListe zeigt alle aktiven Genehmigungen des
|
||||
// Mandanten, die erneut geprüft werden sollten — abgelaufen/bald
|
||||
// ablaufend oder mit einem seither im Katalog geänderten Werkzeug.
|
||||
// Genehmigungen ohne Grund zur Wiedervorlage werden nicht gezeigt.
|
||||
func (s *Server) handleWiedervorlageListe(w http.ResponseWriter, r *http.Request) {
|
||||
entscheidungen, err := s.store.ListAktiveGenehmigungenForAccount(r.Context(), currentUser(r).AccountID)
|
||||
if err != nil {
|
||||
http.Error(w, "Genehmigungen konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var data wiedervorlageListeData
|
||||
data.Title, data.Nav = "Wiedervorlage", navFor(r)
|
||||
for _, e := range entscheidungen {
|
||||
var gruende []string
|
||||
|
||||
if e.GueltigBis != nil {
|
||||
if time.Now().After(*e.GueltigBis) {
|
||||
gruende = append(gruende, "Genehmigung ist abgelaufen (seit "+e.GueltigBis.Format("02.01.2006")+")")
|
||||
} else if time.Until(*e.GueltigBis) < wiedervorlageSchwelle {
|
||||
gruende = append(gruende, "Genehmigung läuft am "+e.GueltigBis.Format("02.01.2006")+" ab")
|
||||
}
|
||||
}
|
||||
|
||||
werkzeugName := ""
|
||||
if e.WerkzeugID != nil {
|
||||
current, err := s.store.GetWerkzeug(r.Context(), *e.WerkzeugID)
|
||||
if err != nil {
|
||||
gruende = append(gruende, "Werkzeug wurde aus dem Katalog entfernt")
|
||||
} else {
|
||||
werkzeugName = current.Name
|
||||
if e.WerkzeugSnapshot != nil {
|
||||
gruende = append(gruende, werkzeugDiff(*e.WerkzeugSnapshot, current)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(gruende) == 0 {
|
||||
continue
|
||||
}
|
||||
antrag, err := s.store.GetAntrag(r.Context(), e.AntragID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
data.Eintraege = append(data.Eintraege, wiedervorlageItem{
|
||||
AntragID: antrag.ID, Titel: antrag.Titel, Werkzeug: werkzeugName, Gruende: gruende,
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.templates.ExecuteTemplate(w, "wiedervorlage-liste", data); err != nil {
|
||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user