From f33eb7fcc7839a797c7b628a2590bae2243d7433 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 23 Oct 2025 09:03:28 +0700 Subject: [PATCH] feat[BE-117]: create lookup API in project flock to get projectFlocks kandang id --- ...ckin_detail.go => ProjectChickinDetail.go} | 0 .../chickins/services/chickin.service.go | 4 +- .../controllers/projectflock.controller.go | 16 +++ .../dto/projectflock_kandang.dto.go | 108 ++++++++++++++++++ .../projectflock_kandang.repository.go | 32 ++++++ .../production/project_flocks/route.go | 1 + .../services/projectflock.service.go | 56 ++++++++- 7 files changed, 214 insertions(+), 3 deletions(-) rename internal/entities/{project_chickin_detail.go => ProjectChickinDetail.go} (100%) create mode 100644 internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go diff --git a/internal/entities/project_chickin_detail.go b/internal/entities/ProjectChickinDetail.go similarity index 100% rename from internal/entities/project_chickin_detail.go rename to internal/entities/ProjectChickinDetail.go diff --git a/internal/modules/production/chickins/services/chickin.service.go b/internal/modules/production/chickins/services/chickin.service.go index 16e37911..de328b46 100644 --- a/internal/modules/production/chickins/services/chickin.service.go +++ b/internal/modules/production/chickins/services/chickin.service.go @@ -109,7 +109,7 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit return nil, err } - projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), 1) + projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId) if err != nil { s.Log.Errorf("Failed to get projectflock kandang: %+v", err) return nil, err @@ -154,7 +154,7 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format") } newChickin := &entity.ProjectChickin{ - ProjectFlockKandangId: projectflockkandang.ProjectFlockId, + ProjectFlockKandangId: projectflockkandang.Id, ChickInDate: chickinDate, Quantity: totalQuantity, Note: "", diff --git a/internal/modules/production/project_flocks/controllers/projectflock.controller.go b/internal/modules/production/project_flocks/controllers/projectflock.controller.go index a7de23fd..ca60d5df 100644 --- a/internal/modules/production/project_flocks/controllers/projectflock.controller.go +++ b/internal/modules/production/project_flocks/controllers/projectflock.controller.go @@ -244,3 +244,19 @@ func (u *ProjectflockController) GetFlockPeriodSummary(c *fiber.Ctx) error { Data: responseBody, }) } + +func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error { + projectFlockIdStr := c.Query("project_flock_id", "") + kandangIdStr := c.Query("kandang_id", "") + + result, err := u.ProjectflockService.GetProjectFlockKandangByParams(c, "", projectFlockIdStr, kandangIdStr) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{Code: fiber.StatusOK, + Status: "success", + Message: "Get projectflock kandang successfully", + Data: dto.ToProjectFlockKandangDTO(*result)}) +} diff --git a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go new file mode 100644 index 00000000..ff82fba9 --- /dev/null +++ b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go @@ -0,0 +1,108 @@ +package dto + +import ( + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" + fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto" + flockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto" + kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" + locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" + userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" +) + +// internal DTO used only for lookup response: project flock with kandangs carrying pivot ids +type KandangWithPivotDTO struct { + kandangDTO.KandangBaseDTO + ProjectFlockKandangId *uint `json:"project_flock_kandang_id,omitempty"` +} + +type ProjectFlockWithPivotDTO struct { + ProjectFlockBaseDTO + Flock *flockDTO.FlockBaseDTO `json:"flock,omitempty"` + Area *areaDTO.AreaBaseDTO `json:"area,omitempty"` + Category string `json:"category"` + Fcr *fcrDTO.FcrBaseDTO `json:"fcr,omitempty"` + Location *locationDTO.LocationBaseDTO `json:"location,omitempty"` + Kandangs []KandangWithPivotDTO `json:"kandangs,omitempty"` + CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"` +} + +type ProjectFlockKandangDTO struct { + Id uint `json:"id"` + ProjectFlockId uint `json:"project_flock_id"` + KandangId uint `json:"kandang_id"` + Kandang *kandangDTO.KandangBaseDTO `json:"kandang,omitempty"` + ProjectFlock *ProjectFlockWithPivotDTO `json:"project_flock,omitempty"` +} + +func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangDTO { + var kandang *kandangDTO.KandangBaseDTO + if e.Kandang.Id != 0 { + mapped := kandangDTO.ToKandangBaseDTO(e.Kandang) + kandang = &mapped + } + + var pf *ProjectFlockWithPivotDTO + if e.ProjectFlock.Id != 0 { + // build project flock with kandangs that include pivot ids + pfLocal := ProjectFlockWithPivotDTO{ + ProjectFlockBaseDTO: ProjectFlockBaseDTO{ + Id: e.ProjectFlock.Id, + Period: e.ProjectFlock.Period, + }, + Category: e.ProjectFlock.Category, + } + + // fill related small summaries + if e.ProjectFlock.Flock.Id != 0 { + mapped := ToFlockSummaryDTO(e.ProjectFlock.Flock) + pfLocal.Flock = &mapped + } + if e.ProjectFlock.Area.Id != 0 { + mapped := areaDTO.ToAreaBaseDTO(e.ProjectFlock.Area) + pfLocal.Area = &mapped + } + if e.ProjectFlock.Fcr.Id != 0 { + mapped := fcrDTO.ToFcrBaseDTO(e.ProjectFlock.Fcr) + pfLocal.Fcr = &mapped + } + if e.ProjectFlock.Location.Id != 0 { + mapped := locationDTO.ToLocationBaseDTO(e.ProjectFlock.Location) + pfLocal.Location = &mapped + } + if e.ProjectFlock.CreatedUser.Id != 0 { + mapped := userDTO.ToUserBaseDTO(e.ProjectFlock.CreatedUser) + pfLocal.CreatedUser = &mapped + } + + // build pivot map + pivotMap := make(map[uint]uint) + for _, ph := range e.ProjectFlock.KandangHistory { + pivotMap[ph.KandangId] = ph.Id + } + + // populate kandangs with pivot ids + for _, k := range e.ProjectFlock.Kandangs { + kb := kandangDTO.ToKandangBaseDTO(k) + var pid *uint + if v, ok := pivotMap[k.Id]; ok { + vv := v + pid = &vv + } + pfLocal.Kandangs = append(pfLocal.Kandangs, KandangWithPivotDTO{ + KandangBaseDTO: kb, + ProjectFlockKandangId: pid, + }) + } + + pf = &pfLocal + } + + return ProjectFlockKandangDTO{ + Id: e.Id, + ProjectFlockId: e.ProjectFlockId, + KandangId: e.KandangId, + Kandang: kandang, + ProjectFlock: pf, + } +} 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 fa6864fa..5c78f830 100644 --- a/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go +++ b/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go @@ -9,6 +9,7 @@ import ( type ProjectFlockKandangRepository interface { GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) + GetByProjectFlockAndKandang(ctx context.Context, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, error) CreateMany(ctx context.Context, records []*entity.ProjectFlockKandang) error DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error GetAll(ctx context.Context) ([]entity.ProjectFlockKandang, error) @@ -45,6 +46,12 @@ func (r *projectFlockKandangRepositoryImpl) GetAll(ctx context.Context) ([]entit if err := r.db.WithContext(ctx). Preload("ProjectFlock"). Preload("ProjectFlock.Flock"). + Preload("ProjectFlock.Fcr"). + Preload("ProjectFlock.Area"). + Preload("ProjectFlock.Location"). + Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.Kandangs"). + Preload("ProjectFlock.KandangHistory"). Preload("Kandang"). Order("project_flock_id ASC, created_at ASC"). Find(&records).Error; err != nil { @@ -66,9 +73,34 @@ func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint if err := r.db.WithContext(ctx). Preload("ProjectFlock"). Preload("ProjectFlock.Flock"). + Preload("ProjectFlock.Fcr"). + Preload("ProjectFlock.Area"). + Preload("ProjectFlock.Location"). + Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.Kandangs"). + Preload("ProjectFlock.KandangHistory"). Preload("Kandang"). First(record, id).Error; err != nil { return nil, err } return record, nil } + +func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockAndKandang(ctx context.Context, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, error) { + record := new(entity.ProjectFlockKandang) + if err := r.db.WithContext(ctx). + Where("project_flock_id = ? AND kandang_id = ?", projectFlockID, kandangID). + Preload("ProjectFlock"). + Preload("ProjectFlock.Flock"). + Preload("ProjectFlock.Fcr"). + Preload("ProjectFlock.Area"). + Preload("ProjectFlock.Location"). + Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.Kandangs"). + Preload("ProjectFlock.KandangHistory"). + Preload("Kandang"). + First(record).Error; err != nil { + return nil, err + } + return record, nil +} diff --git a/internal/modules/production/project_flocks/route.go b/internal/modules/production/project_flocks/route.go index bc9d8797..4c11d3a1 100644 --- a/internal/modules/production/project_flocks/route.go +++ b/internal/modules/production/project_flocks/route.go @@ -25,6 +25,7 @@ func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.Proj route.Get("/:id", ctrl.GetOne) route.Patch("/:id", ctrl.UpdateOne) route.Delete("/:id", ctrl.DeleteOne) + route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang) route.Post("/approvals", ctrl.Approval) route.Get("/flocks/:flock_id/periods", ctrl.GetFlockPeriodSummary) } diff --git a/internal/modules/production/project_flocks/services/projectflock.service.go b/internal/modules/production/project_flocks/services/projectflock.service.go index b36b5774..ffd0787f 100644 --- a/internal/modules/production/project_flocks/services/projectflock.service.go +++ b/internal/modules/production/project_flocks/services/projectflock.service.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "strings" commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" @@ -28,6 +29,7 @@ type ProjectflockService interface { CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectFlock, error) UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectFlock, error) DeleteOne(ctx *fiber.Ctx, id uint) error + GetProjectFlockKandangByParams(ctx *fiber.Ctx, idStr string, projectFlockIdStr string, kandangIdStr string) (*entity.ProjectFlockKandang, error) GetFlockPeriodSummary(ctx *fiber.Ctx, flockID uint) (*FlockPeriodSummary, error) Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error) } @@ -75,7 +77,8 @@ func (s projectflockService) withRelations(db *gorm.DB) *gorm.DB { Preload("Area"). Preload("Fcr"). Preload("Location"). - Preload("Kandangs") + Preload("Kandangs"). + Preload("KandangHistory") } func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) { @@ -639,6 +642,57 @@ func (s projectflockService) DeleteOne(c *fiber.Ctx, id uint) error { return nil } +func (s projectflockService) GetProjectFlockKandang(ctx *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, error) { + // keep for backward compatibility; delegate to new consolidated method + return s.GetProjectFlockKandangByParams(ctx, fmt.Sprintf("%d", id), "", "") +} + +func (s projectflockService) GetProjectFlockKandangByProjectAndKandang(ctx *fiber.Ctx, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, error) { + + pfk, err := s.PivotRepo.GetByProjectFlockAndKandang(ctx.Context(), projectFlockID, kandangID) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "ProjectFlockKandang not found") + } + return nil, err + } + return pfk, nil +} + +func (s projectflockService) GetProjectFlockKandangByParams(ctx *fiber.Ctx, idStr string, projectFlockIdStr string, kandangIdStr string) (*entity.ProjectFlockKandang, error) { + idStr = strings.TrimSpace(idStr) + projectFlockIdStr = strings.TrimSpace(projectFlockIdStr) + kandangIdStr = strings.TrimSpace(kandangIdStr) + + if idStr != "" { + id, err := strconv.Atoi(idStr) + if err != nil || id <= 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid Id") + } + pfk, err := s.PivotRepo.GetByID(ctx.Context(), uint(id)) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "ProjectFlockKandang not found") + } + return nil, err + } + return pfk, nil + } + + if projectFlockIdStr == "" || kandangIdStr == "" { + return nil, fiber.NewError(fiber.StatusBadRequest, "Missing lookup parameters") + } + pfid, err := strconv.Atoi(projectFlockIdStr) + if err != nil || pfid <= 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id") + } + kid, err := strconv.Atoi(kandangIdStr) + if err != nil || kid <= 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id") + } + return s.GetProjectFlockKandangByProjectAndKandang(ctx, uint(pfid), uint(kid)) +} + func (s projectflockService) GetFlockPeriodSummary(c *fiber.Ctx, flockID uint) (*FlockPeriodSummary, error) { flock, err := s.FlockRepo.GetByID(c.Context(), flockID, func(db *gorm.DB) *gorm.DB { return db.Preload("CreatedUser")