From 3d75251c9611694a604d9efd199cf0b55b978b44 Mon Sep 17 00:00:00 2001 From: giovanni Date: Fri, 10 Apr 2026 14:09:31 +0700 Subject: [PATCH] adjust api get all project flock kandang with periode --- .../project_flock_kandang.controller.go | 47 +++++++-- .../dto/project_flock_kandang.dto.go | 29 +++++- .../services/project_flock_kandang.service.go | 37 +++++++ .../project_flock_kandang.validation.go | 23 ++--- .../projectflock_kandang.repository.go | 98 +++++++++++++++++++ 5 files changed, 210 insertions(+), 24 deletions(-) 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 32ac0e38..9333410f 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 @@ -24,22 +24,49 @@ func NewProjectFlockKandangController(projectFlockKandangService service.Project func (u *ProjectFlockKandangController) GetAll(c *fiber.Ctx) error { query := &validation.Query{ - Page: c.QueryInt("page", 1), - Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), - ProjectFlockId: uint(c.QueryInt("project_flock_id", 0)), - KandangId: uint(c.QueryInt("kandang_id", 0)), - Category: c.Query("category", ""), - AreaId: uint(c.QueryInt("area_id", 0)), - SortBy: c.Query("sort_by", ""), - SortOrder: c.Query("sort_order", ""), - StepName: c.Query("step_name", ""), + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + NameWithPeriode: c.QueryBool("name_with_periode", false), + ProjectFlockId: uint(c.QueryInt("project_flock_id", 0)), + KandangId: uint(c.QueryInt("kandang_id", 0)), + Category: c.Query("category", ""), + AreaId: uint(c.QueryInt("area_id", 0)), + SortBy: c.Query("sort_by", ""), + SortOrder: c.Query("sort_order", ""), + StepName: c.Query("step_name", ""), } if query.Page < 1 || query.Limit < 1 { return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0") } + if query.NameWithPeriode { + results, totalResults, err := u.ProjectFlockKandangService.GetAllNameWithPeriode(c, query) + if err != nil { + return err + } + + data := make([]dto.ProjectFlockKandangNameWithPeriodDTO, 0, len(results)) + for _, result := range results { + data = append(data, dto.ToProjectFlockKandangNameWithPeriodDTOValues(result.Id, result.KandangName, result.Period)) + } + + return c.Status(fiber.StatusOK). + JSON(response.SuccessWithPaginate[dto.ProjectFlockKandangNameWithPeriodDTO]{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get all projectFlockKandangs successfully", + Meta: response.Meta{ + Page: query.Page, + Limit: query.Limit, + TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), + TotalResults: totalResults, + }, + Data: data, + }) + } + results, totalResults, err := u.ProjectFlockKandangService.GetAll(c, query) if err != nil { return err 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 8231a551..b914aa10 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 @@ -60,6 +60,11 @@ type ProjectFlockKandangListDTO struct { ChickinApproval *approvalDTO.ApprovalRelationDTO `json:"chickin_approval,omitempty"` } +type ProjectFlockKandangNameWithPeriodDTO struct { + Id uint `json:"id"` + NameWithPeriod string `json:"name_with_period"` +} + type ProjectFlockKandangDetailDTO struct { ProjectFlockKandangListDTO Chickins []chickinDTO.ChickinRelationDTO `json:"chickins,omitempty"` @@ -129,13 +134,17 @@ func toKandangRelation(kandang entity.Kandang) *kandangDTO.KandangRelationDTO { } func toNameWithPeriod(kandang entity.Kandang, period int) string { - if kandang.Name == "" { + return toNameWithPeriodValue(kandang.Name, period) +} + +func toNameWithPeriodValue(kandangName string, period int) string { + if kandangName == "" { return "" } if period == 0 { - return kandang.Name + return kandangName } - return kandang.Name + " Period " + strconv.Itoa(period) + return kandangName + " Period " + strconv.Itoa(period) } func toApprovalDTOSelector( @@ -167,6 +176,20 @@ func ToProjectFlockKandangListDTO(e entity.ProjectFlockKandang) ProjectFlockKand } } +func ToProjectFlockKandangNameWithPeriodDTO(e entity.ProjectFlockKandang) ProjectFlockKandangNameWithPeriodDTO { + return ProjectFlockKandangNameWithPeriodDTO{ + Id: e.Id, + NameWithPeriod: toNameWithPeriod(e.Kandang, e.Period), + } +} + +func ToProjectFlockKandangNameWithPeriodDTOValues(id uint, kandangName string, period int) ProjectFlockKandangNameWithPeriodDTO { + return ProjectFlockKandangNameWithPeriodDTO{ + Id: id, + NameWithPeriod: toNameWithPeriodValue(kandangName, period), + } +} + func toCreatedUserDTO(pf entity.ProjectFlock) *userDTO.UserRelationDTO { if pf.CreatedUser.Id != 0 { mapped := userDTO.ToUserRelationDTO(pf.CreatedUser) 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 1dc62062..f80022b4 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 @@ -26,6 +26,7 @@ import ( type ProjectFlockKandangService interface { GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlockKandang, int64, error) + GetAllNameWithPeriode(ctx *fiber.Ctx, params *validation.Query) ([]ProjectFlockKandangNameWithPeriode, int64, error) GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, map[uint]float64, []entity.ProductWarehouse, error) CheckClosing(ctx *fiber.Ctx, id uint) (*ClosingCheckResult, error) Closing(ctx *fiber.Ctx, id uint, req *validation.Closing) (*entity.ProjectFlockKandang, error) @@ -51,6 +52,12 @@ type ClosingCheckResult struct { Expenses []ExpenseSummary `json:"expenses"` } +type ProjectFlockKandangNameWithPeriode struct { + Id uint + KandangName string + Period int +} + type StockRemainingDetail struct { FlagName string `json:"flag_name"` ProductWarehouseId uint `json:"product_warehouse_id"` @@ -133,6 +140,36 @@ func (s projectFlockKandangService) GetAll(c *fiber.Ctx, params *validation.Quer return projectFlockKandangs, total, nil } +func (s projectFlockKandangService) GetAllNameWithPeriode(c *fiber.Ctx, params *validation.Query) ([]ProjectFlockKandangNameWithPeriode, int64, error) { + if err := s.Validate.Struct(params); err != nil { + return nil, 0, err + } + + scope, err := m.ResolveLocationScope(c, s.Repository.DB()) + if err != nil { + return nil, 0, err + } + + offset := (params.Page - 1) * params.Limit + + rows, total, err := s.Repository.GetAllNameWithPeriodeScoped(c.Context(), offset, params.Limit, params, scope.IDs, scope.Restrict) + if err != nil { + s.Log.Errorf("Failed to get projectFlockKandangs name_with_periode: %+v", err) + return nil, 0, err + } + + results := make([]ProjectFlockKandangNameWithPeriode, 0, len(rows)) + for _, row := range rows { + results = append(results, ProjectFlockKandangNameWithPeriode{ + Id: row.Id, + KandangName: row.KandangName, + Period: row.Period, + }) + } + + return results, total, nil +} + func (s projectFlockKandangService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, map[uint]float64, []entity.ProductWarehouse, error) { scope, err := m.ResolveLocationScope(c, s.Repository.DB()) if err != nil { diff --git a/internal/modules/production/project-flock-kandangs/validations/project_flock_kandang.validation.go b/internal/modules/production/project-flock-kandangs/validations/project_flock_kandang.validation.go index 729d8329..1fc392ec 100644 --- a/internal/modules/production/project-flock-kandangs/validations/project_flock_kandang.validation.go +++ b/internal/modules/production/project-flock-kandangs/validations/project_flock_kandang.validation.go @@ -11,19 +11,20 @@ type Update struct { } 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"` - ProjectFlockId uint `query:"project_flock_id" validate:"omitempty"` - KandangId uint `query:"kandang_id" validate:"omitempty"` - Category string `query:"category" validate:"omitempty,oneof=Growing Laying"` - AreaId uint `query:"area_id" validate:"omitempty"` - SortBy string `query:"sort_by" validate:"omitempty,oneof=created_at period"` - SortOrder string `query:"sort_order" validate:"omitempty,oneof=ASC DESC"` - StepName string `query:"step_name" validate:"omitempty,max=50"` + 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"` + NameWithPeriode bool `query:"name_with_periode"` + ProjectFlockId uint `query:"project_flock_id" validate:"omitempty"` + KandangId uint `query:"kandang_id" validate:"omitempty"` + Category string `query:"category" validate:"omitempty,oneof=Growing Laying"` + AreaId uint `query:"area_id" validate:"omitempty"` + SortBy string `query:"sort_by" validate:"omitempty,oneof=created_at period"` + SortOrder string `query:"sort_order" validate:"omitempty,oneof=ASC DESC"` + StepName string `query:"step_name" validate:"omitempty,max=50"` } type Closing struct { Action string `json:"action" validate:"required,oneof=close unclose"` ClosedDate *string `json:"closed_date,omitempty"` -} \ No newline at end of file +} 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 b200cb18..29b06fe4 100644 --- a/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go +++ b/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go @@ -22,6 +22,7 @@ type ProjectFlockKandangRepository interface { GetAll(ctx context.Context, offset int, limit int, modifier func(*gorm.DB) *gorm.DB) ([]entity.ProjectFlockKandang, int64, error) GetAllWithFilters(ctx context.Context, offset int, limit int, params interface{}) ([]entity.ProjectFlockKandang, int64, error) GetAllWithFiltersScoped(ctx context.Context, offset int, limit int, params interface{}, locationIDs []uint, restrict bool) ([]entity.ProjectFlockKandang, int64, error) + GetAllNameWithPeriodeScoped(ctx context.Context, offset int, limit int, params *validation.Query, locationIDs []uint, restrict bool) ([]ProjectFlockKandangNameWithPeriode, int64, error) GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectFlockKandang, error) ListExistingKandangIDs(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]uint, error) HasKandangsLinkedToOtherProject(ctx context.Context, kandangIDs []uint, exceptProjectID *uint) (bool, error) @@ -40,6 +41,12 @@ type projectFlockKandangRepositoryImpl struct { db *gorm.DB } +type ProjectFlockKandangNameWithPeriode struct { + Id uint `gorm:"column:id"` + KandangName string `gorm:"column:kandang_name"` + Period int `gorm:"column:period"` +} + const flockBaseNameExpression = "LOWER(TRIM(regexp_replace(project_flocks.flock_name, '\\s+\\d+(\\s+\\d+)*$', '', 'g')))" func NewProjectFlockKandangRepository(db *gorm.DB) ProjectFlockKandangRepository { @@ -297,6 +304,97 @@ func (r *projectFlockKandangRepositoryImpl) GetAllWithFiltersScoped(ctx context. return records, total, nil } +func (r *projectFlockKandangRepositoryImpl) GetAllNameWithPeriodeScoped(ctx context.Context, offset int, limit int, params *validation.Query, locationIDs []uint, restrict bool) ([]ProjectFlockKandangNameWithPeriode, int64, error) { + var records []ProjectFlockKandangNameWithPeriode + var total int64 + + q := r.db.WithContext(ctx). + Table("project_flock_kandangs"). + Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\""). + Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"") + + if restrict { + if len(locationIDs) == 0 { + return []ProjectFlockKandangNameWithPeriode{}, 0, nil + } + q = q.Where("\"project_flocks\".\"location_id\" IN ?", locationIDs) + } + + if params != nil && params.StepName != "" { + q = q.Where(` + EXISTS ( + SELECT 1 FROM "approvals" + WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id" + AND "approvals"."approvable_type" = ? + AND LOWER("approvals"."step_name") = LOWER(?) + AND "approvals"."id" IN ( + SELECT "approvals"."id" FROM "approvals" + WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id" + AND "approvals"."approvable_type" = ? + ORDER BY "approvals"."id" DESC + LIMIT 1 + ) + ) + `, "PROJECT_FLOCK_KANDANGS", params.StepName, "PROJECT_FLOCK_KANDANGS") + } + + if params != nil { + if params.Search != "" { + escapedSearch := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(params.Search) + q = q.Where( + r.db.Where("LOWER(\"kandangs\".\"name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%"). + Or("LOWER(\"project_flocks\".\"flock_name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%"), + ) + } + + if params.ProjectFlockId > 0 { + q = q.Where("\"project_flock_kandangs\".\"project_flock_id\" = ?", params.ProjectFlockId) + } + + if params.KandangId > 0 { + q = q.Where("\"project_flock_kandangs\".\"kandang_id\" = ?", params.KandangId) + } + + if params.Category != "" { + q = q.Where("\"project_flocks\".\"category\" = ?", params.Category) + } + + if params.AreaId > 0 { + q = q.Where("\"project_flocks\".\"area_id\" = ?", params.AreaId) + } + } + + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + sortBy := "\"project_flock_kandangs\".\"created_at\" DESC" + if params != nil && params.SortBy != "" { + sortOrder := "DESC" + if params.SortOrder == "ASC" { + sortOrder = "ASC" + } + + switch params.SortBy { + case "created_at": + sortBy = "\"project_flock_kandangs\".\"created_at\" " + sortOrder + case "period": + sortBy = "\"project_flocks\".\"period\" " + sortOrder + } + } + + if err := q. + Select("\"project_flock_kandangs\".\"id\", \"project_flock_kandangs\".\"period\", \"kandangs\".\"name\" AS kandang_name"). + Order(sortBy). + Offset(offset). + Limit(limit). + Scan(&records).Error; err != nil { + return nil, 0, err + } + + return records, total, nil +} + func (r *projectFlockKandangRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockKandangRepository { return &projectFlockKandangRepositoryImpl{db: tx} }