package services import ( "bytes" "encoding/csv" "fmt" "time" "github.com/mchus/quoteforge/internal/config" "github.com/mchus/quoteforge/internal/models" "github.com/xuri/excelize/v2" ) type ExportService struct { config config.ExportConfig } func NewExportService(cfg config.ExportConfig) *ExportService { return &ExportService{config: cfg} } type ExportData struct { Name string Items []ExportItem Total float64 Notes string CreatedAt time.Time } type ExportItem struct { LotName string Description string Category string Quantity int UnitPrice float64 TotalPrice float64 } func (s *ExportService) ToCSV(data *ExportData) ([]byte, error) { var buf bytes.Buffer w := csv.NewWriter(&buf) // Header headers := []string{"Артикул", "Описание", "Категория", "Количество", "Цена за единицу", "Сумма"} if err := w.Write(headers); err != nil { return nil, err } // Items for _, item := range data.Items { row := []string{ item.LotName, item.Description, item.Category, fmt.Sprintf("%d", item.Quantity), fmt.Sprintf("%.2f", item.UnitPrice), fmt.Sprintf("%.2f", item.TotalPrice), } if err := w.Write(row); err != nil { return nil, err } } // Total row if err := w.Write([]string{"", "", "", "", "ИТОГО:", fmt.Sprintf("%.2f", data.Total)}); err != nil { return nil, err } w.Flush() return buf.Bytes(), w.Error() } func (s *ExportService) ToXLSX(data *ExportData) ([]byte, error) { f := excelize.NewFile() sheet := "Конфигурация" f.SetSheetName("Sheet1", sheet) // Styles headerStyle, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true, Size: 12, Color: "#FFFFFF"}, Fill: excelize.Fill{Type: "pattern", Color: []string{"#4472C4"}, Pattern: 1}, Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center"}, Border: []excelize.Border{ {Type: "left", Color: "#000000", Style: 1}, {Type: "top", Color: "#000000", Style: 1}, {Type: "bottom", Color: "#000000", Style: 1}, {Type: "right", Color: "#000000", Style: 1}, }, }) totalStyle, _ := f.NewStyle(&excelize.Style{ Font: &excelize.Font{Bold: true, Size: 12}, Fill: excelize.Fill{Type: "pattern", Color: []string{"#E2EFDA"}, Pattern: 1}, }) priceStyle, _ := f.NewStyle(&excelize.Style{ NumFmt: 4, // #,##0.00 }) // Title f.SetCellValue(sheet, "A1", s.config.CompanyName) f.SetCellValue(sheet, "A2", "Коммерческое предложение: "+data.Name) f.SetCellValue(sheet, "A3", "Дата: "+data.CreatedAt.Format("02.01.2006")) // Headers headers := []string{"Артикул", "Описание", "Категория", "Кол-во", "Цена", "Сумма"} for i, h := range headers { cell := fmt.Sprintf("%c5", 'A'+i) f.SetCellValue(sheet, cell, h) f.SetCellStyle(sheet, cell, cell, headerStyle) } // Data rows row := 6 for _, item := range data.Items { f.SetCellValue(sheet, fmt.Sprintf("A%d", row), item.LotName) f.SetCellValue(sheet, fmt.Sprintf("B%d", row), item.Description) f.SetCellValue(sheet, fmt.Sprintf("C%d", row), item.Category) f.SetCellValue(sheet, fmt.Sprintf("D%d", row), item.Quantity) f.SetCellValue(sheet, fmt.Sprintf("E%d", row), item.UnitPrice) f.SetCellValue(sheet, fmt.Sprintf("F%d", row), item.TotalPrice) f.SetCellStyle(sheet, fmt.Sprintf("E%d", row), fmt.Sprintf("F%d", row), priceStyle) row++ } // Total row f.SetCellValue(sheet, fmt.Sprintf("E%d", row), "ИТОГО:") f.SetCellValue(sheet, fmt.Sprintf("F%d", row), data.Total) f.SetCellStyle(sheet, fmt.Sprintf("E%d", row), fmt.Sprintf("F%d", row), totalStyle) // Notes if data.Notes != "" { row += 2 f.SetCellValue(sheet, fmt.Sprintf("A%d", row), "Примечания: "+data.Notes) } // Column widths f.SetColWidth(sheet, "A", "A", 25) f.SetColWidth(sheet, "B", "B", 50) f.SetColWidth(sheet, "C", "C", 15) f.SetColWidth(sheet, "D", "D", 10) f.SetColWidth(sheet, "E", "E", 15) f.SetColWidth(sheet, "F", "F", 15) var buf bytes.Buffer if err := f.Write(&buf); err != nil { return nil, err } return buf.Bytes(), nil } func (s *ExportService) ConfigToExportData(config *models.Configuration) *ExportData { items := make([]ExportItem, len(config.Items)) var total float64 for i, item := range config.Items { itemTotal := item.UnitPrice * float64(item.Quantity) items[i] = ExportItem{ LotName: item.LotName, Quantity: item.Quantity, UnitPrice: item.UnitPrice, TotalPrice: itemTotal, } total += itemTotal } return &ExportData{ Name: config.Name, Items: items, Total: total, Notes: config.Notes, CreatedAt: config.CreatedAt, } }