From 576f8083a3600360271850e0cbf13a8e4bd437e7 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Wed, 10 Dec 2025 08:23:52 +0700 Subject: [PATCH] Feat[BE}: inisiate repport module --- .../controllers/repport.controller.go | 144 ++++++++++++++++++ internal/modules/repports/dto/repport.dto.go | 16 ++ internal/modules/repports/module.go | 17 +++ internal/modules/repports/route.go | 20 +++ .../repports/services/repport.service.go | 131 ++++++++++++++++ .../validations/repport.validation.go | 15 ++ internal/route/route.go | 2 + 7 files changed, 345 insertions(+) create mode 100644 internal/modules/repports/controllers/repport.controller.go create mode 100644 internal/modules/repports/dto/repport.dto.go create mode 100644 internal/modules/repports/module.go create mode 100644 internal/modules/repports/route.go create mode 100644 internal/modules/repports/services/repport.service.go create mode 100644 internal/modules/repports/validations/repport.validation.go diff --git a/internal/modules/repports/controllers/repport.controller.go b/internal/modules/repports/controllers/repport.controller.go new file mode 100644 index 00000000..abe1901f --- /dev/null +++ b/internal/modules/repports/controllers/repport.controller.go @@ -0,0 +1,144 @@ +package controller + +import ( + "math" + "strconv" + + "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto" + service "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/services" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations" + "gitlab.com/mbugroup/lti-api.git/internal/response" + + "github.com/gofiber/fiber/v2" +) + +type RepportController struct { + RepportService service.RepportService +} + +func NewRepportController(repportService service.RepportService) *RepportController { + return &RepportController{ + RepportService: repportService, + } +} + +func (u *RepportController) 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.RepportService.GetAll(c, query) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.SuccessWithPaginate[dto.RepportListDTO]{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get all repports 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 *RepportController) 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.RepportService.GetOne(c, uint(id)) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get repport successfully", + Data: result, + }) +} + +func (u *RepportController) CreateOne(c *fiber.Ctx) error { + req := new(validation.Create) + + if err := c.BodyParser(req); err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") + } + + result, err := u.RepportService.CreateOne(c, req) + if err != nil { + return err + } + + return c.Status(fiber.StatusCreated). + JSON(response.Success{ + Code: fiber.StatusCreated, + Status: "success", + Message: "Create repport successfully", + Data: result, + }) +} + +func (u *RepportController) 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") + } + + if err := c.BodyParser(req); err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") + } + + result, err := u.RepportService.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 repport successfully", + Data: result, + }) +} + +func (u *RepportController) 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.RepportService.DeleteOne(c, uint(id)); err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Common{ + Code: fiber.StatusOK, + Status: "success", + Message: "Delete repport successfully", + }) +} diff --git a/internal/modules/repports/dto/repport.dto.go b/internal/modules/repports/dto/repport.dto.go new file mode 100644 index 00000000..154c6f47 --- /dev/null +++ b/internal/modules/repports/dto/repport.dto.go @@ -0,0 +1,16 @@ +package dto + +import "time" + +// === DTO Structs === + +type RepportListDTO struct { + Id uint `json:"id"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type RepportDetailDTO struct { + RepportListDTO +} diff --git a/internal/modules/repports/module.go b/internal/modules/repports/module.go new file mode 100644 index 00000000..83bf6ce6 --- /dev/null +++ b/internal/modules/repports/module.go @@ -0,0 +1,17 @@ +package repports + +import ( + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" + + sRepport "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/services" +) + +type RepportModule struct{} + +func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { + repportService := sRepport.NewRepportService(validate) + + RepportRoutes(router, repportService) +} diff --git a/internal/modules/repports/route.go b/internal/modules/repports/route.go new file mode 100644 index 00000000..ccb551ed --- /dev/null +++ b/internal/modules/repports/route.go @@ -0,0 +1,20 @@ +package repports + +import ( + controller "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/controllers" + repport "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/services" + + "github.com/gofiber/fiber/v2" +) + +func RepportRoutes(v1 fiber.Router, s repport.RepportService) { + ctrl := controller.NewRepportController(s) + + route := v1.Group("/repports") + + route.Get("/", ctrl.GetAll) + route.Post("/", ctrl.CreateOne) + route.Get("/:id", ctrl.GetOne) + route.Patch("/:id", ctrl.UpdateOne) + route.Delete("/:id", ctrl.DeleteOne) +} diff --git a/internal/modules/repports/services/repport.service.go b/internal/modules/repports/services/repport.service.go new file mode 100644 index 00000000..69765579 --- /dev/null +++ b/internal/modules/repports/services/repport.service.go @@ -0,0 +1,131 @@ +package service + +import ( + "strings" + "time" + + dto "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "github.com/sirupsen/logrus" +) + +type RepportService interface { + GetAll(ctx *fiber.Ctx, params *validation.Query) ([]dto.RepportListDTO, int64, error) + GetOne(ctx *fiber.Ctx, id uint) (*dto.RepportListDTO, error) + CreateOne(ctx *fiber.Ctx, req *validation.Create) (*dto.RepportListDTO, error) + UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*dto.RepportListDTO, error) + DeleteOne(ctx *fiber.Ctx, id uint) error +} + +type repportService struct { + Log *logrus.Logger + Validate *validator.Validate + dummyData map[uint]dto.RepportListDTO + nextID uint +} + +func NewRepportService(validate *validator.Validate) RepportService { + // Initialize with dummy data + now := time.Now().UTC() + dummyData := map[uint]dto.RepportListDTO{ + 1: {Id: 1, Name: "Sales Report", CreatedAt: now, UpdatedAt: now}, + 2: {Id: 2, Name: "Inventory Report", CreatedAt: now, UpdatedAt: now}, + 3: {Id: 3, Name: "Production Report", CreatedAt: now, UpdatedAt: now}, + } + + return &repportService{ + Log: utils.Log, + Validate: validate, + dummyData: dummyData, + nextID: 4, + } +} + +func (s *repportService) GetAll(c *fiber.Ctx, params *validation.Query) ([]dto.RepportListDTO, int64, error) { + if err := s.Validate.Struct(params); err != nil { + return nil, 0, err + } + + // Convert map to slice + var results []dto.RepportListDTO + for _, v := range s.dummyData { + // Apply search filter if provided + if params.Search != "" && !strings.Contains(strings.ToLower(v.Name), strings.ToLower(params.Search)) { + continue + } + results = append(results, v) + } + + total := int64(len(results)) + + // Apply pagination + offset := (params.Page - 1) * params.Limit + if offset >= int(total) { + return []dto.RepportListDTO{}, total, nil + } + + end := offset + params.Limit + if end > int(total) { + end = int(total) + } + + return results[offset:end], total, nil +} + +func (s *repportService) GetOne(c *fiber.Ctx, id uint) (*dto.RepportListDTO, error) { + if data, ok := s.dummyData[id]; ok { + return &data, nil + } + return nil, fiber.NewError(fiber.StatusNotFound, "Report not found") +} + +func (s *repportService) CreateOne(c *fiber.Ctx, req *validation.Create) (*dto.RepportListDTO, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + + now := time.Now().UTC() + newReport := dto.RepportListDTO{ + Id: s.nextID, + Name: req.Name, + CreatedAt: now, + UpdatedAt: now, + } + s.dummyData[s.nextID] = newReport + s.nextID++ + + return &newReport, nil +} + +func (s *repportService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*dto.RepportListDTO, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + + data, ok := s.dummyData[id] + if !ok { + return nil, fiber.NewError(fiber.StatusNotFound, "Report not found") + } + + if req.Name != nil { + data.Name = *req.Name + } + + data.UpdatedAt = time.Now().UTC() + s.dummyData[id] = data + + return &data, nil +} + +func (s *repportService) DeleteOne(c *fiber.Ctx, id uint) error { + if _, ok := s.dummyData[id]; !ok { + return fiber.NewError(fiber.StatusNotFound, "Report not found") + } + + delete(s.dummyData, id) + return nil +} diff --git a/internal/modules/repports/validations/repport.validation.go b/internal/modules/repports/validations/repport.validation.go new file mode 100644 index 00000000..7d16d3ee --- /dev/null +++ b/internal/modules/repports/validations/repport.validation.go @@ -0,0 +1,15 @@ +package validation + +type Create struct { + Name string `json:"name" validate:"required_strict,min=3"` +} + +type Update struct { + Name *string `json:"name,omitempty" validate:"omitempty"` +} + +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"` + Search string `query:"search" validate:"omitempty,max=50"` +} diff --git a/internal/route/route.go b/internal/route/route.go index 4d1c1bae..294fc900 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -19,6 +19,7 @@ import ( purchases "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases" ssoModule "gitlab.com/mbugroup/lti-api.git/internal/modules/sso" users "gitlab.com/mbugroup/lti-api.git/internal/modules/users" + repports "gitlab.com/mbugroup/lti-api.git/internal/modules/repports" // MODULE IMPORTS ) @@ -42,6 +43,7 @@ func Routes(app *fiber.App, db *gorm.DB) { expenses.ExpenseModule{}, ssoModule.Module{}, closings.ClosingModule{}, + repports.RepportModule{}, // MODULE REGISTRY }