package iace import ( "fmt" "github.com/xuri/excelize/v2" ) // FMEAExportRow represents one row in the VDA FMEA worksheet. type FMEAExportRow struct { ComponentName string `json:"component_name"` ComponentType string `json:"component_type"` FailureMode string `json:"failure_mode"` FailureEffect string `json:"failure_effect"` FailureCause string `json:"failure_cause"` Severity int `json:"severity"` Occurrence int `json:"occurrence"` Detection int `json:"detection"` RPZ int `json:"rpz"` AP string `json:"ap"` Measure string `json:"measure"` DetectionHint string `json:"detection_hint"` } // GenerateFMEAExcel creates a VDA-format FMEA worksheet as xlsx bytes. func GenerateFMEAExcel(projectName string, rows []FMEAExportRow) ([]byte, error) { f := excelize.NewFile() sheet := "FMEA-Worksheet" f.SetSheetName("Sheet1", sheet) // Column widths widths := map[string]float64{ "A": 6, "B": 25, "C": 14, "D": 28, "E": 30, "F": 28, "G": 6, "H": 6, "I": 6, "J": 8, "K": 6, "L": 30, "M": 25, } for col, w := range widths { _ = f.SetColWidth(sheet, col, col, w) } // Header style headerStyle, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"}, Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}}, Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true}, Border: []excelize.Border{ {Type: "left", Color: "000000", Style: 1}, {Type: "right", Color: "000000", Style: 1}, {Type: "top", Color: "000000", Style: 1}, {Type: "bottom", Color: "000000", Style: 1}, }, }) // Title row f.SetCellValue(sheet, "A1", fmt.Sprintf("FMEA-Worksheet — %s", projectName)) titleStyle, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true, Size: 14}, }) f.SetCellStyle(sheet, "A1", "A1", titleStyle) f.MergeCell(sheet, "A1", "M1") // Sub-header f.SetCellValue(sheet, "A2", "AIAG-VDA FMEA Format | AP = Action Priority (H/M/L)") f.MergeCell(sheet, "A2", "M2") // Column headers (row 4) headers := []string{"Nr.", "Komponente", "Typ", "Fehlerart", "Fehlerfolge", "Fehlerursache", "S", "O", "D", "RPZ", "AP", "Empfohlene Massnahme", "Erkennung"} for i, h := range headers { cell := fmt.Sprintf("%s4", string(rune('A'+i))) f.SetCellValue(sheet, cell, h) f.SetCellStyle(sheet, cell, cell, headerStyle) } // Data rows dataStyle, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{WrapText: true, Vertical: "top"}, Border: []excelize.Border{ {Type: "left", Color: "D0D0D0", Style: 1}, {Type: "right", Color: "D0D0D0", Style: 1}, {Type: "bottom", Color: "D0D0D0", Style: 1}, }, }) apHigh, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true, Color: "FFFFFF"}, Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"FF0000"}}, Alignment: &excelize.Alignment{Horizontal: "center"}, }) apMed, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true}, Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"FFD700"}}, Alignment: &excelize.Alignment{Horizontal: "center"}, }) apLow, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true, Color: "FFFFFF"}, Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"00B050"}}, Alignment: &excelize.Alignment{Horizontal: "center"}, }) for i, row := range rows { r := i + 5 // data starts at row 5 f.SetCellValue(sheet, fmt.Sprintf("A%d", r), i+1) f.SetCellValue(sheet, fmt.Sprintf("B%d", r), row.ComponentName) f.SetCellValue(sheet, fmt.Sprintf("C%d", r), row.ComponentType) f.SetCellValue(sheet, fmt.Sprintf("D%d", r), row.FailureMode) f.SetCellValue(sheet, fmt.Sprintf("E%d", r), row.FailureEffect) f.SetCellValue(sheet, fmt.Sprintf("F%d", r), row.FailureCause) f.SetCellValue(sheet, fmt.Sprintf("G%d", r), row.Severity) f.SetCellValue(sheet, fmt.Sprintf("H%d", r), row.Occurrence) f.SetCellValue(sheet, fmt.Sprintf("I%d", r), row.Detection) f.SetCellValue(sheet, fmt.Sprintf("J%d", r), row.RPZ) f.SetCellValue(sheet, fmt.Sprintf("K%d", r), row.AP) f.SetCellValue(sheet, fmt.Sprintf("L%d", r), row.Measure) f.SetCellValue(sheet, fmt.Sprintf("M%d", r), row.DetectionHint) // Style data cells for c := 0; c < 13; c++ { cell := fmt.Sprintf("%s%d", string(rune('A'+c)), r) f.SetCellStyle(sheet, cell, cell, dataStyle) } // AP color apCell := fmt.Sprintf("K%d", r) switch row.AP { case "H": f.SetCellStyle(sheet, apCell, apCell, apHigh) case "M": f.SetCellStyle(sheet, apCell, apCell, apMed) case "L": f.SetCellStyle(sheet, apCell, apCell, apLow) } } buf, err := f.WriteToBuffer() if err != nil { return nil, fmt.Errorf("excel write: %w", err) } return buf.Bytes(), nil }