From 954cccd564231f4531454fa1e3e59577aae5f162 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 6 Nov 2025 21:25:15 +0700 Subject: [PATCH] Fix[BE]: make projectflock kandang API and dto clean --- .../production/chickins/dto/chickin.dto.go | 52 ++++- .../project_flock_kandang.controller.go | 4 +- .../dto/project_flock_kandang.dto.go | 200 +++++------------- .../services/project_flock_kandang.service.go | 69 ++---- .../projectflock_kandang.repository.go | 4 + 5 files changed, 115 insertions(+), 214 deletions(-) diff --git a/internal/modules/production/chickins/dto/chickin.dto.go b/internal/modules/production/chickins/dto/chickin.dto.go index 7271c310..ad6d10f8 100644 --- a/internal/modules/production/chickins/dto/chickin.dto.go +++ b/internal/modules/production/chickins/dto/chickin.dto.go @@ -9,20 +9,29 @@ import ( flockBaseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto" kandangBaseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" locationBaseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" + productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto" + warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils" userBaseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" ) // === DTO Structs (ordered) === +type ProductWarehouseDTO struct { + Id uint `json:"id"` + Product *productDTO.ProductBaseDTO `json:"product,omitempty"` + Warehouse *warehouseDTO.WarehouseBaseDTO `json:"warehouse,omitempty"` +} + type ChickinBaseDTO struct { - Id uint `json:"id"` - ProjectFlockKandangId uint `json:"project_flock_kandang_id"` - ChickInDate time.Time `json:"chick_in_date"` - ProductWarehouseId uint `json:"product_warehouse_id"` - UsageQty float64 `json:"usage_qty"` - PendingUsageQty float64 `json:"pending_usage_qty"` - Notes string `json:"notes"` + Id uint `json:"id"` + ProjectFlockKandangId uint `json:"project_flock_kandang_id"` + ChickInDate time.Time `json:"chick_in_date"` + ProductWarehouseId uint `json:"product_warehouse_id"` + ProductWarehouse *ProductWarehouseDTO `json:"product_warehouse,omitempty"` + UsageQty float64 `json:"usage_qty"` + PendingUsageQty float64 `json:"pending_usage_qty"` + Notes string `json:"notes"` } type ProjectFlockDTO struct { @@ -159,11 +168,18 @@ func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO { // If relation is not loaded but ID is available, use the ID projectFlockKandangId = e.ProjectFlockKandangId } + + var productWarehouse *ProductWarehouseDTO + if e.ProductWarehouse != nil && e.ProductWarehouse.Id != 0 { + productWarehouse = toProductWarehouseDTO(e.ProductWarehouse) + } + return ChickinBaseDTO{ Id: e.Id, ProjectFlockKandangId: projectFlockKandangId, ChickInDate: e.ChickInDate, ProductWarehouseId: e.ProductWarehouseId, + ProductWarehouse: productWarehouse, UsageQty: e.UsageQty, PendingUsageQty: e.PendingUsageQty, Notes: e.Notes, @@ -242,3 +258,25 @@ func ToChickinDetailDTOs(e []entity.ProjectChickin) []ChickinDetailDTO { } return result } + +// === Helper Functions === + +// ToProductWarehouseDTO adalah exported helper untuk mapping ProductWarehouse (shared logic) +func ToProductWarehouseDTO(pw *entity.ProductWarehouse) *ProductWarehouseDTO { + if pw == nil { + return nil + } + + product := productDTO.ToProductBaseDTO(pw.Product) + warehouse := warehouseDTO.ToWarehouseBaseDTO(pw.Warehouse) + + return &ProductWarehouseDTO{ + Id: pw.Id, + Product: &product, + Warehouse: &warehouse, + } +} + +func toProductWarehouseDTO(pw *entity.ProductWarehouse) *ProductWarehouseDTO { + return ToProductWarehouseDTO(pw) +} diff --git a/internal/modules/production/project-flock-kandangs/controllers/project_flock_kandang.controller.go b/internal/modules/production/project-flock-kandangs/controllers/project_flock_kandang.controller.go index 383006bf..21701826 100644 --- a/internal/modules/production/project-flock-kandangs/controllers/project_flock_kandang.controller.go +++ b/internal/modules/production/project-flock-kandangs/controllers/project_flock_kandang.controller.go @@ -47,7 +47,7 @@ func (u *ProjectFlockKandangController) GetAll(c *fiber.Ctx) error { data := make([]dto.ProjectFlockKandangListDTO, 0) for _, result := range results { - data = append(data, dto.ToProjectFlockKandangListDTO(result.Entity)) + data = append(data, dto.ToProjectFlockKandangListDTO(result)) } return c.Status(fiber.StatusOK). @@ -83,6 +83,6 @@ func (u *ProjectFlockKandangController) GetOne(c *fiber.Ctx) error { Code: fiber.StatusOK, Status: "success", Message: "Get projectFlockKandang successfully", - Data: dto.ToProjectFlockKandangListDTOWithAvailableQty(*result, availableQtys), + Data: dto.ToProjectFlockKandangDetailDTOWithAvailableQty(*result, availableQtys), }) } diff --git a/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go b/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go index 8e9d3672..e80e3220 100644 --- a/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go +++ b/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go @@ -19,7 +19,9 @@ import ( // === DTO Structs (ordered) === type ProjectFlockKandangBaseDTO struct { - Id uint `json:"id"` + Id uint `json:"id"` + KandangId uint `json:"kandang_id"` + ProjectFlockId uint `json:"project_flock_id"` } type ProjectFlockDTO struct { @@ -55,23 +57,17 @@ type AvailableQtyDTO struct { type ProjectFlockKandangListDTO struct { ProjectFlockKandangBaseDTO - ProjectFlock *ProjectFlockDTO `json:"project_flock,omitempty"` - Kandang *KandangDTO `json:"kandang,omitempty"` - Chickins []chickinDTO.ChickinBaseDTO `json:"chickins,omitempty"` - AvailableQtys []AvailableQtyDTO `json:"available_qtys,omitempty"` - CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"` - CreatedAt time.Time `json:"created_at"` - Approval *approvalDTO.ApprovalBaseDTO `json:"approval,omitempty"` + ProjectFlock *ProjectFlockDTO `json:"project_flock,omitempty"` + Kandang *KandangDTO `json:"kandang,omitempty"` + CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"` + CreatedAt time.Time `json:"created_at"` + Approval *approvalDTO.ApprovalBaseDTO `json:"approval,omitempty"` } type ProjectFlockKandangDetailDTO struct { ProjectFlockKandangListDTO -} - -// Wrapper untuk GetAll dengan available quantities sudah included -type ProjectFlockKandangWithAvailableQtysDTO struct { - Entity entity.ProjectFlockKandang - AvailableQtys []map[string]interface{} + Chickins []chickinDTO.ChickinBaseDTO `json:"chickins,omitempty"` + AvailableQtys []AvailableQtyDTO `json:"available_qtys,omitempty"` } // === Mapper Functions (ordered) === @@ -101,23 +97,27 @@ func toProjectFlockDTO(pf *projectFlockDTO.ProjectFlockListDTO) *ProjectFlockDTO } } -func ToProjectFlockKandangListDTOWithAvailableQty(e entity.ProjectFlockKandang, availableQtysRaw []map[string]interface{}) ProjectFlockKandangListDTO { +func ToProjectFlockKandangDetailDTOWithAvailableQty(e entity.ProjectFlockKandang, availableQtyMap map[uint]float64) ProjectFlockKandangDetailDTO { var projectFlockSummary *projectFlockDTO.ProjectFlockListDTO if e.ProjectFlock.Id != 0 { mapped := projectFlockDTO.ToProjectFlockListDTO(e.ProjectFlock) projectFlockSummary = &mapped } - return ProjectFlockKandangListDTO{ + listDTO := ProjectFlockKandangListDTO{ ProjectFlockKandangBaseDTO: ToProjectFlockKandangBaseDTO(e), ProjectFlock: toProjectFlockDTO(projectFlockSummary), Kandang: toKandangDTO(e.Kandang), - Chickins: toChickinDTOs(e.Chickins), - AvailableQtys: toAvailableQtyDTOsFromRaw(availableQtysRaw), CreatedAt: e.CreatedAt, CreatedUser: toCreatedUserDTO(e.ProjectFlock), Approval: toApprovalDTO(e), } + + return ProjectFlockKandangDetailDTO{ + ProjectFlockKandangListDTO: listDTO, + Chickins: toChickinDTOs(e.Chickins), + AvailableQtys: toAvailableQtyDTOsFromMap(e.Chickins, availableQtyMap), + } } func toKandangDTO(kandang entity.Kandang) *KandangDTO { @@ -151,8 +151,6 @@ func ToProjectFlockKandangListDTO(e entity.ProjectFlockKandang) ProjectFlockKand ProjectFlockKandangBaseDTO: ToProjectFlockKandangBaseDTO(e), ProjectFlock: toProjectFlockDTO(projectFlockSummary), Kandang: toKandangDTO(e.Kandang), - Chickins: toChickinDTOs(e.Chickins), - AvailableQtys: toAvailableQtyDTOs(e.Chickins), CreatedAt: e.CreatedAt, CreatedUser: toCreatedUserDTO(e.ProjectFlock), Approval: toApprovalDTO(e), @@ -167,75 +165,6 @@ func ToProjectFlockKandangListDTOs(e []entity.ProjectFlockKandang) []ProjectFloc return result } -func ToProjectFlockKandangDetailDTO(e entity.ProjectFlockKandang) ProjectFlockKandangDetailDTO { - return ProjectFlockKandangDetailDTO{ - ProjectFlockKandangListDTO: ToProjectFlockKandangListDTO(e), - } -} - -// === Helper Functions (ordered) === - -func toProductWarehouseDTO(pwData map[string]interface{}) *ProductWarehouseDTO { - if pwData == nil { - return nil - } - - dto := &ProductWarehouseDTO{} - - if id, ok := pwData["id"].(float64); ok { - dto.Id = uint(id) - } else if id, ok := pwData["id"].(uint); ok { - dto.Id = id - } - - if pData, ok := pwData["product"].(map[string]interface{}); ok { - dto.Product = toProductDTO(pData) - } - - if wData, ok := pwData["warehouse"].(map[string]interface{}); ok { - dto.Warehouse = toWarehouseDTO(wData) - } - - return dto -} - -func toProductDTO(pData map[string]interface{}) *productDTO.ProductBaseDTO { - if pData == nil { - return nil - } - - product := &productDTO.ProductBaseDTO{} - if id, ok := pData["id"].(float64); ok { - product.Id = uint(id) - } else if id, ok := pData["id"].(uint); ok { - product.Id = id - } - if name, ok := pData["name"].(string); ok { - product.Name = name - } - return product -} - -func toWarehouseDTO(wData map[string]interface{}) *warehouseDTO.WarehouseBaseDTO { - if wData == nil { - return nil - } - - warehouse := &warehouseDTO.WarehouseBaseDTO{} - if id, ok := wData["id"].(float64); ok { - warehouse.Id = uint(id) - } else if id, ok := wData["id"].(uint); ok { - warehouse.Id = id - } - if name, ok := wData["name"].(string); ok { - warehouse.Name = name - } - if wType, ok := wData["type"].(string); ok { - warehouse.Type = wType - } - return warehouse -} - func toCreatedUserDTO(pf entity.ProjectFlock) *userDTO.UserBaseDTO { if pf.CreatedUser.Id != 0 { mapped := userDTO.ToUserBaseDTO(pf.CreatedUser) @@ -261,78 +190,49 @@ func toChickinDTOs(chickins []entity.ProjectChickin) []chickinDTO.ChickinBaseDTO return result } -func toAvailableQtyDTOs(chickins []entity.ProjectChickin) []AvailableQtyDTO { - if len(chickins) == 0 { - return nil - } - - availableQtyMap := make(map[uint]AvailableQtyDTO) - for _, ch := range chickins { - if ch.ProductWarehouse == nil || ch.ProductWarehouse.Quantity <= 0 { - continue - } - - if _, exists := availableQtyMap[ch.ProductWarehouseId]; exists { - continue - } - - pwDTO := &ProductWarehouseDTO{ - Id: ch.ProductWarehouse.Id, - } - - if ch.ProductWarehouse.Product.Id != 0 { - pwDTO.Product = &productDTO.ProductBaseDTO{ - Id: ch.ProductWarehouse.Product.Id, - Name: ch.ProductWarehouse.Product.Name, - } - } - - if ch.ProductWarehouse.Warehouse.Id != 0 { - pwDTO.Warehouse = &warehouseDTO.WarehouseBaseDTO{ - Id: ch.ProductWarehouse.Warehouse.Id, - Name: ch.ProductWarehouse.Warehouse.Name, - Type: ch.ProductWarehouse.Warehouse.Type, - } - } - - availableQtyMap[ch.ProductWarehouseId] = AvailableQtyDTO{ - ProductWarehouse: pwDTO, - } - } - +func toAvailableQtyDTOsFromMap(chickins []entity.ProjectChickin, availableQtyMap map[uint]float64) []AvailableQtyDTO { if len(availableQtyMap) == 0 { return nil } + pwMap := make(map[uint]*entity.ProductWarehouse) + for _, chickin := range chickins { + if chickin.ProductWarehouse != nil && chickin.ProductWarehouse.Id != 0 { + pwMap[chickin.ProductWarehouseId] = chickin.ProductWarehouse + } + } + result := make([]AvailableQtyDTO, 0, len(availableQtyMap)) - for _, v := range availableQtyMap { - result = append(result, v) - } - return result -} - -func toAvailableQtyDTOsFromRaw(availableQtysRaw []map[string]interface{}) []AvailableQtyDTO { - if len(availableQtysRaw) == 0 { - return nil - } - - result := make([]AvailableQtyDTO, len(availableQtysRaw)) - for i, v := range availableQtysRaw { - pwData, ok := v["product_warehouse"].(map[string]interface{}) - if !ok { + for pwId, availableQty := range availableQtyMap { + pw, exists := pwMap[pwId] + if !exists || pw == nil { continue } - pwDTO := toProductWarehouseDTO(pwData) - availableQty := 0.0 - if qty, ok := v["available_qty"].(float64); ok { - availableQty = qty - } + pwDTO := ToProductWarehouseDTO(pw) - result[i] = AvailableQtyDTO{ + result = append(result, AvailableQtyDTO{ AvailableQty: availableQty, ProductWarehouse: pwDTO, - } + }) } + return result } + +func ToProductWarehouseDTO(pw *entity.ProductWarehouse) *ProductWarehouseDTO { + if pw == nil { + return nil + } + + chickinPwDTO := chickinDTO.ToProductWarehouseDTO(pw) + if chickinPwDTO == nil { + return nil + } + + return &ProductWarehouseDTO{ + Id: chickinPwDTO.Id, + Product: chickinPwDTO.Product, + Warehouse: chickinPwDTO.Warehouse, + } +} diff --git a/internal/modules/production/project-flock-kandangs/services/project_flock_kandang.service.go b/internal/modules/production/project-flock-kandangs/services/project_flock_kandang.service.go index 90e85882..fcdb31f4 100644 --- a/internal/modules/production/project-flock-kandangs/services/project_flock_kandang.service.go +++ b/internal/modules/production/project-flock-kandangs/services/project_flock_kandang.service.go @@ -7,7 +7,6 @@ import ( entity "gitlab.com/mbugroup/lti-api.git/internal/entities" rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories" - dto "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/dto" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/validations" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories" "gitlab.com/mbugroup/lti-api.git/internal/utils" @@ -19,10 +18,12 @@ import ( ) type ProjectFlockKandangService interface { - GetAll(ctx *fiber.Ctx, params *validation.Query) ([]dto.ProjectFlockKandangWithAvailableQtysDTO, int64, error) - GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, []map[string]interface{}, error) + GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlockKandang, int64, error) + GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, map[uint]float64, error) } +// Note: map[uint]float64 adalah mapping dari ProductWarehouse ID ke calculated available quantity + type projectFlockKandangService struct { Log *logrus.Logger Validate *validator.Validate @@ -45,18 +46,11 @@ func NewProjectFlockKandangService(repo repository.ProjectFlockKandangRepository } } -func (s projectFlockKandangService) GetAll(c *fiber.Ctx, params *validation.Query) ([]dto.ProjectFlockKandangWithAvailableQtysDTO, int64, error) { +func (s projectFlockKandangService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlockKandang, int64, error) { if err := s.Validate.Struct(params); err != nil { return nil, 0, err } - if params.Page <= 0 { - params.Page = 1 - } - if params.Limit <= 0 { - params.Limit = 10 - } - offset := (params.Page - 1) * params.Limit projectFlockKandangs, total, err := s.Repository.GetAllWithFilters(c.Context(), offset, params.Limit, params) @@ -85,24 +79,11 @@ func (s projectFlockKandangService) GetAll(c *fiber.Ctx, params *validation.Quer } } } - results := make([]dto.ProjectFlockKandangWithAvailableQtysDTO, 0) - for i := range projectFlockKandangs { - availableQtys, err := s.getAvailableQuantities(c, &projectFlockKandangs[i]) - if err != nil { - s.Log.Warnf("Failed to fetch available quantities for kandang %d: %+v", projectFlockKandangs[i].Kandang.Id, err) - availableQtys = nil - } - results = append(results, dto.ProjectFlockKandangWithAvailableQtysDTO{ - Entity: projectFlockKandangs[i], - AvailableQtys: availableQtys, - }) - } - - return results, total, nil + return projectFlockKandangs, total, nil } -func (s projectFlockKandangService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, []map[string]interface{}, error) { +func (s projectFlockKandangService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, map[uint]float64, error) { projectFlockKandang, err := s.Repository.GetByID(c.Context(), id) if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil, fiber.NewError(fiber.StatusNotFound, "ProjectFlockKandang not found") @@ -122,16 +103,16 @@ func (s projectFlockKandangService) GetOne(c *fiber.Ctx, id uint) (*entity.Proje } } - availableQtys, err := s.getAvailableQuantities(c, projectFlockKandang) + availableQtyMap, err := s.getAvailableQuantities(c, projectFlockKandang) if err != nil { s.Log.Errorf("Failed to fetch available quantities for kandang %d: %+v", projectFlockKandang.Kandang.Id, err) - availableQtys = nil + availableQtyMap = nil } - return projectFlockKandang, availableQtys, nil + return projectFlockKandang, availableQtyMap, nil } -func (s projectFlockKandangService) getAvailableQuantities(c *fiber.Ctx, projectFlockKandang *entity.ProjectFlockKandang) ([]map[string]interface{}, error) { +func (s projectFlockKandangService) getAvailableQuantities(c *fiber.Ctx, projectFlockKandang *entity.ProjectFlockKandang) (map[uint]float64, error) { if projectFlockKandang.Kandang.Id == 0 || s.WarehouseRepo == nil || s.ProductWarehouseRepo == nil { return nil, nil } @@ -155,38 +136,16 @@ func (s projectFlockKandangService) getAvailableQuantities(c *fiber.Ctx, project return nil, nil } - var result []map[string]interface{} + result := make(map[uint]float64) for _, pw := range products { availableQty, err := s.calculateAvailableQuantityForProductWarehouse(c, projectFlockKandang, &pw) if err != nil { s.Log.Warnf("Failed to calculate available quantity for product warehouse %d: %v", pw.Id, err) } - if availableQty <= 0 { - continue + if availableQty > 0 { + result[pw.Id] = availableQty } - - productData := map[string]interface{}{ - "id": pw.Product.Id, - "name": pw.Product.Name, - } - - warehouseData := map[string]interface{}{ - "id": pw.Warehouse.Id, - "name": pw.Warehouse.Name, - "type": pw.Warehouse.Type, - } - - productWarehouseData := map[string]interface{}{ - "id": pw.Id, - "product": productData, - "warehouse": warehouseData, - } - - result = append(result, map[string]interface{}{ - "available_qty": availableQty, - "product_warehouse": productWarehouseData, - }) } return result, nil diff --git a/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go b/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go index 1a05acff..a2ab8ebe 100644 --- a/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go +++ b/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go @@ -189,6 +189,8 @@ func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint Preload("Chickins"). Preload("Chickins.CreatedUser"). Preload("Chickins.ProductWarehouse"). + Preload("Chickins.ProductWarehouse.Product"). + Preload("Chickins.ProductWarehouse.Warehouse"). First(record, id).Error; err != nil { return nil, err } @@ -210,6 +212,8 @@ func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockAndKandang(ctx cont Preload("Chickins"). Preload("Chickins.CreatedUser"). Preload("Chickins.ProductWarehouse"). + Preload("Chickins.ProductWarehouse.Product"). + Preload("Chickins.ProductWarehouse.Warehouse"). First(record).Error; err != nil { return nil, err }