mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-21 13:55:43 +00:00
adjust export format purchase and filter
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/finance/transactions/dto"
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/finance/transactions/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/finance/transactions/validations"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
const transactionExcelExportFetchLimit = 99999999
|
||||
|
||||
type TransactionController struct {
|
||||
TransactionService service.TransactionService
|
||||
}
|
||||
@@ -107,6 +110,14 @@ func (u *TransactionController) GetAll(c *fiber.Ctx) error {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||
}
|
||||
|
||||
if isTransactionExcelExportRequest(c) {
|
||||
results, err := u.getAllTransactionsForExcel(c, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return exportTransactionListExcel(c, results)
|
||||
}
|
||||
|
||||
result, totalResults, err := u.TransactionService.GetAll(c, query)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -149,6 +160,32 @@ func (u *TransactionController) GetOne(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
func isTransactionExcelExportRequest(c *fiber.Ctx) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(c.Query("export")), "excel")
|
||||
}
|
||||
|
||||
func (u *TransactionController) getAllTransactionsForExcel(c *fiber.Ctx, baseQuery *validation.Query) ([]entity.Payment, error) {
|
||||
query := *baseQuery
|
||||
query.Page = 1
|
||||
query.Limit = transactionExcelExportFetchLimit
|
||||
results := make([]entity.Payment, 0)
|
||||
for {
|
||||
pageResults, total, err := u.TransactionService.GetAll(c, &query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pageResults) == 0 || total == 0 {
|
||||
break
|
||||
}
|
||||
results = append(results, pageResults...)
|
||||
if int64(len(results)) >= total {
|
||||
break
|
||||
}
|
||||
query.Page++
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (u *TransactionController) DeleteOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/xuri/excelize/v2"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
)
|
||||
|
||||
const transactionExportSheetName = "Transaksi"
|
||||
|
||||
func exportTransactionListExcel(c *fiber.Ctx, payments []entity.Payment) error {
|
||||
content, err := buildTransactionExportWorkbook(payments)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("transaksi_%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 buildTransactionExportWorkbook(payments []entity.Payment) ([]byte, error) {
|
||||
file := excelize.NewFile()
|
||||
defer file.Close()
|
||||
|
||||
defaultSheet := file.GetSheetName(file.GetActiveSheetIndex())
|
||||
if defaultSheet != transactionExportSheetName {
|
||||
if err := file.SetSheetName(defaultSheet, transactionExportSheetName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := setTransactionExportColumns(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setTransactionExportHeaders(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setTransactionExportRows(file, payments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := file.SetPanes(transactionExportSheetName, &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 setTransactionExportColumns(file *excelize.File) error {
|
||||
columnWidths := map[string]float64{
|
||||
"A": 20,
|
||||
"B": 22,
|
||||
"C": 18,
|
||||
"D": 25,
|
||||
"E": 14,
|
||||
"F": 16,
|
||||
"G": 16,
|
||||
"H": 22,
|
||||
"I": 22,
|
||||
"J": 18,
|
||||
"K": 18,
|
||||
"L": 18,
|
||||
"M": 30,
|
||||
"N": 22,
|
||||
"O": 20,
|
||||
}
|
||||
|
||||
sheet := transactionExportSheetName
|
||||
for col, width := range columnWidths {
|
||||
if err := file.SetColWidth(sheet, col, col, width); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return file.SetRowHeight(sheet, 1, 24)
|
||||
}
|
||||
|
||||
func setTransactionExportHeaders(file *excelize.File) error {
|
||||
sheet := transactionExportSheetName
|
||||
headers := []string{
|
||||
"Kode Pembayaran",
|
||||
"No. Referensi",
|
||||
"Tipe Transaksi",
|
||||
"Pihak",
|
||||
"Tipe Pihak",
|
||||
"Tanggal Bayar",
|
||||
"Metode Bayar",
|
||||
"Bank",
|
||||
"No. Rekening Bank",
|
||||
"Pemasukan",
|
||||
"Pengeluaran",
|
||||
"Nominal",
|
||||
"Catatan",
|
||||
"Dibuat Oleh",
|
||||
"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", "O1", headerStyle)
|
||||
}
|
||||
|
||||
func setTransactionExportRows(file *excelize.File, payments []entity.Payment) error {
|
||||
if len(payments) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sheet := transactionExportSheetName
|
||||
for i, p := range payments {
|
||||
row := strconv.Itoa(i + 2)
|
||||
if err := writeTransactionExportRow(file, sheet, row, p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lastRow := strconv.Itoa(len(payments) + 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", "O"+lastRow, dataStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numericStyle, 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, "J2", "L"+lastRow, numericStyle)
|
||||
}
|
||||
|
||||
func writeTransactionExportRow(file *excelize.File, sheet, row string, p entity.Payment) error {
|
||||
incomeAmount, expenseAmount := txAmounts(p.Direction, p.Nominal)
|
||||
|
||||
values := []interface{}{
|
||||
safeTxText(p.PaymentCode),
|
||||
safeTxRefNumber(p.ReferenceNumber),
|
||||
safeTxText(txTransactionType(p)),
|
||||
safeTxText(txPartyName(p)),
|
||||
safeTxText(p.PartyType),
|
||||
formatTxDate(p.PaymentDate),
|
||||
safeTxText(p.PaymentMethod),
|
||||
safeTxBank(p),
|
||||
safeTxBankAccount(p),
|
||||
incomeAmount,
|
||||
expenseAmount,
|
||||
p.Nominal,
|
||||
safeTxText(p.Notes),
|
||||
safeTxText(txCreatedBy(p)),
|
||||
formatTxStatus(p),
|
||||
}
|
||||
|
||||
for colIdx, val := range values {
|
||||
colName, err := excelize.ColumnNumberToName(colIdx + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.SetCellValue(sheet, colName+row, val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func safeTxText(s string) string {
|
||||
trimmed := strings.TrimSpace(s)
|
||||
if trimmed == "" {
|
||||
return "-"
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
func safeTxRefNumber(s *string) string {
|
||||
if s == nil {
|
||||
return "-"
|
||||
}
|
||||
return safeTxText(*s)
|
||||
}
|
||||
|
||||
func safeTxBank(p entity.Payment) string {
|
||||
if p.BankWarehouse.Id == 0 {
|
||||
return "-"
|
||||
}
|
||||
return safeTxText(p.BankWarehouse.Name)
|
||||
}
|
||||
|
||||
func safeTxBankAccount(p entity.Payment) string {
|
||||
if p.BankWarehouse.Id == 0 {
|
||||
return "-"
|
||||
}
|
||||
return safeTxText(p.BankWarehouse.AccountNumber)
|
||||
}
|
||||
|
||||
func formatTxDate(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "-"
|
||||
}
|
||||
loc, err := time.LoadLocation("Asia/Jakarta")
|
||||
if err == nil {
|
||||
t = t.In(loc)
|
||||
}
|
||||
return t.Format("02-01-2006")
|
||||
}
|
||||
|
||||
func formatTxStatus(p entity.Payment) string {
|
||||
if p.LatestApproval == nil {
|
||||
return "-"
|
||||
}
|
||||
return safeTxText(p.LatestApproval.StepName)
|
||||
}
|
||||
|
||||
func txTransactionType(p entity.Payment) string {
|
||||
if p.TransactionType != "" {
|
||||
return p.TransactionType
|
||||
}
|
||||
return p.Direction
|
||||
}
|
||||
|
||||
func txPartyName(p entity.Payment) string {
|
||||
switch p.PartyType {
|
||||
case "CUSTOMER":
|
||||
if p.Customer != nil && p.Customer.Id != 0 {
|
||||
return p.Customer.Name
|
||||
}
|
||||
case "SUPPLIER":
|
||||
if p.Supplier != nil && p.Supplier.Id != 0 {
|
||||
return p.Supplier.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func txCreatedBy(p entity.Payment) string {
|
||||
if p.CreatedUser.Id == 0 {
|
||||
return ""
|
||||
}
|
||||
return p.CreatedUser.Name
|
||||
}
|
||||
|
||||
func txAmounts(direction string, nominal float64) (income, expense float64) {
|
||||
switch strings.ToUpper(direction) {
|
||||
case "IN":
|
||||
return nominal, 0
|
||||
case "OUT":
|
||||
return 0, nominal
|
||||
default:
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ type Update struct {
|
||||
|
||||
type Query struct {
|
||||
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,gt=0"`
|
||||
Search string `query:"search" validate:"omitempty,max=50"`
|
||||
TransactionTypes []string `query:"transaction_types" validate:"omitempty,dive,max=50"`
|
||||
BankIDs []uint `query:"bank_ids" validate:"omitempty,dive,gt=0"`
|
||||
|
||||
Reference in New Issue
Block a user