Fix adjusment stock chickin, transfer to laying and chickin

This commit is contained in:
ragilap
2026-03-08 23:31:04 +07:00
parent fca96df3d9
commit 45cc057dd4
23 changed files with 1905 additions and 525 deletions
@@ -26,11 +26,15 @@ import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/jackc/pgconn"
pgconnv5 "github.com/jackc/pgx/v5/pgconn"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
const chickinDeletePopulationGuardMessage = "Chickin tidak dapat dihapus karena sudah memiliki population. Lakukan rollback/penyesuaian population terlebih dahulu"
type ChickinService interface {
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectChickin, int64, error)
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectChickin, error)
@@ -189,31 +193,31 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product warehouse %d belongs to different flock. Only product warehouses with project_flock_kandang_id = NULL or = %d can be used", chickinReq.ProductWarehouseId, req.ProjectFlockKandangId))
}
if productWarehouse.Product.Id != 0 {
category := strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category))
if category != string(utils.ProjectFlockCategoryGrowing) && category != string(utils.ProjectFlockCategoryLaying) {
return nil, fmt.Errorf("invalid flock category for chickin")
}
if productWarehouse.Product.Id != 0 {
category := strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category))
if category != string(utils.ProjectFlockCategoryGrowing) && category != string(utils.ProjectFlockCategoryLaying) {
return nil, fmt.Errorf("invalid flock category for chickin")
}
hasAyamFlag := false
for _, flag := range productWarehouse.Product.Flags {
if utils.CanonicalFlagType(flag.Name) == utils.FlagAyam {
hasAyamFlag = true
break
}
}
if !hasAyamFlag {
return nil, fmt.Errorf(
"product warehouse %d cannot be used for %s chickin. Product must have AYAM flag (or legacy alias DOC/PULLET/LAYER) (product ID: %d, warehouse ID: %d)",
chickinReq.ProductWarehouseId,
projectFlockKandang.ProjectFlock.Category,
productWarehouse.Product.Id,
productWarehouse.Id,
)
hasAyamFlag := false
for _, flag := range productWarehouse.Product.Flags {
if utils.CanonicalFlagType(flag.Name) == utils.FlagAyam {
hasAyamFlag = true
break
}
}
if !hasAyamFlag {
return nil, fmt.Errorf(
"product warehouse %d cannot be used for %s chickin. Product must have AYAM flag (or legacy alias DOC/PULLET/LAYER) (product ID: %d, warehouse ID: %d)",
chickinReq.ProductWarehouseId,
projectFlockKandang.ProjectFlock.Category,
productWarehouse.Product.Id,
productWarehouse.Id,
)
}
}
chickinDate, err := utils.ParseDateString(chickinReq.ChickInDate)
if err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid ChickInDate format for product warehouse %d", chickinReq.ProductWarehouseId))
@@ -421,6 +425,14 @@ func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
if err := s.ensureNotTransferred(c.Context(), chickin.ProjectFlockKandangId); err != nil {
return err
}
hasPopulation, err := s.ProjectflockPopulationRepo.ExistsByProjectChickinID(c.Context(), chickin.Id)
if err != nil {
s.Log.Errorf("Failed to check population by chickin %d: %+v", chickin.Id, err)
return fiber.NewError(fiber.StatusInternalServerError, "Gagal memvalidasi population chickin")
}
if hasPopulation {
return fiber.NewError(fiber.StatusBadRequest, chickinDeletePopulationGuardMessage)
}
actorID, err := m.ActorIDFromContext(c)
if err != nil {
@@ -429,17 +441,35 @@ func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
chickinRepoTx := repository.NewChickinRepository(tx)
if chickin.UsageQty > 0 || chickin.PendingUsageQty > 0 {
if err := s.ReleaseChickinStocks(c.Context(), tx, chickin, actorID); err != nil {
return err
}
}
now := time.Now().UTC()
note := "delete chickin rollback"
if err := tx.WithContext(c.Context()).
Model(&entity.StockAllocation{}).
Where("usable_type = ? AND usable_id = ? AND status = ?",
fifo.UsableKeyProjectChickin.String(),
chickin.Id,
entity.StockAllocationStatusActive,
).
Updates(map[string]any{
"status": entity.StockAllocationStatusReleased,
"released_at": now,
"note": note,
}).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Gagal release alokasi FIFO chickin")
}
if err := chickinRepoTx.DeleteOne(c.Context(), id); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
}
if isForeignKeyViolation(err) {
return fiber.NewError(fiber.StatusBadRequest, chickinDeletePopulationGuardMessage)
}
return err
}
@@ -459,6 +489,24 @@ func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
return nil
}
func isForeignKeyViolation(err error) bool {
if err == nil {
return false
}
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
return pgErr.Code == "23503"
}
var pgErrV5 *pgconnv5.PgError
if errors.As(err, &pgErrV5) {
return pgErrV5.Code == "23503"
}
return false
}
func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error) {
if err := s.Validate.Struct(req); err != nil {
return nil, err