Merge branch 'dev/gio' into 'feat/BE/Sprint-6'

feat[BE-298]: add api get one closing general information

See merge request mbugroup/lti-api!82
This commit is contained in:
Hafizh A. Y.
2025-12-06 04:34:31 +00:00
5 changed files with 161 additions and 26 deletions
@@ -53,15 +53,15 @@ func (u *ClosingController) GetAll(c *fiber.Ctx) error {
}) })
} }
func (u *ClosingController) GetOne(c *fiber.Ctx) error { func (u *ClosingController) GetClosingSummary(c *fiber.Ctx) error {
param := c.Params("id") param := c.Params("projectFlockId")
id, err := strconv.Atoi(param) id, err := strconv.Atoi(param)
if err != nil { if err != nil || id <= 0 {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") return fiber.NewError(fiber.StatusBadRequest, "Invalid projectFlockId")
} }
result, err := u.ClosingService.GetOne(c, uint(id)) result, err := u.ClosingService.GetClosingSummary(c, uint(id))
if err != nil { if err != nil {
return err return err
} }
@@ -70,7 +70,7 @@ func (u *ClosingController) GetOne(c *fiber.Ctx) error {
JSON(response.Success{ JSON(response.Success{
Code: fiber.StatusOK, Code: fiber.StatusOK,
Status: "success", Status: "success",
Message: "Get closing successfully", Message: "Retrieved project information successfully",
Data: dto.ToClosingListDTO(*result), Data: result,
}) })
} }
@@ -1,6 +1,7 @@
package dto package dto
import ( import (
"fmt"
"time" "time"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
@@ -26,6 +27,67 @@ type ClosingDetailDTO struct {
ClosingListDTO ClosingListDTO
} }
type ClosingSummaryDTO struct {
LocationID uint `json:"location_id"`
Periode int `json:"periode"`
JenisProduk string `json:"jenis_produk"`
LabelPopulasi string `json:"label_populasi"`
JumlahPopulasi int `json:"jumlah_populasi"`
JumlahPopulasiFormatted string `json:"jumlah_populasi_formatted"`
JenisProject string `json:"jenis_project"`
KandangAktif int `json:"kandang_aktif"`
KandangAktifFormatted string `json:"kandang_aktif_formatted"`
StatusPembayaranPenjualan string `json:"status_pembayaran_penjualan"`
StatusPembayaranMitra string `json:"status_pembayaran_mitra"`
StatusProject string `json:"status_project"`
StatusClosing string `json:"status_closing"`
}
func ToClosingSummaryDTO(project entity.ProjectFlock, statusProject, statusClosing string) ClosingSummaryDTO {
history := project.KandangHistory
period := maxPeriod(history)
kandangCount := len(history)
population := sumPopulation(history)
populationInt := int(population)
return ClosingSummaryDTO{
LocationID: project.LocationId,
Periode: period,
JenisProduk: project.Category,
LabelPopulasi: "",
JumlahPopulasi: populationInt,
JumlahPopulasiFormatted: fmt.Sprintf("%d Ekor", populationInt),
JenisProject: "",
KandangAktif: kandangCount,
KandangAktifFormatted: fmt.Sprintf("%d Kandang", kandangCount),
StatusPembayaranPenjualan: "Tempo",
StatusPembayaranMitra: "",
StatusProject: statusProject,
StatusClosing: statusClosing,
}
}
func maxPeriod(history []entity.ProjectFlockKandang) int {
max := 0
for _, h := range history {
if h.Period > max {
max = h.Period
}
}
return max
}
func sumPopulation(history []entity.ProjectFlockKandang) float64 {
var total float64
for _, h := range history {
for _, chickin := range h.Chickins {
total += chickin.UsageQty + chickin.PendingUsageQty
}
}
return total
}
// === Mapper Functions === // === Mapper Functions ===
func ToClosingRelationDTO(e entity.ProjectFlock) ClosingRelationDTO { func ToClosingRelationDTO(e entity.ProjectFlock) ClosingRelationDTO {
+6 -3
View File
@@ -5,9 +5,10 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm" "gorm.io/gorm"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
rClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories" rClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories"
sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services" sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services"
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
) )
@@ -18,9 +19,11 @@ func (ClosingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
closingRepo := rClosing.NewClosingRepository(db) closingRepo := rClosing.NewClosingRepository(db)
userRepo := rUser.NewUserRepository(db) userRepo := rUser.NewUserRepository(db)
closingService := sClosing.NewClosingService(closingRepo, validate) approvalRepo := commonRepo.NewApprovalRepository(db)
approvalService := commonSvc.NewApprovalService(approvalRepo)
closingService := sClosing.NewClosingService(closingRepo, approvalService, validate)
userService := sUser.NewUserService(userRepo, validate) userService := sUser.NewUserService(userRepo, validate)
ClosingRoutes(router, userService, closingService) ClosingRoutes(router, userService, closingService)
} }
+2 -2
View File
@@ -12,7 +12,7 @@ import (
func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) { func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) {
ctrl := controller.NewClosingController(s) ctrl := controller.NewClosingController(s)
route := v1.Group("/closings") route := v1.Group("/closing")
// route.Get("/", m.Auth(u), ctrl.GetAll) // route.Get("/", m.Auth(u), ctrl.GetAll)
// route.Post("/", m.Auth(u), ctrl.CreateOne) // route.Post("/", m.Auth(u), ctrl.CreateOne)
@@ -21,5 +21,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
route.Get("/", ctrl.GetAll) route.Get("/", ctrl.GetAll)
route.Get("/:id", ctrl.GetOne) route.Get("/:projectFlockId", ctrl.GetClosingSummary)
} }
@@ -1,12 +1,16 @@
package service package service
import ( import (
"context"
"errors" "errors"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations"
"gitlab.com/mbugroup/lti-api.git/internal/utils" "gitlab.com/mbugroup/lti-api.git/internal/utils"
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@@ -16,20 +20,22 @@ import (
type ClosingService interface { type ClosingService interface {
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error)
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error) GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
} }
type closingService struct { type closingService struct {
Log *logrus.Logger Log *logrus.Logger
Validate *validator.Validate Validate *validator.Validate
Repository repository.ClosingRepository Repository repository.ClosingRepository
ApprovalSvc commonSvc.ApprovalService
} }
func NewClosingService(repo repository.ClosingRepository, validate *validator.Validate) ClosingService { func NewClosingService(repo repository.ClosingRepository, approvalSvc commonSvc.ApprovalService, validate *validator.Validate) ClosingService {
return &closingService{ return &closingService{
Log: utils.Log, Log: utils.Log,
Validate: validate, Validate: validate,
Repository: repo, Repository: repo,
ApprovalSvc: approvalSvc,
} }
} }
@@ -37,6 +43,12 @@ func (s closingService) withRelations(db *gorm.DB) *gorm.DB {
return db.Preload("CreatedUser") return db.Preload("CreatedUser")
} }
func (s closingService) withClosingRelations(db *gorm.DB) *gorm.DB {
return s.withRelations(db).
Preload("KandangHistory").
Preload("KandangHistory.Chickins")
}
func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) { func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
if err := s.Validate.Struct(params); err != nil { if err := s.Validate.Struct(params); err != nil {
return nil, 0, err return nil, 0, err
@@ -59,14 +71,72 @@ func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
return closings, total, nil return closings, total, nil
} }
func (s closingService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlock, error) { func (s closingService) GetClosingSummary(c *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error) {
closing, err := s.Repository.GetByID(c.Context(), id, s.withRelations) if projectFlockID == 0 {
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
}
project, err := s.Repository.GetByID(c.Context(), projectFlockID, s.withClosingRelations)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "Closing not found") return nil, fiber.NewError(fiber.StatusNotFound, "Project flock not found")
} }
if err != nil { if err != nil {
s.Log.Errorf("Failed get closing by id: %+v", err) s.Log.Errorf("Failed get project flock %d for closing summary: %+v", projectFlockID, err)
return nil, err return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
} }
return closing, nil
statusProject, statusClosing, err := s.getApprovalStatuses(c.Context(), projectFlockID)
if err != nil {
s.Log.Errorf("Failed to retrieve approval statuses for project flock %d: %+v", projectFlockID, err)
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch approval status")
}
summary := dto.ToClosingSummaryDTO(*project, statusProject, statusClosing)
return &summary, nil
}
func (s closingService) getApprovalStatuses(ctx context.Context, projectFlockID uint) (string, string, error) {
if s.ApprovalSvc == nil {
return "", "Belum Selesai", nil
}
records, _, err := s.ApprovalSvc.List(ctx, utils.ApprovalWorkflowProjectFlock.String(), &projectFlockID, 1, 1000, "")
if err != nil {
return "", "", err
}
var (
minStep uint16
statusProject string
completed int
)
for _, rec := range records {
if minStep == 0 || rec.StepNumber < minStep {
minStep = rec.StepNumber
statusProject = rec.StepName
}
if rec.StepNumber == uint16(utils.ProjectFlockStepSelesai) {
completed++
}
}
if statusProject == "" && minStep > 0 {
if label, ok := approvalutils.ApprovalStepName(utils.ApprovalWorkflowProjectFlock, approvalutils.ApprovalStep(minStep)); ok {
statusProject = label
}
}
statusClosing := "Belum Selesai"
switch {
case len(records) == 0 || completed == 0:
statusClosing = "Belum Selesai"
case completed < len(records):
statusClosing = "Sebagian"
default:
statusClosing = "Selesai"
}
return statusProject, statusClosing, nil
} }