mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 21:41:55 +00:00
311 lines
7.6 KiB
Go
311 lines
7.6 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"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": 18,
|
|
"F": 60,
|
|
"G": 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",
|
|
"Tanggal",
|
|
"Status",
|
|
"Customer",
|
|
"Grand Total",
|
|
"Products",
|
|
"Notes",
|
|
}
|
|
|
|
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", "G1", headerStyle)
|
|
}
|
|
|
|
func setMarketingExportRows(file *excelize.File, sheet string, items []dto.MarketingListDTO) error {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for i, item := range items {
|
|
rowNumber := i + 2
|
|
if err := file.SetCellValue(sheet, "A"+strconv.Itoa(rowNumber), safeMarketingExportText(item.SoNumber)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "B"+strconv.Itoa(rowNumber), formatMarketingExportDate(item.SoDate)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "C"+strconv.Itoa(rowNumber), formatMarketingExportStatus(item)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "D"+strconv.Itoa(rowNumber), safeMarketingExportText(item.Customer.Name)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "E"+strconv.Itoa(rowNumber), formatMarketingRupiah(sumMarketingGrandTotal(item.SalesOrder))); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "F"+strconv.Itoa(rowNumber), formatMarketingProducts(item.SalesOrder)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "G"+strconv.Itoa(rowNumber), safeMarketingExportText(item.Notes)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
lastRow := len(items) + 1
|
|
dataStyle, err := file.NewStyle(&excelize.Style{
|
|
Alignment: &excelize.Alignment{
|
|
Horizontal: "left",
|
|
Vertical: "center",
|
|
WrapText: true,
|
|
},
|
|
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
|
|
}
|
|
|
|
if err := file.SetCellStyle(sheet, "A2", "G"+strconv.Itoa(lastRow), dataStyle); err != nil {
|
|
return err
|
|
}
|
|
|
|
moneyStyle, err := file.NewStyle(&excelize.Style{
|
|
Alignment: &excelize.Alignment{
|
|
Horizontal: "right",
|
|
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, "E2", "E"+strconv.Itoa(lastRow), moneyStyle)
|
|
}
|
|
|
|
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 formatMarketingProducts(items []dto.DeliveryMarketingProductDTO) string {
|
|
if len(items) == 0 {
|
|
return "-"
|
|
}
|
|
|
|
seen := make(map[string]struct{})
|
|
names := make([]string, 0, len(items))
|
|
for _, item := range items {
|
|
if item.ProductWarehouse == nil || item.ProductWarehouse.Product == nil {
|
|
continue
|
|
}
|
|
|
|
name := strings.TrimSpace(item.ProductWarehouse.Product.Name)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
|
|
if _, exists := seen[name]; exists {
|
|
continue
|
|
}
|
|
seen[name] = struct{}{}
|
|
names = append(names, name)
|
|
}
|
|
|
|
if len(names) == 0 {
|
|
return "-"
|
|
}
|
|
|
|
return strings.Join(names, ", ")
|
|
}
|
|
|
|
func sumMarketingGrandTotal(items []dto.DeliveryMarketingProductDTO) float64 {
|
|
total := 0.0
|
|
for _, item := range items {
|
|
total += item.TotalPrice
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
func formatMarketingRupiah(value float64) string {
|
|
if math.IsNaN(value) || math.IsInf(value, 0) {
|
|
return "Rp 0"
|
|
}
|
|
|
|
rounded := int64(math.Round(value))
|
|
sign := ""
|
|
if rounded < 0 {
|
|
sign = "-"
|
|
rounded = -rounded
|
|
}
|
|
|
|
raw := strconv.FormatInt(rounded, 10)
|
|
if raw == "" {
|
|
raw = "0"
|
|
}
|
|
|
|
var grouped strings.Builder
|
|
rem := len(raw) % 3
|
|
if rem > 0 {
|
|
grouped.WriteString(raw[:rem])
|
|
if len(raw) > rem {
|
|
grouped.WriteString(".")
|
|
}
|
|
}
|
|
for i := rem; i < len(raw); i += 3 {
|
|
grouped.WriteString(raw[i : i+3])
|
|
if i+3 < len(raw) {
|
|
grouped.WriteString(".")
|
|
}
|
|
}
|
|
|
|
return "Rp " + sign + grouped.String()
|
|
}
|
|
|
|
func safeMarketingExportText(value string) string {
|
|
trimmed := strings.TrimSpace(value)
|
|
if trimmed == "" {
|
|
return "-"
|
|
}
|
|
return trimmed
|
|
}
|