diff --git a/internal/database/migrations/20250925040409_create_master_tables.down.sql b/internal/database/migrations/20250925040409_create_master_tables.down.sql index 3f32606a..9816b3e0 100644 --- a/internal/database/migrations/20250925040409_create_master_tables.down.sql +++ b/internal/database/migrations/20250925040409_create_master_tables.down.sql @@ -36,3 +36,9 @@ DROP TABLE IF EXISTS projects; DROP INDEX IF EXISTS users_id_user_unique; DROP INDEX IF EXISTS users_email_unique; DROP TABLE IF EXISTS users; +DROP INDEX IF EXISTS idx_product_warehouses_unique; +DROP INDEX IF EXISTS idx_product_warehouses_deleted_at; +DROP INDEX IF EXISTS idx_product_warehouses_warehouse_id; +DROP INDEX IF EXISTS idx_product_warehouses_product_id; +DROP TABLE IF EXISTS product_warehouses; +DROP TABLE IF EXISTS stock_logs; \ No newline at end of file diff --git a/internal/database/migrations/20250925040409_create_master_tables.up.sql b/internal/database/migrations/20250925040409_create_master_tables.up.sql index 6dcd914a..5d844b3e 100644 --- a/internal/database/migrations/20250925040409_create_master_tables.up.sql +++ b/internal/database/migrations/20250925040409_create_master_tables.up.sql @@ -232,3 +232,45 @@ CREATE TABLE projects ( deleted_at TIMESTAMPTZ, created_by BIGINT REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE ); + +-- PRODUCT WAREHOUSES TABLE +CREATE TABLE product_warehouses ( + id BIGSERIAL PRIMARY KEY, + product_id BIGINT NOT NULL REFERENCES products(id), + warehouse_id BIGINT NOT NULL REFERENCES warehouses(id), + quantity INTEGER NOT NULL DEFAULT 0, + created_by BIGINT NOT NULL REFERENCES users(id), + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- INDEXES +CREATE INDEX idx_product_warehouses_product_id ON product_warehouses(product_id); +CREATE INDEX idx_product_warehouses_warehouse_id ON product_warehouses(warehouse_id); +CREATE INDEX idx_product_warehouses_deleted_at ON product_warehouses(deleted_at); +CREATE UNIQUE INDEX idx_product_warehouses_unique ON product_warehouses(product_id, warehouse_id) WHERE deleted_at IS NULL; + +-- STOCK LOGS +CREATE TABLE stock_logs ( + id BIGSERIAL PRIMARY KEY, + trancaction_type VARCHAR(20) NOT NULL, + quantity NUMERIC(15,3) NOT NULL, + before_quantity NUMERIC(15,3) NOT NULL, + after_quantity NUMERIC(15,3) NOT NULL, + log_type VARCHAR(50) NOT NULL, + log_id BIGINT NOT NULL, + note TEXT, + product_warehouse_id BIGINT NOT NULL REFERENCES product_warehouses(id) ON DELETE CASCADE ON UPDATE CASCADE, + created_by BIGINT NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- Create indexes for better performance +CREATE INDEX stock_logs_product_warehouse_id_idx ON stock_logs (product_warehouse_id); +CREATE INDEX stock_logs_log_type_log_id_idx ON stock_logs (log_type, log_id); +CREATE INDEX stock_logs_created_by_idx ON stock_logs (created_by); +CREATE INDEX stock_logs_created_at_idx ON stock_logs (created_at); +CREATE INDEX stock_logs_deleted_at_idx ON stock_logs (deleted_at); \ No newline at end of file diff --git a/internal/database/seed/seeder.go b/internal/database/seed/seeder.go index 57c3687b..a9bac734 100644 --- a/internal/database/seed/seeder.go +++ b/internal/database/seed/seeder.go @@ -674,6 +674,12 @@ func seedNonstocks(tx *gorm.DB, createdBy uint, uoms map[string]uint, suppliers return nil } +// nanti saya isi +func seedProductWarehouse(tx *gorm.DB, createdBy uint, products map[string]uint, warehouses map[string]uint) error { + + return nil +} + func seedFlags(tx *gorm.DB, flagableID uint, flagableType string, flags []utils.FlagType) error { if len(flags) == 0 { return nil diff --git a/internal/entities/adjustment.go b/internal/entities/adjustment.go new file mode 100644 index 00000000..840bbe3a --- /dev/null +++ b/internal/entities/adjustment.go @@ -0,0 +1,18 @@ +package entities + +import ( + "time" + + "gorm.io/gorm" +) + +type Adjustment struct { + Id uint `gorm:"primaryKey"` + Name string `gorm:"not null;uniqueIndex:idx_name,where:deleted_at IS NULL"` + CreatedBy uint `gorm:"not null"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + + CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"` +} diff --git a/internal/entities/product_warehouse.go b/internal/entities/product_warehouse.go new file mode 100644 index 00000000..84c3ae32 --- /dev/null +++ b/internal/entities/product_warehouse.go @@ -0,0 +1,23 @@ +package entities + +import ( + "time" + + "gorm.io/gorm" +) + +type ProductWarehouse struct { + Id uint `json:"id" gorm:"primaryKey;autoIncrement"` + ProductId uint `json:"product_id" gorm:"not null"` + WarehouseId uint `json:"warehouse_id" gorm:"not null"` + Quantity int `json:"quantity" gorm:"default:0"` + CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` + CreatedBy uint `json:"created_by" gorm:"not null"` + DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"` + + // Relations + Product Product `json:"product,omitempty" gorm:"foreignKey:ProductId;references:Id"` + Warehouse Warehouse `json:"warehouse,omitempty" gorm:"foreignKey:WarehouseId;references:Id"` + CreatedUser User `json:"created_user,omitempty" gorm:"foreignKey:CreatedBy;references:Id"` +} diff --git a/internal/entities/stock_log.go b/internal/entities/stock_log.go new file mode 100644 index 00000000..bdac1745 --- /dev/null +++ b/internal/entities/stock_log.go @@ -0,0 +1,28 @@ +package entities + +import ( + "time" + + "gorm.io/gorm" +) + +type StockLog struct { + Id uint `json:"id" gorm:"primaryKey;"` + TransactionType string `json:"transaction_type" gorm:"type:varchar(20);not null"` + Quantity float64 `json:"quantity" gorm:"type:numeric(15,3);not null"` + BeforeQuantity float64 `json:"before_quantity" gorm:"type:numeric(15,3);not null"` + AfterQuantity float64 `json:"after_quantity" gorm:"type:numeric(15,3);not null"` + LogType string `json:"log_type" gorm:"type:varchar(50);not null"` + LogId uint `json:"log_id" gorm:"not null"` + Note string `json:"note" gorm:"type:text"` + ProductWarehouseId uint `json:"product_warehouse_id" gorm:"not null;index"` + CreatedBy uint `json:"created_by" gorm:"not null;index"` + CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` + DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"` + + ProductWarehouse *ProductWarehouse `json:"product_warehouse,omitempty" gorm:"foreignKey:ProductWarehouseId;references:Id"` + CreatedUser *User `json:"created_user,omitempty" gorm:"foreignKey:CreatedBy;references:Id"` +} + + diff --git a/internal/modules/inventory/adjustments/controllers/adjustment.controller.go b/internal/modules/inventory/adjustments/controllers/adjustment.controller.go new file mode 100644 index 00000000..199e10fa --- /dev/null +++ b/internal/modules/inventory/adjustments/controllers/adjustment.controller.go @@ -0,0 +1,140 @@ +package controller + +import ( + "math" + "strconv" + + "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/dto" + service "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/validations" + "gitlab.com/mbugroup/lti-api.git/internal/response" + + "github.com/gofiber/fiber/v2" +) + +type AdjustmentController struct { + AdjustmentService service.AdjustmentService +} + +func NewAdjustmentController(adjustmentService service.AdjustmentService) *AdjustmentController { + return &AdjustmentController{ + AdjustmentService: adjustmentService, + } +} + +func (u *AdjustmentController) GetAll(c *fiber.Ctx) error { + query := &validation.Query{ + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + } + + result, totalResults, err := u.AdjustmentService.GetAll(c, query) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.SuccessWithPaginate[dto.AdjustmentListDTO]{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get all adjustments successfully", + Meta: response.Meta{ + Page: query.Page, + Limit: query.Limit, + TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), + TotalResults: totalResults, + }, + Data: dto.ToAdjustmentListDTOs(result), + }) +} + +func (u *AdjustmentController) 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.AdjustmentService.GetOne(c, uint(id)) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get adjustment successfully", + Data: dto.ToAdjustmentListDTO(*result), + }) +} + +func (u *AdjustmentController) 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.AdjustmentService.CreateOne(c, req) + if err != nil { + return err + } + + return c.Status(fiber.StatusCreated). + JSON(response.Success{ + Code: fiber.StatusCreated, + Status: "success", + Message: "Create adjustment successfully", + Data: dto.ToAdjustmentListDTO(*result), + }) +} + +func (u *AdjustmentController) 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.AdjustmentService.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 adjustment successfully", + Data: dto.ToAdjustmentListDTO(*result), + }) +} + +func (u *AdjustmentController) 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.AdjustmentService.DeleteOne(c, uint(id)); err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Common{ + Code: fiber.StatusOK, + Status: "success", + Message: "Delete adjustment successfully", + }) +} diff --git a/internal/modules/inventory/adjustments/dto/adjustment.dto.go b/internal/modules/inventory/adjustments/dto/adjustment.dto.go new file mode 100644 index 00000000..d0c2458a --- /dev/null +++ b/internal/modules/inventory/adjustments/dto/adjustment.dto.go @@ -0,0 +1,64 @@ +package dto + +import ( + "time" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" +) + +// === DTO Structs === + +type AdjustmentBaseDTO struct { + Id uint `json:"id"` + Name string `json:"name"` +} + +type AdjustmentListDTO struct { + AdjustmentBaseDTO + CreatedUser *userDTO.UserBaseDTO `json:"created_user"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type AdjustmentDetailDTO struct { + AdjustmentListDTO +} + +// === Mapper Functions === + +func ToAdjustmentBaseDTO(e entity.Adjustment) AdjustmentBaseDTO { + return AdjustmentBaseDTO{ + Id: e.Id, + Name: e.Name, + } +} + +func ToAdjustmentListDTO(e entity.Adjustment) AdjustmentListDTO { + var createdUser *userDTO.UserBaseDTO + if e.CreatedUser.Id != 0 { + mapped := userDTO.ToUserBaseDTO(e.CreatedUser) + createdUser = &mapped + } + + return AdjustmentListDTO{ + AdjustmentBaseDTO: ToAdjustmentBaseDTO(e), + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + CreatedUser: createdUser, + } +} + +func ToAdjustmentListDTOs(e []entity.Adjustment) []AdjustmentListDTO { + result := make([]AdjustmentListDTO, len(e)) + for i, r := range e { + result[i] = ToAdjustmentListDTO(r) + } + return result +} + +func ToAdjustmentDetailDTO(e entity.Adjustment) AdjustmentDetailDTO { + return AdjustmentDetailDTO{ + AdjustmentListDTO: ToAdjustmentListDTO(e), + } +} diff --git a/internal/modules/inventory/adjustments/module.go b/internal/modules/inventory/adjustments/module.go new file mode 100644 index 00000000..82126e43 --- /dev/null +++ b/internal/modules/inventory/adjustments/module.go @@ -0,0 +1,26 @@ +package adjustments + +import ( + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" + + rAdjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/repositories" + sAdjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" + + rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" + sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" +) + +type AdjustmentModule struct{} + +func (AdjustmentModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { + adjustmentRepo := rAdjustment.NewAdjustmentRepository(db) + userRepo := rUser.NewUserRepository(db) + + adjustmentService := sAdjustment.NewAdjustmentService(adjustmentRepo, validate) + userService := sUser.NewUserService(userRepo, validate) + + AdjustmentRoutes(router, userService, adjustmentService) +} + diff --git a/internal/modules/inventory/adjustments/repositories/adjustment.repository.go b/internal/modules/inventory/adjustments/repositories/adjustment.repository.go new file mode 100644 index 00000000..b8f3cc2b --- /dev/null +++ b/internal/modules/inventory/adjustments/repositories/adjustment.repository.go @@ -0,0 +1,21 @@ +package repository + +import ( + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + "gitlab.com/mbugroup/lti-api.git/internal/common/repository" + "gorm.io/gorm" +) + +type AdjustmentRepository interface { + repository.BaseRepository[entity.Adjustment] +} + +type AdjustmentRepositoryImpl struct { + *repository.BaseRepositoryImpl[entity.Adjustment] +} + +func NewAdjustmentRepository(db *gorm.DB) AdjustmentRepository { + return &AdjustmentRepositoryImpl{ + BaseRepositoryImpl: repository.NewBaseRepository[entity.Adjustment](db), + } +} diff --git a/internal/modules/inventory/adjustments/route.go b/internal/modules/inventory/adjustments/route.go new file mode 100644 index 00000000..deba38ea --- /dev/null +++ b/internal/modules/inventory/adjustments/route.go @@ -0,0 +1,28 @@ +package adjustments + +import ( + // m "gitlab.com/mbugroup/lti-api.git/internal/middleware" + controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/controllers" + adjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" + user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" + + "github.com/gofiber/fiber/v2" +) + +func AdjustmentRoutes(v1 fiber.Router, u user.UserService, s adjustment.AdjustmentService) { + ctrl := controller.NewAdjustmentController(s) + + route := v1.Group("/adjustments") + + // route.Get("/", m.Auth(u), ctrl.GetAll) + // route.Post("/", m.Auth(u), ctrl.CreateOne) + // route.Get("/:id", m.Auth(u), ctrl.GetOne) + // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) + // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) + + 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/inventory/adjustments/services/adjustment.service.go b/internal/modules/inventory/adjustments/services/adjustment.service.go new file mode 100644 index 00000000..bdea7eab --- /dev/null +++ b/internal/modules/inventory/adjustments/services/adjustment.service.go @@ -0,0 +1,129 @@ +package service + +import ( + "errors" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + repository "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/repositories" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +type AdjustmentService interface { + GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.Adjustment, int64, error) + GetOne(ctx *fiber.Ctx, id uint) (*entity.Adjustment, error) + CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.Adjustment, error) + UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.Adjustment, error) + DeleteOne(ctx *fiber.Ctx, id uint) error +} + +type adjustmentService struct { + Log *logrus.Logger + Validate *validator.Validate + Repository repository.AdjustmentRepository +} + +func NewAdjustmentService(repo repository.AdjustmentRepository, validate *validator.Validate) AdjustmentService { + return &adjustmentService{ + Log: utils.Log, + Validate: validate, + Repository: repo, + } +} + +func (s adjustmentService) withRelations(db *gorm.DB) *gorm.DB { + return db.Preload("CreatedUser") +} + +func (s adjustmentService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Adjustment, int64, error) { + if err := s.Validate.Struct(params); err != nil { + return nil, 0, err + } + + offset := (params.Page - 1) * params.Limit + + adjustments, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { + db = s.withRelations(db) + if params.Search != "" { + return db.Where("name LIKE ?", "%"+params.Search+"%") + } + return db.Order("created_at DESC").Order("updated_at DESC") + }) + + if err != nil { + s.Log.Errorf("Failed to get adjustments: %+v", err) + return nil, 0, err + } + return adjustments, total, nil +} + +func (s adjustmentService) GetOne(c *fiber.Ctx, id uint) (*entity.Adjustment, error) { + adjustment, err := s.Repository.GetByID(c.Context(), id, s.withRelations) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "Adjustment not found") + } + if err != nil { + s.Log.Errorf("Failed get adjustment by id: %+v", err) + return nil, err + } + return adjustment, nil +} + +func (s *adjustmentService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.Adjustment, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + + createBody := &entity.Adjustment{ + Name: req.Name, + } + + if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil { + s.Log.Errorf("Failed to create adjustment: %+v", err) + return nil, err + } + + return s.GetOne(c, createBody.Id) +} + +func (s adjustmentService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.Adjustment, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + + updateBody := make(map[string]any) + + if req.Name != nil { + updateBody["name"] = *req.Name + } + + if len(updateBody) == 0 { + return s.GetOne(c, id) + } + + if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "Adjustment not found") + } + s.Log.Errorf("Failed to update adjustment: %+v", err) + return nil, err + } + + return s.GetOne(c, id) +} + +func (s adjustmentService) DeleteOne(c *fiber.Ctx, id uint) error { + if err := s.Repository.DeleteOne(c.Context(), id); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusNotFound, "Adjustment not found") + } + s.Log.Errorf("Failed to delete adjustment: %+v", err) + return err + } + return nil +} diff --git a/internal/modules/inventory/adjustments/validations/adjustment.validation.go b/internal/modules/inventory/adjustments/validations/adjustment.validation.go new file mode 100644 index 00000000..95505746 --- /dev/null +++ b/internal/modules/inventory/adjustments/validations/adjustment.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"` + Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` + Search string `query:"search" validate:"omitempty,max=50"` +} diff --git a/internal/modules/inventory/module.go b/internal/modules/inventory/module.go new file mode 100644 index 00000000..4a60224a --- /dev/null +++ b/internal/modules/inventory/module.go @@ -0,0 +1,13 @@ +package inventory + +import ( + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" +) + +type InventoryModule struct{} + +func (InventoryModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { + RegisterRoutes(router, db, validate) +} diff --git a/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go b/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go new file mode 100644 index 00000000..4e526184 --- /dev/null +++ b/internal/modules/inventory/product-warehouses/controllers/product_warehouse.controller.go @@ -0,0 +1,142 @@ +package controller + +import ( + "math" + "strconv" + + "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto" + service "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/validations" + "gitlab.com/mbugroup/lti-api.git/internal/response" + + "github.com/gofiber/fiber/v2" +) + +type ProductWarehouseController struct { + ProductWarehouseService service.ProductWarehouseService +} + +func NewProductWarehouseController(productWarehouseService service.ProductWarehouseService) *ProductWarehouseController { + return &ProductWarehouseController{ + ProductWarehouseService: productWarehouseService, + } +} + +func (u *ProductWarehouseController) GetAll(c *fiber.Ctx) error { + query := &validation.Query{ + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + ProductId: uint(c.QueryInt("product_id", 0)), + WarehouseId: uint(c.QueryInt("warehouse_id", 0)), + } + + result, totalResults, err := u.ProductWarehouseService.GetAll(c, query) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.SuccessWithPaginate[dto.ProductWarehouseListDTO]{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get all productWarehouses successfully", + Meta: response.Meta{ + Page: query.Page, + Limit: query.Limit, + TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), + TotalResults: totalResults, + }, + Data: dto.ToProductWarehouseListDTOs(result), + }) +} + +func (u *ProductWarehouseController) 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.ProductWarehouseService.GetOne(c, uint(id)) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get productWarehouse successfully", + Data: dto.ToProductWarehouseListDTO(*result), + }) +} + +func (u *ProductWarehouseController) 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.ProductWarehouseService.CreateOne(c, req) + if err != nil { + return err + } + + return c.Status(fiber.StatusCreated). + JSON(response.Success{ + Code: fiber.StatusCreated, + Status: "success", + Message: "Create productWarehouse successfully", + Data: dto.ToProductWarehouseListDTO(*result), + }) +} + +func (u *ProductWarehouseController) 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.ProductWarehouseService.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 productWarehouse successfully", + Data: dto.ToProductWarehouseListDTO(*result), + }) +} + +func (u *ProductWarehouseController) 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.ProductWarehouseService.DeleteOne(c, uint(id)); err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Common{ + Code: fiber.StatusOK, + Status: "success", + Message: "Delete productWarehouse successfully", + }) +} diff --git a/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go b/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go new file mode 100644 index 00000000..0d7ef471 --- /dev/null +++ b/internal/modules/inventory/product-warehouses/dto/product_warehouse.dto.go @@ -0,0 +1,104 @@ +package dto + +import ( + "time" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" +) + +// === DTO Structs === + +type ProductWarehouseBaseDTO struct { + Id uint `json:"id"` + ProductId uint `json:"product_id"` + WarehouseId uint `json:"warehouse_id"` + Quantity int `json:"quantity"` +} + +type ProductWarehouseListDTO struct { + ProductWarehouseBaseDTO + Product *ProductBaseDTO `json:"product,omitempty"` + Warehouse *WarehouseBaseDTO `json:"warehouse,omitempty"` + CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type ProductWarehouseDetailDTO struct { + ProductWarehouseListDTO +} + +// Nested DTOs for relations +type ProductBaseDTO struct { + Id uint `json:"id"` + Name string `json:"name"` + Sku string `json:"sku"` +} + +type WarehouseBaseDTO struct { + Id uint `json:"id"` + Name string `json:"name"` +} + +// === Mapper Functions === + +func ToProductWarehouseBaseDTO(e entity.ProductWarehouse) ProductWarehouseBaseDTO { + return ProductWarehouseBaseDTO{ + Id: e.Id, + ProductId: e.ProductId, // Field yang benar dari entity + WarehouseId: e.WarehouseId, // Field yang benar dari entity + Quantity: e.Quantity, + } +} + +func ToProductWarehouseListDTO(e entity.ProductWarehouse) ProductWarehouseListDTO { + dto := ProductWarehouseListDTO{ + ProductWarehouseBaseDTO: ToProductWarehouseBaseDTO(e), + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + } + + // Map Product relation jika ada + if e.Product.Id != 0 { + product := ProductBaseDTO{ + Id: e.Product.Id, + Name: e.Product.Name, + } + if e.Product.Sku != nil { + product.Sku = *e.Product.Sku + } + dto.Product = &product + } + + // Map Warehouse relation jika ada + if e.Warehouse.Id != 0 { + warehouse := WarehouseBaseDTO{ + Id: e.Warehouse.Id, + Name: e.Warehouse.Name, + } + dto.Warehouse = &warehouse + } + + // Map CreatedUser relation jika ada + if e.CreatedUser.Id != 0 { + user := userDTO.ToUserBaseDTO(e.CreatedUser) + dto.CreatedUser = &user + } + + return dto +} + +func ToProductWarehouseListDTOs(e []entity.ProductWarehouse) []ProductWarehouseListDTO { + result := make([]ProductWarehouseListDTO, len(e)) + for i, r := range e { + result[i] = ToProductWarehouseListDTO(r) + } + return result +} + +func ToProductWarehouseDetailDTO(e entity.ProductWarehouse) ProductWarehouseDetailDTO { + return ProductWarehouseDetailDTO{ + ProductWarehouseListDTO: ToProductWarehouseListDTO(e), + } +} diff --git a/internal/modules/inventory/product-warehouses/module.go b/internal/modules/inventory/product-warehouses/module.go new file mode 100644 index 00000000..dfb72e8f --- /dev/null +++ b/internal/modules/inventory/product-warehouses/module.go @@ -0,0 +1,26 @@ +package productWarehouses + +import ( + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" + + rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" + sProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services" + + rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" + sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" +) + +type ProductWarehouseModule struct{} + +func (ProductWarehouseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { + productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db) + userRepo := rUser.NewUserRepository(db) + + productWarehouseService := sProductWarehouse.NewProductWarehouseService(productWarehouseRepo, validate) + userService := sUser.NewUserService(userRepo, validate) + + ProductWarehouseRoutes(router, userService, productWarehouseService) +} + diff --git a/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go b/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go new file mode 100644 index 00000000..053592da --- /dev/null +++ b/internal/modules/inventory/product-warehouses/repositories/product_warehouse.repository.go @@ -0,0 +1,53 @@ +package repository + +import ( + "context" + + "gitlab.com/mbugroup/lti-api.git/internal/common/repository" + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + "gorm.io/gorm" +) + +type ProductWarehouseRepository interface { + repository.BaseRepository[entity.ProductWarehouse] + ProductWarehouseExists(ctx context.Context, productId, warehouseId uint, excludeID *uint) (bool, error) + IsProductExist(ctx context.Context, productId uint) (bool, error) + IsWarehouseExist(ctx context.Context, warehouseId uint) (bool, error) + ExistsByID(ctx context.Context, id uint) (bool, error) +} + +type ProductWarehouseRepositoryImpl struct { + *repository.BaseRepositoryImpl[entity.ProductWarehouse] + db *gorm.DB +} + +func NewProductWarehouseRepository(db *gorm.DB) ProductWarehouseRepository { + return &ProductWarehouseRepositoryImpl{ + BaseRepositoryImpl: repository.NewBaseRepository[entity.ProductWarehouse](db), + db: db, + } +} + +func (r *ProductWarehouseRepositoryImpl) ProductWarehouseExists(ctx context.Context, productId, warehouseId uint, excludeID *uint) (bool, error) { + var count int64 + query := r.db.WithContext(ctx).Model(&entity.ProductWarehouse{}). + Where("product_id = ? AND warehouse_id = ?", productId, warehouseId) + if excludeID != nil { + query = query.Where("id != ?", *excludeID) + } + if err := query.Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func (r *ProductWarehouseRepositoryImpl) IsProductExist(ctx context.Context, productId uint) (bool, error) { + return repository.Exists[entity.Product](ctx, r.db, productId) +} +func (r *ProductWarehouseRepositoryImpl) IsWarehouseExist(ctx context.Context, warehouseId uint) (bool, error) { + return repository.Exists[entity.Warehouse](ctx, r.db, warehouseId) +} + +func (r *ProductWarehouseRepositoryImpl) ExistsByID(ctx context.Context, id uint) (bool, error) { + return repository.Exists[entity.ProductWarehouse](ctx, r.db, id) +} diff --git a/internal/modules/inventory/product-warehouses/route.go b/internal/modules/inventory/product-warehouses/route.go new file mode 100644 index 00000000..b0cc9c65 --- /dev/null +++ b/internal/modules/inventory/product-warehouses/route.go @@ -0,0 +1,28 @@ +package productWarehouses + +import ( + // m "gitlab.com/mbugroup/lti-api.git/internal/middleware" + controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/controllers" + productWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services" + user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" + + "github.com/gofiber/fiber/v2" +) + +func ProductWarehouseRoutes(v1 fiber.Router, u user.UserService, s productWarehouse.ProductWarehouseService) { + ctrl := controller.NewProductWarehouseController(s) + + route := v1.Group("/product-warehouses") + + // route.Get("/", m.Auth(u), ctrl.GetAll) + // route.Post("/", m.Auth(u), ctrl.CreateOne) + // route.Get("/:id", m.Auth(u), ctrl.GetOne) + // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) + // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) + + 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/inventory/product-warehouses/services/product_warehouse.service.go b/internal/modules/inventory/product-warehouses/services/product_warehouse.service.go new file mode 100644 index 00000000..21675ef1 --- /dev/null +++ b/internal/modules/inventory/product-warehouses/services/product_warehouse.service.go @@ -0,0 +1,210 @@ +package service + +import ( + "errors" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + repository "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +type ProductWarehouseService interface { + GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) + GetOne(ctx *fiber.Ctx, id uint) (*entity.ProductWarehouse, error) + CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProductWarehouse, error) + UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProductWarehouse, error) + DeleteOne(ctx *fiber.Ctx, id uint) error +} + +type productWarehouseService struct { + Log *logrus.Logger + Validate *validator.Validate + Repository repository.ProductWarehouseRepository +} + +func NewProductWarehouseService(repo repository.ProductWarehouseRepository, validate *validator.Validate) ProductWarehouseService { + return &productWarehouseService{ + Log: utils.Log, + Validate: validate, + Repository: repo, + } +} + +func (s productWarehouseService) withRelations(db *gorm.DB) *gorm.DB { + return db.Preload("Product").Preload("Warehouse").Preload("CreatedUser") +} + +func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) { + if err := s.Validate.Struct(params); err != nil { + return nil, 0, err + } + + offset := (params.Page - 1) * params.Limit + + productWarehouses, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { + db = s.withRelations(db) + + if params.ProductId != 0 { + db = db.Where("product_id = ?", params.ProductId) + } + + if params.WarehouseId != 0 { + db = db.Where("warehouse_id = ?", params.WarehouseId) + } + + // Search in related product or warehouse names + if params.Search != "" { + db = db.Joins("LEFT JOIN products ON products.id = product_warehouse.product_id"). + Joins("LEFT JOIN warehouses ON warehouses.id = product_warehouse.warehouse_id"). + Where("products.name ILIKE ? OR warehouses.name ILIKE ?", "%"+params.Search+"%", "%"+params.Search+"%") + } + + return db.Order("created_at DESC").Order("updated_at DESC") + }) + + if err != nil { + s.Log.Errorf("Failed to get productWarehouses: %+v", err) + return nil, 0, err + } + return productWarehouses, total, nil +} + +func (s productWarehouseService) GetOne(c *fiber.Ctx, id uint) (*entity.ProductWarehouse, error) { + productWarehouse, err := s.Repository.GetByID(c.Context(), id, s.withRelations) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "ProductWarehouse not found") + } + if err != nil { + s.Log.Errorf("Failed get productWarehouse by id: %+v", err) + return nil, err + } + return productWarehouse, nil +} + +func (s *productWarehouseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.ProductWarehouse, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + + isProductExist, err := s.Repository.IsProductExist(c.Context(), req.ProductId) + if err != nil { + s.Log.Errorf("Failed to check product existence: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check product existence") + } + if !isProductExist { + return nil, fiber.NewError(fiber.StatusBadRequest, "Product not found") + } + + isWarehouseExist, err := s.Repository.IsWarehouseExist(c.Context(), req.WarehouseId) + if err != nil { + s.Log.Errorf("Failed to check warehouse existence: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check warehouse existence") + } + if !isWarehouseExist { + return nil, fiber.NewError(fiber.StatusBadRequest, "Warehouse not found") + } + + // chceking if productWarehouse with same product_id and warehouse_id already + exists, err := s.Repository.ProductWarehouseExists(c.Context(), req.ProductId, req.WarehouseId, nil) + if err != nil { + s.Log.Errorf("Failed to check productWarehouse existence: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check productWarehouse existence") + } + + if exists { + return nil, fiber.NewError(fiber.StatusConflict, "ProductWarehouse already exists") + } + + createBody := &entity.ProductWarehouse{ + ProductId: req.ProductId, + WarehouseId: req.WarehouseId, + Quantity: req.Quantity, + CreatedBy: 1, // TODO: Ganti dengan user ID dari context setelah middleware auth diimplementasi + } + + if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil { + s.Log.Errorf("Failed to create productWarehouse: %+v", err) + return nil, err + } + + return s.GetOne(c, createBody.Id) +} + +func (s productWarehouseService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProductWarehouse, error) { + if err := s.Validate.Struct(req); err != nil { + return nil, err + } + + // validation Id exist + if exists, err := s.Repository.ExistsByID(c.Context(), id); err != nil { + s.Log.Errorf("Failed to check productWarehouse existence: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check productWarehouse existence") + } else if !exists { + return nil, fiber.NewError(fiber.StatusNotFound, "ProductWarehouse not found") + } + // validation productId and warehouseId exist + if req.ProductId != nil { + isProductExist, err := s.Repository.IsProductExist(c.Context(), *req.ProductId) + if err != nil { + s.Log.Errorf("Failed to check product existence: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check product existence") + } + if !isProductExist { + return nil, fiber.NewError(fiber.StatusBadRequest, "Product not found") + } + } + + if req.WarehouseId != nil { + isWarehouseExist, err := s.Repository.IsWarehouseExist(c.Context(), *req.WarehouseId) + if err != nil { + s.Log.Errorf("Failed to check warehouse existence: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check warehouse existence") + } + if !isWarehouseExist { + return nil, fiber.NewError(fiber.StatusNotFound, "Warehouse not found") + } + } + + updateBody := make(map[string]any) + + if req.ProductId != nil { + updateBody["product_id"] = *req.ProductId + } + if req.WarehouseId != nil { + updateBody["warehouse_id"] = *req.WarehouseId + } + if req.Quantity != nil { + updateBody["quantity"] = *req.Quantity + } + + if len(updateBody) == 0 { + return s.GetOne(c, id) + } + + if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "ProductWarehouse not found") + } + s.Log.Errorf("Failed to update productWarehouse: %+v", err) + return nil, err + } + + return s.GetOne(c, id) +} + +func (s productWarehouseService) DeleteOne(c *fiber.Ctx, id uint) error { + if err := s.Repository.DeleteOne(c.Context(), id); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusNotFound, "ProductWarehouse not found") + } + s.Log.Errorf("Failed to delete productWarehouse: %+v", err) + return err + } + return nil +} diff --git a/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go b/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go new file mode 100644 index 00000000..1031595b --- /dev/null +++ b/internal/modules/inventory/product-warehouses/validations/product_warehouse.validation.go @@ -0,0 +1,21 @@ +package validation + +type Create struct { + ProductId uint `json:"product_id" validate:"required,number,min=1"` + WarehouseId uint `json:"warehouse_id" validate:"required,number,min=1"` + Quantity int `json:"quantity" validate:"required,number,min=0"` +} + +type Update struct { + ProductId *uint `json:"product_id,omitempty" validate:"omitempty,number,min=1"` + WarehouseId *uint `json:"warehouse_id,omitempty" validate:"omitempty,number,min=1"` + Quantity *int `json:"quantity,omitempty" validate:"omitempty,number,min=0"` +} + +type Query struct { + Page int `query:"page" validate:"omitempty,number,min=1"` + Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` + Search string `query:"search" validate:"omitempty,max=50"` + ProductId uint `query:"product_id" validate:"omitempty,number,min=1"` + WarehouseId uint `query:"warehouse_id" validate:"omitempty,number,min=1"` +} diff --git a/internal/modules/inventory/route.go b/internal/modules/inventory/route.go new file mode 100644 index 00000000..f37e8cad --- /dev/null +++ b/internal/modules/inventory/route.go @@ -0,0 +1,28 @@ +package inventory + +import ( + "gitlab.com/mbugroup/lti-api.git/internal/modules" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" + + productWarehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses" + adjustments "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments" + // MODULE IMPORTS +) + +func RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { + group := router.Group("/inventory") + + allModules := []modules.Module{ + productWarehouses.ProductWarehouseModule{}, + + adjustments.AdjustmentModule{}, + // MODULE REGISTRY + } + + for _, m := range allModules { + m.RegisterRoutes(group, db, validate) + } +} diff --git a/internal/modules/master/kandangs/controllers/kandang.controller.go b/internal/modules/master/kandangs/controllers/kandang.controller.go index 45ade39d..23d22334 100644 --- a/internal/modules/master/kandangs/controllers/kandang.controller.go +++ b/internal/modules/master/kandangs/controllers/kandang.controller.go @@ -24,9 +24,11 @@ func NewKandangController(kandangService service.KandangService) *KandangControl func (u *KandangController) GetAll(c *fiber.Ctx) error { query := &validation.Query{ - Page: c.QueryInt("page", 1), - Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + LocationId: c.QueryInt("location_id", 0), + PicId: c.QueryInt("pic_id", 0), } result, totalResults, err := u.KandangService.GetAll(c, query) diff --git a/internal/modules/master/kandangs/services/kandang.service.go b/internal/modules/master/kandangs/services/kandang.service.go index 59162860..d856f736 100644 --- a/internal/modules/master/kandangs/services/kandang.service.go +++ b/internal/modules/master/kandangs/services/kandang.service.go @@ -54,6 +54,12 @@ func (s kandangService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity if params.Search != "" { return db.Where("name LIKE ?", "%"+params.Search+"%") } + if params.LocationId != 0 { + db = db.Where("location_id = ?", params.LocationId) + } + if params.PicId != 0 { + db = db.Where("pic_id = ?", params.PicId) + } return db.Order("created_at DESC").Order("updated_at DESC") }) diff --git a/internal/modules/master/kandangs/validations/kandang.validation.go b/internal/modules/master/kandangs/validations/kandang.validation.go index d76a6982..8b986ca6 100644 --- a/internal/modules/master/kandangs/validations/kandang.validation.go +++ b/internal/modules/master/kandangs/validations/kandang.validation.go @@ -13,7 +13,9 @@ type Update struct { } type Query struct { - Page int `query:"page" validate:"omitempty,number,min=1"` - Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` - Search string `query:"search" validate:"omitempty,max=50"` + Page int `query:"page" validate:"omitempty,number,min=1"` + Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` + Search string `query:"search" validate:"omitempty,max=50"` + LocationId int `query:"location_id" validate:"omitempty,number,gt=0"` + PicId int `query:"pic_id" validate:"omitempty,number,gt=0"` } diff --git a/internal/modules/master/locations/controllers/location.controller.go b/internal/modules/master/locations/controllers/location.controller.go index 37330928..8f8211d7 100644 --- a/internal/modules/master/locations/controllers/location.controller.go +++ b/internal/modules/master/locations/controllers/location.controller.go @@ -27,6 +27,7 @@ func (u *LocationController) GetAll(c *fiber.Ctx) error { Page: c.QueryInt("page", 1), Limit: c.QueryInt("limit", 10), Search: c.Query("search", ""), + AreaId: c.QueryInt("area_id", 0), } result, totalResults, err := u.LocationService.GetAll(c, query) diff --git a/internal/modules/master/locations/services/location.service.go b/internal/modules/master/locations/services/location.service.go index 1aae174c..7b7599ea 100644 --- a/internal/modules/master/locations/services/location.service.go +++ b/internal/modules/master/locations/services/location.service.go @@ -54,6 +54,9 @@ func (s locationService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entit if params.Search != "" { db = db.Where("name LIKE ?", "%"+params.Search+"%") } + if params.AreaId != 0 { + db = db.Where("area_id = ?", params.AreaId) + } return db.Order("created_at DESC").Order("updated_at DESC") }) diff --git a/internal/modules/master/locations/validations/location.validation.go b/internal/modules/master/locations/validations/location.validation.go index a9af1eb8..029953c0 100644 --- a/internal/modules/master/locations/validations/location.validation.go +++ b/internal/modules/master/locations/validations/location.validation.go @@ -16,4 +16,5 @@ type Query struct { Page int `query:"page" validate:"omitempty,number,min=1"` Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` Search string `query:"search" validate:"omitempty,max=50"` + AreaId int `query:"area_id" validate:"omitempty,number,gt=0"` } diff --git a/internal/modules/master/route.go b/internal/modules/master/route.go index 6c98db4f..88584c13 100644 --- a/internal/modules/master/route.go +++ b/internal/modules/master/route.go @@ -8,17 +8,17 @@ import ( "gorm.io/gorm" areas "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas" + banks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks" customers "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers" fcrs "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs" kandangs "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs" locations "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations" nonstocks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks" productcategories "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories" + products "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products" suppliers "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers" uoms "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms" warehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses" - products "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products" - banks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks" // MODULE IMPORTS ) diff --git a/internal/modules/master/warehouses/controllers/warehouse.controller.go b/internal/modules/master/warehouses/controllers/warehouse.controller.go index b1813ef2..b841d4ef 100644 --- a/internal/modules/master/warehouses/controllers/warehouse.controller.go +++ b/internal/modules/master/warehouses/controllers/warehouse.controller.go @@ -27,6 +27,7 @@ func (u *WarehouseController) GetAll(c *fiber.Ctx) error { Page: c.QueryInt("page", 1), Limit: c.QueryInt("limit", 10), Search: c.Query("search", ""), + AreaId: c.QueryInt("area_id", 0), } result, totalResults, err := u.WarehouseService.GetAll(c, query) diff --git a/internal/modules/master/warehouses/services/warehouse.service.go b/internal/modules/master/warehouses/services/warehouse.service.go index 3b45de5f..6cf45e0a 100644 --- a/internal/modules/master/warehouses/services/warehouse.service.go +++ b/internal/modules/master/warehouses/services/warehouse.service.go @@ -55,6 +55,9 @@ func (s warehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti if params.Search != "" { return db.Where("name LIKE ?", "%"+params.Search+"%") } + if params.AreaId != 0 { + db = db.Where("area_id = ?", params.AreaId) + } return db.Order("created_at DESC").Order("updated_at DESC") }) diff --git a/internal/modules/master/warehouses/validations/warehouse.validation.go b/internal/modules/master/warehouses/validations/warehouse.validation.go index ab16c02a..809ef0c4 100644 --- a/internal/modules/master/warehouses/validations/warehouse.validation.go +++ b/internal/modules/master/warehouses/validations/warehouse.validation.go @@ -20,4 +20,5 @@ type Query struct { Page int `query:"page" validate:"omitempty,number,min=1"` Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` Search string `query:"search" validate:"omitempty,max=50"` + AreaId int `query:"area_id" validate:"omitempty,number,gt=0"` } diff --git a/internal/route/route.go b/internal/route/route.go index c4bfa4b0..7acab04a 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -8,9 +8,10 @@ import ( "github.com/gofiber/fiber/v2" "gorm.io/gorm" + constants "gitlab.com/mbugroup/lti-api.git/internal/modules/constants" master "gitlab.com/mbugroup/lti-api.git/internal/modules/master" users "gitlab.com/mbugroup/lti-api.git/internal/modules/users" - constants "gitlab.com/mbugroup/lti-api.git/internal/modules/constants" + inventory "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory" // MODULE IMPORTS ) @@ -23,7 +24,8 @@ func Routes(app *fiber.App, db *gorm.DB) { allModules := []modules.Module{ users.UserModule{}, master.MasterModule{}, - constants.ConstantModule{}, + constants.ConstantModule{}, + inventory.InventoryModule{}, // MODULE REGISTRY }