mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 15:25:43 +00:00
add export excel all expenses
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/dto"
|
||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||
supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
const expenseExportSheetName = "Expenses"
|
||||
|
||||
func isAllExpenseExcelExportRequest(c *fiber.Ctx) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(c.Query("export")), "excel") &&
|
||||
strings.EqualFold(strings.TrimSpace(c.Query("type")), "all")
|
||||
}
|
||||
|
||||
func exportExpenseListExcel(c *fiber.Ctx, items []dto.ExpenseListDTO) error {
|
||||
content, err := buildExpenseExportWorkbook(items)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("expenses_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 buildExpenseExportWorkbook(items []dto.ExpenseListDTO) ([]byte, error) {
|
||||
file := excelize.NewFile()
|
||||
defer file.Close()
|
||||
|
||||
defaultSheet := file.GetSheetName(file.GetActiveSheetIndex())
|
||||
if defaultSheet != expenseExportSheetName {
|
||||
if err := file.SetSheetName(defaultSheet, expenseExportSheetName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := setExpenseExportColumns(file, expenseExportSheetName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setExpenseExportHeaders(file, expenseExportSheetName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setExpenseExportRows(file, expenseExportSheetName, items); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := file.SetPanes(expenseExportSheetName, &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 setExpenseExportColumns(file *excelize.File, sheet string) error {
|
||||
columnWidths := map[string]float64{
|
||||
"A": 8,
|
||||
"B": 16,
|
||||
"C": 20,
|
||||
"D": 18,
|
||||
"E": 18,
|
||||
"F": 16,
|
||||
"G": 24,
|
||||
"H": 22,
|
||||
"I": 16,
|
||||
"J": 24,
|
||||
}
|
||||
|
||||
for col, width := range columnWidths {
|
||||
if err := file.SetColWidth(sheet, col, col, width); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return file.SetRowHeight(sheet, 1, 24)
|
||||
}
|
||||
|
||||
func setExpenseExportHeaders(file *excelize.File, sheet string) error {
|
||||
headers := []string{
|
||||
"No",
|
||||
"No. PO",
|
||||
"No. Referensi",
|
||||
"Tanggal Realisasi",
|
||||
"Tanggal Transaksi",
|
||||
"Kategori",
|
||||
"Produk",
|
||||
"Lokasi",
|
||||
"Grand Total",
|
||||
"Status",
|
||||
}
|
||||
|
||||
for i, header := range headers {
|
||||
colName, err := excelize.ColumnNumberToName(i + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, colName+"1", 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", "J1", headerStyle)
|
||||
}
|
||||
|
||||
func setExpenseExportRows(file *excelize.File, sheet string, items []dto.ExpenseListDTO) error {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, item := range items {
|
||||
row := strconv.Itoa(i + 2)
|
||||
if err := file.SetCellValue(sheet, "A"+row, i+1); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "B"+row, safeExpenseExportText(item.PoNumber)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "C"+row, safeExpenseExportText(item.ReferenceNumber)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "D"+row, formatExpenseExportDate(item.RealizationDate)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "E"+row, formatExpenseExportDate(&item.TransactionDate)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "F"+row, safeExpenseExportText(item.Category)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "G"+row, safeExpenseSupplierName(item.Supplier)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "H"+row, safeExpenseLocationName(item.Location)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "I"+row, safeExpenseExportNumber(item.GrandTotal)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, "J"+row, formatExpenseExportStatus(item.LatestApproval)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lastRow := len(items) + 1
|
||||
|
||||
dataStyle, err := file.NewStyle(&excelize.Style{
|
||||
Alignment: &excelize.Alignment{
|
||||
Horizontal: "left",
|
||||
Vertical: "center",
|
||||
WrapText: false,
|
||||
},
|
||||
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", "J"+strconv.Itoa(lastRow), dataStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ordinalStyle, err := file.NewStyle(&excelize.Style{
|
||||
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
|
||||
}
|
||||
if err := file.SetCellStyle(sheet, "A2", "A"+strconv.Itoa(lastRow), ordinalStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numberFormat := "#,##0.##"
|
||||
numberStyle, err := file.NewStyle(&excelize.Style{
|
||||
Alignment: &excelize.Alignment{
|
||||
Horizontal: "right",
|
||||
Vertical: "center",
|
||||
},
|
||||
CustomNumFmt: &numberFormat,
|
||||
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, "I2", "I"+strconv.Itoa(lastRow), numberStyle)
|
||||
}
|
||||
|
||||
func formatExpenseExportDate(value *time.Time) string {
|
||||
if value == nil || value.IsZero() {
|
||||
return "-"
|
||||
}
|
||||
|
||||
t := *value
|
||||
location, err := time.LoadLocation("Asia/Jakarta")
|
||||
if err == nil {
|
||||
t = t.In(location)
|
||||
}
|
||||
|
||||
return t.Format("02-01-2006")
|
||||
}
|
||||
|
||||
func formatExpenseExportStatus(latestApproval *approvalDTO.ApprovalRelationDTO) string {
|
||||
if latestApproval == nil {
|
||||
return "-"
|
||||
}
|
||||
|
||||
if latestApproval.Action != nil &&
|
||||
strings.EqualFold(strings.TrimSpace(*latestApproval.Action), string(entity.ApprovalActionRejected)) {
|
||||
return "Ditolak"
|
||||
}
|
||||
|
||||
return safeExpenseExportText(latestApproval.StepName)
|
||||
}
|
||||
|
||||
func safeExpenseSupplierName(value *supplierDTO.SupplierRelationDTO) string {
|
||||
if value == nil {
|
||||
return "-"
|
||||
}
|
||||
return safeExpenseExportText(value.Name)
|
||||
}
|
||||
|
||||
func safeExpenseLocationName(value *locationDTO.LocationRelationDTO) string {
|
||||
if value == nil {
|
||||
return "-"
|
||||
}
|
||||
return safeExpenseExportText(value.Name)
|
||||
}
|
||||
|
||||
func safeExpenseExportNumber(value float64) float64 {
|
||||
if math.IsNaN(value) || math.IsInf(value, 0) {
|
||||
return 0
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func safeExpenseExportText(value string) string {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return "-"
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
Reference in New Issue
Block a user