mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-21 13:55:43 +00:00
467 lines
12 KiB
Go
467 lines
12 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/xuri/excelize/v2"
|
|
)
|
|
|
|
const purchaseExportSheetName = "Purchases"
|
|
|
|
func isAllPurchaseExcelExportRequest(c *fiber.Ctx) bool {
|
|
return strings.EqualFold(strings.TrimSpace(c.Query("export")), "excel") &&
|
|
strings.EqualFold(strings.TrimSpace(c.Query("type")), "all")
|
|
}
|
|
|
|
func exportPurchaseListExcel(c *fiber.Ctx, purchases []entity.Purchase) error {
|
|
content, err := buildPurchaseExportWorkbook(purchases)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
|
|
}
|
|
|
|
filename := fmt.Sprintf("purchases_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 buildPurchaseExportWorkbook(purchases []entity.Purchase) ([]byte, error) {
|
|
file := excelize.NewFile()
|
|
defer file.Close()
|
|
|
|
defaultSheet := file.GetSheetName(file.GetActiveSheetIndex())
|
|
if defaultSheet != purchaseExportSheetName {
|
|
if err := file.SetSheetName(defaultSheet, purchaseExportSheetName); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := setPurchaseExportColumns(file, purchaseExportSheetName); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := setPurchaseExportHeaders(file, purchaseExportSheetName); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := setPurchaseExportRows(file, purchaseExportSheetName, purchases); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := file.SetPanes(purchaseExportSheetName, &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 setPurchaseExportColumns(file *excelize.File, sheet string) error {
|
|
columnWidths := map[string]float64{
|
|
"A": 16,
|
|
"B": 16,
|
|
"C": 14,
|
|
"D": 14,
|
|
"E": 22,
|
|
"F": 22,
|
|
"G": 22,
|
|
"H": 32,
|
|
"I": 10,
|
|
"J": 12,
|
|
"K": 16,
|
|
"L": 16,
|
|
"M": 22,
|
|
"N": 12,
|
|
"O": 16,
|
|
"P": 16,
|
|
"Q": 18,
|
|
"R": 18,
|
|
"S": 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 setPurchaseExportHeaders(file *excelize.File, sheet string) error {
|
|
headers := []string{
|
|
"PR Number", // A
|
|
"PO Number", // B
|
|
"Tanggal PO", // C
|
|
"Tanggal Terima", // D
|
|
"Supplier", // E
|
|
"Lokasi", // F
|
|
"Gudang", // G
|
|
"Product", // H
|
|
"Qty", // I
|
|
"Satuan", // J
|
|
"Price", // K
|
|
"Total Produk", // L
|
|
"Vendor Ekspedisi",// M
|
|
"Qty Ekspedisi", // N
|
|
"Price Ekspedisi", // O
|
|
"Total Ekspedisi", // P
|
|
"Grand Total All", // Q
|
|
"Status", // R
|
|
"Notes", // S
|
|
}
|
|
|
|
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", "S1", headerStyle)
|
|
}
|
|
|
|
func setPurchaseExportRows(file *excelize.File, sheet string, purchases []entity.Purchase) error {
|
|
if len(purchases) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var sumL, sumP, sumQ float64
|
|
|
|
rowIdx := 2
|
|
for p := range purchases {
|
|
purchase := &purchases[p]
|
|
if len(purchase.Items) == 0 {
|
|
if err := writePurchaseExportRow(file, sheet, rowIdx, purchase, nil, &sumL, &sumP, &sumQ); err != nil {
|
|
return err
|
|
}
|
|
rowIdx++
|
|
continue
|
|
}
|
|
for it := range purchase.Items {
|
|
if err := writePurchaseExportRow(file, sheet, rowIdx, purchase, &purchase.Items[it], &sumL, &sumP, &sumQ); err != nil {
|
|
return err
|
|
}
|
|
rowIdx++
|
|
}
|
|
}
|
|
|
|
lastDataRow := rowIdx - 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", "S"+strconv.Itoa(lastDataRow), 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
|
|
}
|
|
if err := file.SetCellStyle(sheet, "K2", "Q"+strconv.Itoa(lastDataRow), moneyStyle); err != nil {
|
|
return err
|
|
}
|
|
|
|
return addPurchaseExportSumRow(file, sheet, rowIdx, sumL, sumP, sumQ)
|
|
}
|
|
|
|
func writePurchaseExportRow(file *excelize.File, sheet string, rowIdx int, purchase *entity.Purchase, item *entity.PurchaseItem, sumL, sumP, sumQ *float64) error {
|
|
row := strconv.Itoa(rowIdx)
|
|
|
|
// Purchase-level columns (repeat for every item row of the same purchase)
|
|
if err := file.SetCellValue(sheet, "A"+row, safePurchaseExportText(purchase.PrNumber)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "B"+row, safePurchaseExportPointerText(purchase.PoNumber)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "C"+row, formatPurchaseExportDate(purchase.PoDate)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "E"+row, safePurchaseExportEntitySupplierName(purchase)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "R"+row, formatPurchaseExportEntityStatus(purchase)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "S"+row, safePurchaseExportPointerText(purchase.Notes)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if item == nil {
|
|
for _, col := range []string{"D", "F", "G", "H", "J", "M"} {
|
|
if err := file.SetCellValue(sheet, col+row, "-"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, col := range []string{"I", "K", "L", "N", "O", "P", "Q"} {
|
|
if err := file.SetCellValue(sheet, col+row, 0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Item-level columns
|
|
var expeditionQty, expeditionPrice, expeditionTotal float64
|
|
if item.ExpenseNonstock != nil {
|
|
expeditionQty = item.ExpenseNonstock.Qty
|
|
expeditionPrice = item.ExpenseNonstock.Price
|
|
expeditionTotal = expeditionQty * expeditionPrice
|
|
}
|
|
itemGrandTotal := item.TotalPrice + expeditionTotal
|
|
|
|
*sumL += item.TotalPrice
|
|
*sumP += expeditionTotal
|
|
*sumQ += itemGrandTotal
|
|
|
|
if err := file.SetCellValue(sheet, "D"+row, formatPurchaseExportDate(item.ReceivedDate)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "F"+row, safePurchaseItemLocationName(item)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "G"+row, safePurchaseWarehouseName(item)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "H"+row, safePurchaseItemProductName(item)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "I"+row, item.TotalQty); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "J"+row, safePurchaseItemUomName(item)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "K"+row, item.Price); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "L"+row, item.TotalPrice); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "M"+row, safePurchaseItemExpeditionVendorName(item)); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "N"+row, expeditionQty); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "O"+row, expeditionPrice); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "P"+row, expeditionTotal); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "Q"+row, itemGrandTotal); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func addPurchaseExportSumRow(file *excelize.File, sheet string, rowIdx int, sumL, sumP, sumQ float64) error {
|
|
row := strconv.Itoa(rowIdx)
|
|
|
|
sumStyle, err := file.NewStyle(&excelize.Style{
|
|
Font: &excelize.Font{Bold: true, Color: "1F2937"},
|
|
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"FEF3C7"}},
|
|
Alignment: &excelize.Alignment{
|
|
Horizontal: "left",
|
|
Vertical: "center",
|
|
},
|
|
Border: []excelize.Border{
|
|
{Type: "left", Color: "D1D5DB", Style: 1},
|
|
{Type: "top", Color: "D1D5DB", Style: 2},
|
|
{Type: "bottom", Color: "D1D5DB", Style: 1},
|
|
{Type: "right", Color: "D1D5DB", Style: 1},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sumMoneyStyle, err := file.NewStyle(&excelize.Style{
|
|
Font: &excelize.Font{Bold: true, Color: "1F2937"},
|
|
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"FEF3C7"}},
|
|
Alignment: &excelize.Alignment{
|
|
Horizontal: "right",
|
|
Vertical: "center",
|
|
},
|
|
Border: []excelize.Border{
|
|
{Type: "left", Color: "D1D5DB", Style: 1},
|
|
{Type: "top", Color: "D1D5DB", Style: 2},
|
|
{Type: "bottom", Color: "D1D5DB", Style: 1},
|
|
{Type: "right", Color: "D1D5DB", Style: 1},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := file.SetCellStyle(sheet, "A"+row, "S"+row, sumStyle); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellStyle(sheet, "L"+row, "L"+row, sumMoneyStyle); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellStyle(sheet, "P"+row, "Q"+row, sumMoneyStyle); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := file.SetCellValue(sheet, "A"+row, "TOTAL"); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "L"+row, sumL); err != nil {
|
|
return err
|
|
}
|
|
if err := file.SetCellValue(sheet, "P"+row, sumP); err != nil {
|
|
return err
|
|
}
|
|
return file.SetCellValue(sheet, "Q"+row, sumQ)
|
|
}
|
|
|
|
func safePurchaseExportEntitySupplierName(purchase *entity.Purchase) string {
|
|
if purchase.Supplier.Id == 0 {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(purchase.Supplier.Name)
|
|
}
|
|
|
|
func safePurchaseWarehouseName(item *entity.PurchaseItem) string {
|
|
if item.Warehouse == nil {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(item.Warehouse.Name)
|
|
}
|
|
|
|
func safePurchaseItemLocationName(item *entity.PurchaseItem) string {
|
|
if item.Warehouse == nil || item.Warehouse.Location == nil {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(item.Warehouse.Location.Name)
|
|
}
|
|
|
|
func safePurchaseItemProductName(item *entity.PurchaseItem) string {
|
|
if item.Product == nil {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(item.Product.Name)
|
|
}
|
|
|
|
func safePurchaseItemUomName(item *entity.PurchaseItem) string {
|
|
if item.Product == nil || item.Product.Uom.Id == 0 {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(item.Product.Uom.Name)
|
|
}
|
|
|
|
func safePurchaseItemExpeditionVendorName(item *entity.PurchaseItem) string {
|
|
if item.ExpenseNonstock == nil || item.ExpenseNonstock.Expense == nil {
|
|
return "-"
|
|
}
|
|
exp := item.ExpenseNonstock.Expense
|
|
if exp.Supplier == nil || exp.Supplier.Id == 0 {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(exp.Supplier.Name)
|
|
}
|
|
|
|
func formatPurchaseExportEntityStatus(purchase *entity.Purchase) string {
|
|
if purchase.LatestApproval == nil {
|
|
return "-"
|
|
}
|
|
|
|
if purchase.LatestApproval.Action != nil &&
|
|
strings.EqualFold(strings.TrimSpace(string(*purchase.LatestApproval.Action)), string(entity.ApprovalActionRejected)) {
|
|
return "Ditolak"
|
|
}
|
|
|
|
return safePurchaseExportText(purchase.LatestApproval.StepName)
|
|
}
|
|
|
|
func formatPurchaseExportDate(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 safePurchaseExportPointerText(value *string) string {
|
|
if value == nil {
|
|
return "-"
|
|
}
|
|
return safePurchaseExportText(*value)
|
|
}
|
|
|
|
func safePurchaseExportText(value string) string {
|
|
trimmed := strings.TrimSpace(value)
|
|
if trimmed == "" {
|
|
return "-"
|
|
}
|
|
return trimmed
|
|
}
|
|
|