mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
add export excel all expenses
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/exportprogress"
|
||||
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"
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/validations"
|
||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||
supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto"
|
||||
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
type expenseServiceStub struct {
|
||||
getAllCalls []validation.Query
|
||||
}
|
||||
|
||||
var _ service.ExpenseService = (*expenseServiceStub)(nil)
|
||||
|
||||
func (s *expenseServiceStub) GetAll(_ *fiber.Ctx, params *validation.Query) ([]dto.ExpenseListDTO, int64, error) {
|
||||
callCopy := *params
|
||||
s.getAllCalls = append(s.getAllCalls, callCopy)
|
||||
|
||||
switch params.Page {
|
||||
case 1:
|
||||
return []dto.ExpenseListDTO{
|
||||
buildExpenseListForControllerTest("EXP-00001"),
|
||||
buildExpenseListForControllerTest("EXP-00002"),
|
||||
}, 3, nil
|
||||
case 2:
|
||||
return []dto.ExpenseListDTO{
|
||||
buildExpenseListForControllerTest("EXP-00003"),
|
||||
}, 3, nil
|
||||
default:
|
||||
return []dto.ExpenseListDTO{}, 3, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) GetOne(_ *fiber.Ctx, _ uint) (*dto.ExpenseDetailDTO, error) {
|
||||
return &dto.ExpenseDetailDTO{}, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) CreateOne(_ *fiber.Ctx, _ *validation.Create) (*dto.ExpenseDetailDTO, error) {
|
||||
return &dto.ExpenseDetailDTO{}, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) UpdateOne(_ *fiber.Ctx, _ *validation.Update, _ uint) (*dto.ExpenseDetailDTO, error) {
|
||||
return &dto.ExpenseDetailDTO{}, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) DeleteOne(_ *fiber.Ctx, _ uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) CreateRealization(_ *fiber.Ctx, _ uint, _ *validation.CreateRealization) (*dto.ExpenseDetailDTO, error) {
|
||||
return &dto.ExpenseDetailDTO{}, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) CompleteExpense(_ *fiber.Ctx, _ uint, _ *string) (*dto.ExpenseDetailDTO, error) {
|
||||
return &dto.ExpenseDetailDTO{}, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) UpdateRealization(_ *fiber.Ctx, _ uint, _ *validation.UpdateRealization) (*dto.ExpenseDetailDTO, error) {
|
||||
return &dto.ExpenseDetailDTO{}, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) DeleteDocument(_ *fiber.Ctx, _ uint, _ uint64, _ bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) Approval(_ *fiber.Ctx, _ *validation.ApprovalRequest, _ string) ([]dto.ExpenseDetailDTO, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) BulkApproveToStatus(_ *fiber.Ctx, _ *validation.BulkApprovalRequest, _ approvalutils.ApprovalStep) ([]dto.ExpenseDetailDTO, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *expenseServiceStub) GetProgressRows(_ *fiber.Ctx, _ *exportprogress.Query) ([]exportprogress.Row, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestExpenseControllerGetAllExportAllIgnoresRequestLimit(t *testing.T) {
|
||||
app := fiber.New()
|
||||
stub := &expenseServiceStub{}
|
||||
ctrl := NewExpenseController(stub)
|
||||
app.Get("/expenses", ctrl.GetAll)
|
||||
|
||||
req := httptest.NewRequest(
|
||||
http.MethodGet,
|
||||
"/expenses?export=excel&type=all&page=9&limit=1&search=operasional",
|
||||
nil,
|
||||
)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected app.Test error: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != fiber.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if !strings.Contains(contentType, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
|
||||
t.Fatalf("unexpected content-type: %s", contentType)
|
||||
}
|
||||
|
||||
disposition := resp.Header.Get("Content-Disposition")
|
||||
if !strings.Contains(disposition, "expenses_all_") {
|
||||
t.Fatalf("unexpected content-disposition: %s", disposition)
|
||||
}
|
||||
|
||||
if len(stub.getAllCalls) != 2 {
|
||||
t.Fatalf("expected 2 GetAll calls, got %d", len(stub.getAllCalls))
|
||||
}
|
||||
|
||||
firstCall := stub.getAllCalls[0]
|
||||
secondCall := stub.getAllCalls[1]
|
||||
if firstCall.Page != 1 || secondCall.Page != 2 {
|
||||
t.Fatalf("expected internal paging page 1 and 2, got %d and %d", firstCall.Page, secondCall.Page)
|
||||
}
|
||||
if firstCall.Limit != expenseExcelExportFetchLimit || secondCall.Limit != expenseExcelExportFetchLimit {
|
||||
t.Fatalf("expected internal limit %d, got %d and %d", expenseExcelExportFetchLimit, firstCall.Limit, secondCall.Limit)
|
||||
}
|
||||
if firstCall.Search != "operasional" {
|
||||
t.Fatalf("expected search filter to be forwarded, got %q", firstCall.Search)
|
||||
}
|
||||
|
||||
payload, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read excel payload: %v", err)
|
||||
}
|
||||
file, err := excelize.OpenReader(bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse excel payload: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if got, _ := file.GetCellValue(expenseExportSheetName, "A1"); got != "No" {
|
||||
t.Fatalf("expected A1 header to be No, got %q", got)
|
||||
}
|
||||
if got, _ := file.GetCellValue(expenseExportSheetName, "C2"); got != "EXP-00001" {
|
||||
t.Fatalf("expected first row reference EXP-00001, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpenseControllerGetAllKeepsPaginationValidationForNonExportAll(t *testing.T) {
|
||||
app := fiber.New()
|
||||
stub := &expenseServiceStub{}
|
||||
ctrl := NewExpenseController(stub)
|
||||
app.Get("/expenses", ctrl.GetAll)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/expenses?page=1&limit=0", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected app.Test error: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != fiber.StatusBadRequest {
|
||||
t.Fatalf("expected status 400, got %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpenseControllerGetAllProgressExportUnchanged(t *testing.T) {
|
||||
app := fiber.New()
|
||||
stub := &expenseServiceStub{}
|
||||
ctrl := NewExpenseController(stub)
|
||||
app.Get("/expenses", ctrl.GetAll)
|
||||
|
||||
req := httptest.NewRequest(
|
||||
http.MethodGet,
|
||||
"/expenses?export=excel&type=progress&start_date=2026-04-01&end_date=2026-04-22&limit=0",
|
||||
nil,
|
||||
)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected app.Test error: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != fiber.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", resp.StatusCode)
|
||||
}
|
||||
if len(stub.getAllCalls) != 0 {
|
||||
t.Fatalf("expected list GetAll not to be called for progress export, got %d calls", len(stub.getAllCalls))
|
||||
}
|
||||
}
|
||||
|
||||
func buildExpenseListForControllerTest(referenceNumber string) dto.ExpenseListDTO {
|
||||
approvedAction := string(entity.ApprovalActionApproved)
|
||||
|
||||
return dto.ExpenseListDTO{
|
||||
ExpenseBaseDTO: dto.ExpenseBaseDTO{
|
||||
ReferenceNumber: referenceNumber,
|
||||
PoNumber: "PO-" + strings.TrimPrefix(referenceNumber, "EXP-"),
|
||||
TransactionDate: time.Date(2026, time.April, 22, 0, 0, 0, 0, time.UTC),
|
||||
Category: "BOP",
|
||||
Supplier: &supplierDTO.SupplierRelationDTO{
|
||||
Name: "Supplier A",
|
||||
},
|
||||
Location: &locationDTO.LocationRelationDTO{
|
||||
Name: "Farm A",
|
||||
},
|
||||
},
|
||||
GrandTotal: 1500000,
|
||||
LatestApproval: &approvalDTO.ApprovalRelationDTO{
|
||||
StepName: "Finance",
|
||||
Action: &approvedAction,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user