FIX[BE]: period and adjustment helper to function

This commit is contained in:
ragilap
2025-10-16 16:35:01 +07:00
parent 3ec05eb76f
commit 62a1011a4b
4 changed files with 84 additions and 12 deletions
@@ -101,7 +101,11 @@ func (s *kandangService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
); err != nil {
return nil, err
}
status := strings.ToUpper(req.Status)
status := strings.ToUpper(strings.TrimSpace(req.Status))
if status == "" {
status = string(utils.KandangStatusNonActive)
}
if !utils.IsValidKandangStatus(status) {
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid kandang status")
}
@@ -2,7 +2,7 @@ package validation
type Create struct {
Name string `json:"name" validate:"required_strict,min=3"`
Status string `json:"status" validate:"required_strict,min=3"`
Status string `json:"status,omitempty" validate:"omitempty,min=3"`
LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"`
PicId uint `json:"pic_id" validate:"required_strict,number,gt=0"`
ProjectFlockId *uint `json:"project_flock_id" validate:"omitempty,number,gt=0"`
@@ -7,6 +7,7 @@ import (
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type ProjectflockRepository interface {
@@ -14,6 +15,7 @@ type ProjectflockRepository interface {
GetAllByFlock(ctx context.Context, flockID uint) ([]entity.ProjectFlock, error)
GetActiveByFlock(ctx context.Context, flockID uint) (*entity.ProjectFlock, error)
GetMaxPeriodByFlock(ctx context.Context, flockID uint) (int, error)
GetNextPeriodForFlock(ctx context.Context, flockID uint) (int, error)
}
type ProjectflockRepositoryImpl struct {
@@ -64,3 +66,23 @@ func (r *ProjectflockRepositoryImpl) GetMaxPeriodByFlock(ctx context.Context, fl
}
return max, nil
}
func (r *ProjectflockRepositoryImpl) GetNextPeriodForFlock(ctx context.Context, flockID uint) (int, error) {
var payload struct {
Period int
}
if err := r.DB().WithContext(ctx).
Model(&entity.ProjectFlock{}).
Where("flock_id = ?", flockID).
Clauses(clause.Locking{Strength: "UPDATE"}).
Order("period DESC").
Limit(1).
Select("period").
Scan(&payload).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return 1, nil
}
return 0, err
}
return payload.Period + 1, nil
}
@@ -1,9 +1,12 @@
package service
import (
"context"
"errors"
"fmt"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
common "gitlab.com/mbugroup/lti-api.git/internal/common/service"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
flockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/repositories"
kandangRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
@@ -15,7 +18,6 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type ProjectflockService interface {
@@ -106,6 +108,16 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
return nil, fiber.NewError(fiber.StatusBadRequest, "kandang_ids is required")
}
if err := common.EnsureRelations(c.Context(),
common.RelationCheck{Name: "Flock", ID: &req.FlockId, Exists: relationExistsChecker[entity.Flock](s.Repository.DB())},
common.RelationCheck{Name: "Area", ID: &req.AreaId, Exists: relationExistsChecker[entity.Area](s.Repository.DB())},
common.RelationCheck{Name: "Product category", ID: &req.ProductCategoryId, Exists: relationExistsChecker[entity.ProductCategory](s.Repository.DB())},
common.RelationCheck{Name: "FCR", ID: &req.FcrId, Exists: relationExistsChecker[entity.Fcr](s.Repository.DB())},
common.RelationCheck{Name: "Location", ID: &req.LocationId, Exists: relationExistsChecker[entity.Location](s.Repository.DB())},
); err != nil {
return nil, err
}
kandangIDs := uniqueUintSlice(req.KandangIds)
kandangs, err := s.KandangRepo.GetByIDs(c.Context(), kandangIDs, nil)
if err != nil {
@@ -128,18 +140,14 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
}
var nextPeriod int
periodQuery := tx.Model(&entity.ProjectFlock{}).
Where("flock_id = ?", req.FlockId).
Clauses(clause.Locking{Strength: "UPDATE"})
if err := periodQuery.Select("COALESCE(MAX(period), 0)").Scan(&nextPeriod).Error; err != nil {
projectRepo := repository.NewProjectflockRepository(tx)
nextPeriod, err := projectRepo.GetNextPeriodForFlock(c.Context(), req.FlockId)
if err != nil {
tx.Rollback()
s.Log.Errorf("Failed to determine next period for flock %d: %+v", req.FlockId, err)
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to determine next period")
}
nextPeriod++
projectRepo := s.Repository.WithTx(tx)
createBody := &entity.ProjectFlock{
FlockId: req.FlockId,
AreaId: req.AreaId,
@@ -190,26 +198,58 @@ func (s projectflockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id
}
updateBody := make(map[string]any)
var relationChecks []common.RelationCheck
if req.FlockId != nil {
updateBody["flock_id"] = *req.FlockId
relationChecks = append(relationChecks, common.RelationCheck{
Name: "Flock",
ID: req.FlockId,
Exists: relationExistsChecker[entity.Flock](s.Repository.DB()),
})
}
if req.AreaId != nil {
updateBody["area_id"] = *req.AreaId
relationChecks = append(relationChecks, common.RelationCheck{
Name: "Area",
ID: req.AreaId,
Exists: relationExistsChecker[entity.Area](s.Repository.DB()),
})
}
if req.ProductCategoryId != nil {
updateBody["product_category_id"] = *req.ProductCategoryId
relationChecks = append(relationChecks, common.RelationCheck{
Name: "Product category",
ID: req.ProductCategoryId,
Exists: relationExistsChecker[entity.ProductCategory](s.Repository.DB()),
})
}
if req.FcrId != nil {
updateBody["fcr_id"] = *req.FcrId
relationChecks = append(relationChecks, common.RelationCheck{
Name: "FCR",
ID: req.FcrId,
Exists: relationExistsChecker[entity.Fcr](s.Repository.DB()),
})
}
if req.LocationId != nil {
updateBody["location_id"] = *req.LocationId
relationChecks = append(relationChecks, common.RelationCheck{
Name: "Location",
ID: req.LocationId,
Exists: relationExistsChecker[entity.Location](s.Repository.DB()),
})
}
if req.Period != nil {
updateBody["period"] = *req.Period
}
if len(relationChecks) > 0 {
if err := common.EnsureRelations(c.Context(), relationChecks...); err != nil {
return nil, err
}
}
var newKandangIDs []uint
if req.KandangIds != nil {
if len(req.KandangIds) == 0 {
@@ -238,7 +278,7 @@ func (s projectflockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
}
projectRepo := s.Repository.WithTx(tx)
projectRepo := repository.NewProjectflockRepository(tx)
if len(updateBody) > 0 {
if err := projectRepo.PatchOne(c.Context(), id, updateBody, nil); err != nil {
tx.Rollback()
@@ -332,7 +372,7 @@ func (s projectflockService) DeleteOne(c *fiber.Ctx, id uint) error {
}
}
if err := s.Repository.WithTx(tx).DeleteOne(c.Context(), id); err != nil {
if err := repository.NewProjectflockRepository(tx).DeleteOne(c.Context(), id); err != nil {
tx.Rollback()
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Projectflock not found")
@@ -385,3 +425,9 @@ func uniqueUintSlice(values []uint) []uint {
}
return result
}
func relationExistsChecker[T any](db *gorm.DB) func(context.Context, uint) (bool, error) {
return func(ctx context.Context, id uint) (bool, error) {
return commonRepo.Exists[T](ctx, db, id)
}
}