feat[BE-127]; creating correct logic update and delete transfer laying

This commit is contained in:
aguhh18
2025-11-06 10:31:35 +07:00
parent 4aed480662
commit 1c99093ff8
2 changed files with 161 additions and 19 deletions
@@ -361,7 +361,9 @@ func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
return nil, err
}
_, err := s.Repository.GetByID(c.Context(), id, nil)
existingTransfer, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
return db.Preload("Sources.ProductWarehouse").Preload("Targets")
})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "TransferLaying not found")
@@ -382,26 +384,140 @@ func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
}
}
updateBody := make(map[string]any)
if req.TransferDate != nil {
updateBody["transfer_date"] = *req.TransferDate
if _, err := s.ProjectFlockRepo.GetByID(c.Context(), req.SourceProjectFlockId, nil); err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, "Source project flock not found")
}
if req.Reason != nil {
updateBody["notes"] = *req.Reason
if _, err := s.ProjectFlockRepo.GetByID(c.Context(), req.TargetProjectFlockId, nil); err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, "Target project flock not found")
}
if len(updateBody) == 0 {
return s.GetOne(c, id)
transferDate, err := time.Parse("2006-01-02", req.TransferDate)
if err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid transfer date format")
}
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "TransferLaying not found")
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
projectFlockPopulationRepoTx := s.ProjectFlockPopulationRepo.WithTx(dbTransaction)
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
for _, oldSource := range existingTransfer.Sources {
if oldSource.ProductWarehouseId != nil && oldSource.Qty > 0 {
if err := productWarehouseRepoTx.PatchOne(c.Context(), *oldSource.ProductWarehouseId, map[string]any{
"quantity": gorm.Expr("quantity + ?", oldSource.Qty),
}, nil); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to restore warehouse quantity")
}
if err := s.restoreProjectFlockPopulation(c.Context(), projectFlockPopulationRepoTx, oldSource.SourceProjectFlockKandangId, oldSource.Qty); err != nil {
return err
}
}
}
s.Log.Errorf("Failed to update transferLaying: %+v", err)
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update transfer laying")
for _, oldSource := range existingTransfer.Sources {
if err := dbTransaction.Delete(&oldSource).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete old source")
}
}
for _, oldTarget := range existingTransfer.Targets {
if err := dbTransaction.Delete(&oldTarget).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete old target")
}
}
totalSourceQty := 0.0
for _, source := range req.SourceKandangs {
totalSourceQty += source.Quantity
}
if err := s.Repository.WithTx(dbTransaction).PatchOne(c.Context(), id, map[string]any{
"transfer_date": transferDate,
"notes": req.Reason,
"pending_usage_qty": &totalSourceQty,
}, nil); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update transfer header")
}
sourceWarehouseMap := make(map[uint]uint)
for _, sourceDetail := range req.SourceKandangs {
populations, err := projectFlockPopulationRepoTx.GetByProjectFlockKandangID(c.Context(), sourceDetail.ProjectFlockKandangId)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get populations")
}
if len(populations) == 0 {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Source kandang %d has no population available", sourceDetail.ProjectFlockKandangId))
}
var totalPopulation float64
var productWarehouseId uint
for _, pop := range populations {
totalPopulation += pop.TotalQty
if pop.ProductWarehouseId > 0 {
productWarehouseId = pop.ProductWarehouseId
}
}
if totalPopulation < sourceDetail.Quantity {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Source kandang %d has insufficient quantity. Available: %.0f, Requested: %.0f", sourceDetail.ProjectFlockKandangId, totalPopulation, sourceDetail.Quantity))
}
sourceWarehouseMap[sourceDetail.ProjectFlockKandangId] = productWarehouseId
source := entity.LayingTransferSource{
LayingTransferId: id,
SourceProjectFlockKandangId: sourceDetail.ProjectFlockKandangId,
Qty: sourceDetail.Quantity,
ProductWarehouseId: &productWarehouseId,
}
if err := dbTransaction.Create(&source).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer source")
}
if err := s.reduceProjectFlockPopulation(c.Context(), projectFlockPopulationRepoTx, sourceDetail.ProjectFlockKandangId, sourceDetail.Quantity); err != nil {
return err
}
if err := productWarehouseRepoTx.PatchOne(c.Context(), productWarehouseId, map[string]any{"quantity": gorm.Expr("quantity - ?", sourceDetail.Quantity)}, nil); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update source warehouse quantity")
}
}
for _, targetDetail := range req.TargetKandangs {
targetPFK, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetDetail.ProjectFlockKandangId)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target project flock kandang")
}
targetWarehouse, err := s.WarehouseRepo.GetLatestByKandangID(c.Context(), targetPFK.KandangId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No warehouse found for target kandang %d", targetDetail.ProjectFlockKandangId))
}
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target warehouse")
}
target := entity.LayingTransferTarget{
LayingTransferId: id,
TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId,
Qty: targetDetail.Quantity,
ProductWarehouseId: &targetWarehouse.Id,
}
if err := dbTransaction.Create(&target).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target")
}
}
return nil
})
if err != nil {
return nil, err
}
return s.GetOne(c, id)
@@ -464,9 +580,8 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
for i := len(populations) - 1; i >= 0 && remainingToRestore > 0; i-- {
pop := populations[i]
restoreAmount := remainingToRestore
if remainingToRestore < pop.TotalQty {
restoreAmount = remainingToRestore
if pop.TotalQty < remainingToRestore {
restoreAmount = pop.TotalQty
}
newQty := pop.TotalQty + restoreAmount
@@ -725,3 +840,26 @@ func (s *transferLayingService) reduceProjectFlockPopulation(ctx context.Context
return nil
}
func (s *transferLayingService) restoreProjectFlockPopulation(ctx context.Context, populationRepo ProjectFlockRepository.ProjectFlockPopulationRepository, projectFlockKandangID uint, quantityToRestore float64) error {
populations, err := populationRepo.GetByProjectFlockKandangID(ctx, projectFlockKandangID)
if err != nil {
return err
}
if len(populations) == 0 {
return fiber.NewError(fiber.StatusBadRequest, "No populations found for restoration")
}
// Restore in LIFO order (from newest to oldest)
// Add all quantity back to the last (newest) population
if len(populations) > 0 {
lastPop := populations[len(populations)-1]
newQty := lastPop.TotalQty + quantityToRestore
if err := populationRepo.PatchOne(ctx, lastPop.Id, map[string]any{"total_qty": newQty}, nil); err != nil {
return err
}
}
return nil
}
@@ -20,8 +20,12 @@ type Create struct {
}
type Update struct {
TransferDate *string `json:"transfer_date,omitempty" validate:"omitempty,datetime=2006-01-02"`
Reason *string `json:"reason,omitempty" validate:"omitempty,max=1000"`
TransferDate string `json:"transfer_date" validate:"required,datetime=2006-01-02"`
SourceProjectFlockId uint `json:"source_project_flock_id" validate:"required"`
TargetProjectFlockId uint `json:"target_project_flock_id" validate:"required"`
SourceKandangs []SourceKandangDetail `json:"source_kandangs" validate:"required,min=1,dive,required"`
TargetKandangs []TargetKandangDetail `json:"target_kandangs" validate:"required,min=1,dive,required"`
Reason string `json:"reason" validate:"omitempty,max=1000"`
}
type Query struct {