package report import ( "bytes" "context" "fmt" "log/slog" "reflect" "strconv" "vctp/db" "github.com/xuri/excelize/v2" ) func CreateReport(logger *slog.Logger, Database db.Database, ctx context.Context) ([]byte, error) { var xlsx *excelize.File sheetName := "Inventory Report" var buffer bytes.Buffer logger.Debug("Querying inventory table") results, err := Database.Queries().ListInventory(ctx) if err != nil { logger.Error("Unable to query inventory table", "error", err) return nil, err } if len(results) == 0 { logger.Error("Empty inventory results") return nil, fmt.Errorf("Empty inventory results") } // Use reflection to determine column headings from the first item firstItem := results[0] v := reflect.ValueOf(firstItem) typeOfItem := v.Type() // Create column headers dynamically for i := 0; i < v.NumField(); i++ { column := string(rune('A'+i)) + "1" // A1, B1, C1, etc. xlsx.SetCellValue(sheetName, column, typeOfItem.Field(i).Name) } // Populate the Excel file with data from the Inventory table for i, item := range results { v = reflect.ValueOf(item) for j := 0; j < v.NumField(); j++ { column := string(rune('A'+j)) + strconv.Itoa(i+2) // Start from row 2 xlsx.SetCellValue(sheetName, column, v.Field(j).Interface()) } } // Save the Excel file into a byte buffer if err := xlsx.Write(&buffer); err != nil { return nil, err } return buffer.Bytes(), nil }