Files
lti-api/internal/modules/marketing/controllers/deliveryorder.export.go
T

348 lines
8.8 KiB
Go

package controller
import (
"fmt"
"strconv"
"strings"
"time"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/dto"
"github.com/gofiber/fiber/v2"
"github.com/xuri/excelize/v2"
)
const marketingExportSheetName = "Marketings"
func isAllExcelExportRequest(c *fiber.Ctx) bool {
return strings.EqualFold(strings.TrimSpace(c.Query("export")), "excel") &&
strings.EqualFold(strings.TrimSpace(c.Query("type")), "all")
}
func exportMarketingListExcel(c *fiber.Ctx, items []dto.MarketingListDTO) error {
content, err := buildMarketingExportWorkbook(items)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
}
filename := fmt.Sprintf("marketings_all_%s.xlsx", time.Now().Format("20060102_150405"))
c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
return c.Status(fiber.StatusOK).Send(content)
}
func buildMarketingExportWorkbook(items []dto.MarketingListDTO) ([]byte, error) {
file := excelize.NewFile()
defer file.Close()
defaultSheet := file.GetSheetName(file.GetActiveSheetIndex())
if defaultSheet != marketingExportSheetName {
if err := file.SetSheetName(defaultSheet, marketingExportSheetName); err != nil {
return nil, err
}
}
if err := setMarketingExportColumns(file, marketingExportSheetName); err != nil {
return nil, err
}
if err := setMarketingExportHeaders(file, marketingExportSheetName); err != nil {
return nil, err
}
if err := setMarketingExportRows(file, marketingExportSheetName, items); err != nil {
return nil, err
}
if err := file.SetPanes(marketingExportSheetName, &excelize.Panes{
Freeze: true,
YSplit: 1,
TopLeftCell: "A2",
ActivePane: "bottomLeft",
}); err != nil {
return nil, err
}
buffer, err := file.WriteToBuffer()
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func setMarketingExportColumns(file *excelize.File, sheet string) error {
columnWidths := map[string]float64{
"A": 16,
"B": 14,
"C": 18,
"D": 20,
"E": 14,
"F": 40,
"G": 10,
"H": 12,
"I": 12,
"J": 12,
"K": 16,
"L": 16,
"M": 18,
"N": 18,
"O": 18,
"P": 24,
}
for col, width := range columnWidths {
if err := file.SetColWidth(sheet, col, col, width); err != nil {
return err
}
}
if err := file.SetRowHeight(sheet, 1, 24); err != nil {
return err
}
return nil
}
func setMarketingExportHeaders(file *excelize.File, sheet string) error {
headers := []string{
"No. Order", // A
"Tanggal", // B
"Status", // C
"Customer", // D
"Tipe", // E
"Nama Produk", // F
"Week", // G
"Jumlah", // H
"Satuan", // I
"Qty Peti", // J
"Berat Rata-rata (kg)", // K
"Total Berat (kg)", // L
"Harga Satuan", // M
"Total Harga", // N
"Grand Total", // O
"Catatan", // P
}
for i, header := range headers {
colName, err := excelize.ColumnNumberToName(i + 1)
if err != nil {
return err
}
cell := colName + "1"
if err := file.SetCellValue(sheet, cell, header); err != nil {
return err
}
}
headerStyle, err := file.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Color: "1F2937"},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"DCEBFA"}},
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center"},
Border: []excelize.Border{
{Type: "left", Color: "D1D5DB", Style: 1},
{Type: "top", Color: "D1D5DB", Style: 1},
{Type: "bottom", Color: "D1D5DB", Style: 1},
{Type: "right", Color: "D1D5DB", Style: 1},
},
})
if err != nil {
return err
}
return file.SetCellStyle(sheet, "A1", "P1", headerStyle)
}
func setMarketingExportRows(file *excelize.File, sheet string, items []dto.MarketingListDTO) error {
if len(items) == 0 {
return nil
}
row := 1
for _, item := range items {
soNumber := safeMarketingExportText(item.SoNumber)
soDate := formatMarketingExportDate(item.SoDate)
status := formatMarketingExportStatus(item)
customer := safeMarketingExportText(item.Customer.Name)
grandTotal := sumMarketingGrandTotal(item.SalesOrder)
notes := safeMarketingExportText(item.Notes)
if len(item.SalesOrder) == 0 {
row++
r := strconv.Itoa(row)
vals := map[string]interface{}{
"A": soNumber, "B": soDate, "C": status, "D": customer,
"E": "-", "F": "-", "G": "-", "H": "-", "I": "-", "J": "-",
"K": "-", "L": "-", "M": "-", "N": "-",
"O": grandTotal, "P": notes,
}
for col, val := range vals {
if err := file.SetCellValue(sheet, col+r, val); err != nil {
return err
}
}
continue
}
for _, prod := range item.SalesOrder {
row++
r := strconv.Itoa(row)
productName := "-"
if prod.ProductWarehouse != nil && prod.ProductWarehouse.Product != nil {
if n := strings.TrimSpace(prod.ProductWarehouse.Product.Name); n != "" {
productName = n
}
}
week := "-"
if prod.Week != nil {
week = strconv.Itoa(*prod.Week)
}
satuan := "-"
if prod.ConvertionUnit != nil && strings.TrimSpace(*prod.ConvertionUnit) != "" {
satuan = *prod.ConvertionUnit
}
if err := file.SetCellValue(sheet, "A"+r, soNumber); err != nil {
return err
}
if err := file.SetCellValue(sheet, "B"+r, soDate); err != nil {
return err
}
if err := file.SetCellValue(sheet, "C"+r, status); err != nil {
return err
}
if err := file.SetCellValue(sheet, "D"+r, customer); err != nil {
return err
}
if err := file.SetCellValue(sheet, "E"+r, safeMarketingExportText(prod.MarketingType)); err != nil {
return err
}
if err := file.SetCellValue(sheet, "F"+r, productName); err != nil {
return err
}
if err := file.SetCellValue(sheet, "G"+r, week); err != nil {
return err
}
if err := file.SetCellValue(sheet, "H"+r, prod.Qty); err != nil {
return err
}
if err := file.SetCellValue(sheet, "I"+r, satuan); err != nil {
return err
}
if prod.TotalPeti != nil {
if err := file.SetCellValue(sheet, "J"+r, *prod.TotalPeti); err != nil {
return err
}
} else {
if err := file.SetCellValue(sheet, "J"+r, "-"); err != nil {
return err
}
}
if err := file.SetCellValue(sheet, "K"+r, prod.AvgWeight); err != nil {
return err
}
if err := file.SetCellValue(sheet, "L"+r, prod.TotalWeight); err != nil {
return err
}
if err := file.SetCellValue(sheet, "M"+r, prod.UnitPrice); err != nil {
return err
}
if err := file.SetCellValue(sheet, "N"+r, prod.TotalPrice); err != nil {
return err
}
if err := file.SetCellValue(sheet, "O"+r, grandTotal); err != nil {
return err
}
if err := file.SetCellValue(sheet, "P"+r, notes); err != nil {
return err
}
}
}
lastRow := row
lastRowStr := strconv.Itoa(lastRow)
border := []excelize.Border{
{Type: "left", Color: "D1D5DB", Style: 1},
{Type: "top", Color: "D1D5DB", Style: 1},
{Type: "bottom", Color: "D1D5DB", Style: 1},
{Type: "right", Color: "D1D5DB", Style: 1},
}
dataStyle, err := file.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{Horizontal: "left", Vertical: "center", WrapText: true},
Border: border,
})
if err != nil {
return err
}
if err := file.SetCellStyle(sheet, "A2", "P"+lastRowStr, dataStyle); err != nil {
return err
}
numberStyle, err := file.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{Horizontal: "right", Vertical: "center"},
Border: border,
})
if err != nil {
return err
}
if err := file.SetCellStyle(sheet, "K2", "O"+lastRowStr, numberStyle); err != nil {
return err
}
centerStyle, err := file.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center"},
Border: border,
})
if err != nil {
return err
}
for _, col := range []string{"G", "H", "J"} {
if err := file.SetCellStyle(sheet, col+"2", col+lastRowStr, centerStyle); err != nil {
return err
}
}
return nil
}
func formatMarketingExportDate(value time.Time) string {
if value.IsZero() {
return "-"
}
location, err := time.LoadLocation("Asia/Jakarta")
if err == nil {
value = value.In(location)
}
return value.Format("02-01-2006")
}
func formatMarketingExportStatus(item dto.MarketingListDTO) string {
if item.LatestApproval.Action != nil && strings.EqualFold(strings.TrimSpace(*item.LatestApproval.Action), string(entity.ApprovalActionRejected)) {
return "Ditolak"
}
return safeMarketingExportText(item.LatestApproval.StepName)
}
func sumMarketingGrandTotal(items []dto.DeliveryMarketingProductDTO) float64 {
total := 0.0
for _, item := range items {
total += item.TotalPrice
}
return total
}
func safeMarketingExportText(value string) string {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return "-"
}
return trimmed
}