package controller import ( "encoding/json" "fmt" "math" "strconv" "strings" "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" "gitlab.com/mbugroup/lti-api.git/internal/response" "github.com/gofiber/fiber/v2" ) type ExpenseController struct { ExpenseService service.ExpenseService } func NewExpenseController(expenseService service.ExpenseService) *ExpenseController { return &ExpenseController{ ExpenseService: expenseService, } } func (u *ExpenseController) GetAll(c *fiber.Ctx) error { query := &validation.Query{ Page: c.QueryInt("page", 1), Limit: c.QueryInt("limit", 10), Search: c.Query("search", ""), } if query.Page < 1 || query.Limit < 1 { return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0") } result, totalResults, err := u.ExpenseService.GetAll(c, query) if err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.SuccessWithPaginate[dto.ExpenseListDTO]{ Code: fiber.StatusOK, Status: "success", Message: "Get all expenses successfully", Meta: response.Meta{ Page: query.Page, Limit: query.Limit, TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), TotalResults: totalResults, }, Data: result, }) } func (u *ExpenseController) GetOne(c *fiber.Ctx) error { param := c.Params("id") id, err := strconv.Atoi(param) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") } result, err := u.ExpenseService.GetOne(c, uint(id)) if err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Success{ Code: fiber.StatusOK, Status: "success", Message: "Get expense successfully", Data: result, }) } func (u *ExpenseController) CreateOne(c *fiber.Ctx) error { req := new(validation.Create) req.TransactionDate = c.FormValue("transaction_date") req.Category = c.FormValue("category") supplierID, err := strconv.ParseUint(c.FormValue("supplier_id"), 10, 64) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid supplier_id format") } req.SupplierID = supplierID form, err := c.MultipartForm() if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form") } req.Documents = form.File["documents"] expenseNonstocksJSON := c.FormValue("expense_nonstocks") if expenseNonstocksJSON != "" { if err := json.Unmarshal([]byte(expenseNonstocksJSON), &req.ExpenseNonstocks); err != nil { var singleExpenseNonstock validation.ExpenseNonstock if err := json.Unmarshal([]byte(expenseNonstocksJSON), &singleExpenseNonstock); err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid expense_nonstocks JSON: %v", err)) } if singleExpenseNonstock.KandangID == 0 { return fiber.NewError(fiber.StatusBadRequest, "Field KandangID is required") } req.ExpenseNonstocks = []validation.ExpenseNonstock{singleExpenseNonstock} } else { for i, expenseNonstock := range req.ExpenseNonstocks { if expenseNonstock.KandangID == 0 { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Field KandangID is required for expense_nonstocks[%d]", i)) } } } } else { return fiber.NewError(fiber.StatusBadRequest, "Field expense_nonstocks is required") } result, err := u.ExpenseService.CreateOne(c, req) if err != nil { return err } return c.Status(fiber.StatusCreated). JSON(response.Success{ Code: fiber.StatusCreated, Status: "success", Message: "Create expense successfully", Data: result, }) } func (u *ExpenseController) UpdateOne(c *fiber.Ctx) error { req := new(validation.Update) param := c.Params("id") id, err := strconv.Atoi(param) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") } form, err := c.MultipartForm() if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form") } req.Documents = form.File["documents"] transactionDate := c.FormValue("transaction_date") if transactionDate != "" { req.TransactionDate = &transactionDate } categoryVal := c.FormValue("category") if categoryVal != "" { req.Category = &categoryVal } supplierIDVal := c.FormValue("supplier_id") if supplierIDVal != "" { supplierID, err := strconv.ParseUint(supplierIDVal, 10, 64) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid supplier_id format") } req.SupplierID = &supplierID } expenseNonstocksJSON := c.FormValue("expense_nonstocks") if expenseNonstocksJSON != "" { var expenseNonstocks []validation.ExpenseNonstock if err := json.Unmarshal([]byte(expenseNonstocksJSON), &expenseNonstocks); err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid expense_nonstocks JSON: %v", err)) } for i, expenseNonstock := range expenseNonstocks { if expenseNonstock.KandangID == 0 { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Field KandangID is required for expense_nonstocks[%d]", i)) } } req.ExpenseNonstocks = &expenseNonstocks } result, err := u.ExpenseService.UpdateOne(c, req, uint(id)) if err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Success{ Code: fiber.StatusOK, Status: "success", Message: "Update expense successfully", Data: result, }) } func (u *ExpenseController) DeleteOne(c *fiber.Ctx) error { param := c.Params("id") id, err := strconv.Atoi(param) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") } if err := u.ExpenseService.DeleteOne(c, uint(id)); err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Common{ Code: fiber.StatusOK, Status: "success", Message: "Delete expense successfully", }) } func (u *ExpenseController) Approval(c *fiber.Ctx) error { req := new(validation.ApprovalRequest) if err := c.BodyParser(req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } path := c.Path() approvalType := "" if strings.Contains(path, "/approvals/manager") { approvalType = "manager" } else if strings.Contains(path, "/approvals/finance") { approvalType = "finance" } else { return fiber.NewError(fiber.StatusBadRequest, "Invalid approval path") } results, err := u.ExpenseService.Approval(c, req, approvalType) if err != nil { return err } var ( data interface{} message = "Submit expense approval successfully" ) if len(results) == 1 { data = results[0] } else { message = "Submit expense approvals successfully" data = results } return c.Status(fiber.StatusOK). JSON(response.Success{ Code: fiber.StatusOK, Status: "success", Message: message, Data: data, }) } func (u *ExpenseController) CreateRealization(c *fiber.Ctx) error { expenseID := c.Params("id") id, err := strconv.Atoi(expenseID) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid expense ID") } req := new(validation.CreateRealization) form, err := c.MultipartForm() if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form") } req.Documents = form.File["documents"] req.RealizationDate = c.FormValue("realization_date") realizationsJSON := c.FormValue("realizations") if realizationsJSON != "" { if err := json.Unmarshal([]byte(realizationsJSON), &req.Realizations); err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid realizations JSON: %v", err)) } } expense, err := u.ExpenseService.CreateRealization(c, uint(id), req) if err != nil { return err } return c.Status(fiber.StatusCreated). JSON(response.Success{ Code: fiber.StatusCreated, Status: "success", Message: "Create realization successfully", Data: expense, }) } func (u *ExpenseController) UpdateRealization(c *fiber.Ctx) error { expenseID := c.Params("id") id, err := strconv.Atoi(expenseID) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid expense ID") } var req validation.UpdateRealization form, err := c.MultipartForm() if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form") } req.Documents = form.File["documents"] realizationDate := c.FormValue("realization_date") if realizationDate != "" { req.RealizationDate = &realizationDate } realizationsJSON := c.FormValue("realizations") if realizationsJSON != "" { var realizations []validation.RealizationItem if err := json.Unmarshal([]byte(realizationsJSON), &realizations); err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid realizations JSON: %v", err)) } req.Realizations = &realizations } expense, err := u.ExpenseService.UpdateRealization(c, uint(id), &req) if err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Success{ Code: fiber.StatusOK, Status: "success", Message: "Update realization successfully", Data: expense, }) } func (u *ExpenseController) CompleteExpense(c *fiber.Ctx) error { expenseID := c.Params("id") id, err := strconv.Atoi(expenseID) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid expense ID") } expense, err := u.ExpenseService.CompleteExpense(c, uint(id), nil) if err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Success{ Code: fiber.StatusOK, Status: "success", Message: "Complete expense successfully", Data: expense, }) } func (u *ExpenseController) DeleteDocument(c *fiber.Ctx) error { expenseID, err := strconv.Atoi(c.Params("id")) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid expense ID") } documentID, err := strconv.ParseUint(c.Params("documentId"), 10, 64) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid document ID") } if err := u.ExpenseService.DeleteDocument(c, uint(expenseID), documentID, false); err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Common{ Code: fiber.StatusOK, Status: "success", Message: "Delete document successfully", }) } func (u *ExpenseController) DeleteRealizationDocument(c *fiber.Ctx) error { expenseID, err := strconv.Atoi(c.Params("id")) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid expense ID") } documentID, err := strconv.ParseUint(c.Params("documentId"), 10, 64) if err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid document ID") } if err := u.ExpenseService.DeleteDocument(c, uint(expenseID), documentID, true); err != nil { return err } return c.Status(fiber.StatusOK). JSON(response.Common{ Code: fiber.StatusOK, Status: "success", Message: "Delete realization document successfully", }) }