// Ebene 3 — Register und Export (Schritt 6 der Baureihenfolge, siehe // CLAUDE.md). Jede Genehmigung erzeugt automatisch einen Registereintrag // (siehe handleFallEntscheiden); diese Seiten zeigen und exportieren // sie. Reuse des bereits vorhandenen (bisher ungenutzten) go-pdf/fpdf- // Dependency statt einer neuen Bibliothek für den PDF-Export. package web import ( "encoding/csv" "fmt" "net/http" "strings" "github.com/go-pdf/fpdf" ) type registerEintragView struct { Zweck string Abteilung string Werkzeug string Datenklasse string Einstufung string Auflagen string Verantwortlicher string EntschiedenAm string GueltigBis string } type registerListeData struct { Title string Nav navData Eintraege []registerEintragView } func (s *Server) registerEintraegeFuerAccount(r *http.Request) ([]registerEintragView, error) { eintraege, err := s.store.ListRegistereintraegeForAccount(r.Context(), currentUser(r).AccountID) if err != nil { return nil, err } views := make([]registerEintragView, len(eintraege)) for i, e := range eintraege { gueltigBis := "" if e.GueltigBis != nil { gueltigBis = e.GueltigBis.Format("02.01.2006") } views[i] = registerEintragView{ Zweck: e.Zweck, Abteilung: e.Abteilung, Werkzeug: e.Werkzeug, Datenklasse: e.Datenklasse, Einstufung: e.Einstufung, Auflagen: strings.Join(e.Auflagen, "; "), Verantwortlicher: e.Verantwortlicher, EntschiedenAm: e.EntschiedenAm.Format("02.01.2006 15:04"), GueltigBis: gueltigBis, } } return views, nil } // handleRegisterListe zeigt das Register (alle Genehmigungen des // Mandanten) — Ebene 3, für verantwortlicher und pruefer gleichermaßen // lesbar. func (s *Server) handleRegisterListe(w http.ResponseWriter, r *http.Request) { views, err := s.registerEintraegeFuerAccount(r) if err != nil { http.Error(w, "Register konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } data := registerListeData{Title: "Register", Nav: navFor(r), Eintraege: views} if err := s.templates.ExecuteTemplate(w, "register-liste", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } } var registerCSVHeader = []string{"Zweck", "Abteilung", "Werkzeug", "Datenklasse", "Einstufung", "Auflagen", "Verantwortliche/r", "Entschieden am", "Gültig bis"} func registerCSVRow(e registerEintragView) []string { return []string{e.Zweck, e.Abteilung, e.Werkzeug, e.Datenklasse, e.Einstufung, e.Auflagen, e.Verantwortlicher, e.EntschiedenAm, e.GueltigBis} } // handleRegisterExportCSV exportiert das Register als CSV. func (s *Server) handleRegisterExportCSV(w http.ResponseWriter, r *http.Request) { views, err := s.registerEintraegeFuerAccount(r) if err != nil { http.Error(w, "Register konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/csv; charset=utf-8") w.Header().Set("Content-Disposition", `attachment; filename="register.csv"`) cw := csv.NewWriter(w) _ = cw.Write(registerCSVHeader) for _, e := range views { _ = cw.Write(registerCSVRow(e)) } cw.Flush() } // handleRegisterExportPDF exportiert das Register als PDF — eine // einfache Tabelle, keine Gestaltung über den Design-System-Anspruch // des restlichen Produkts hinaus. func (s *Server) handleRegisterExportPDF(w http.ResponseWriter, r *http.Request) { views, err := s.registerEintraegeFuerAccount(r) if err != nil { http.Error(w, "Register konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } pdf := fpdf.New("L", "mm", "A4", "") // cp1252 deckt deutsche Umlaute/ß ab — die eingebauten Helvetica- // Fonts von fpdf können sonst kein UTF-8 darstellen. tr := pdf.UnicodeTranslatorFromDescriptor("") pdf.AddPage() pdf.SetFont("Helvetica", "B", 14) pdf.CellFormat(0, 10, tr("Register"), "", 1, "L", false, 0, "") pdf.Ln(2) colWidths := []float64{45, 25, 35, 30, 30, 55, 40, 30, 25} pdf.SetFont("Helvetica", "B", 8) for i, h := range registerCSVHeader { pdf.CellFormat(colWidths[i], 8, tr(h), "1", 0, "L", false, 0, "") } pdf.Ln(-1) pdf.SetFont("Helvetica", "", 8) for _, e := range views { row := registerCSVRow(e) for i, cell := range row { pdf.CellFormat(colWidths[i], 8, tr(cell), "1", 0, "L", false, 0, "") } pdf.Ln(-1) } w.Header().Set("Content-Type", "application/pdf") w.Header().Set("Content-Disposition", `attachment; filename="register.pdf"`) if err := pdf.Output(w); err != nil { http.Error(w, fmt.Sprintf("PDF konnte nicht erzeugt werden: %v", err), http.StatusInternalServerError) } }