From 22bd0e589150661bd0c7ba1b4bf5614b4169d24b Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Fri, 30 Jan 2026 08:24:43 +0700 Subject: [PATCH] FEAT[BE] :enhance UpdateOne method with validation for source and target kandang ownership and quantity checks --- .../services/transfer_laying.service.go | 141 +++++++++++++----- 1 file changed, 105 insertions(+), 36 deletions(-) diff --git a/internal/modules/production/transfer_layings/services/transfer_laying.service.go b/internal/modules/production/transfer_layings/services/transfer_laying.service.go index 2acc2e8f..396944e1 100644 --- a/internal/modules/production/transfer_layings/services/transfer_laying.service.go +++ b/internal/modules/production/transfer_layings/services/transfer_laying.service.go @@ -444,15 +444,105 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, return nil, fiber.NewError(fiber.StatusBadRequest, "Target project flock not found") } + sourceKandangIDs := make([]uint, len(req.SourceKandangs)) + for i, detail := range req.SourceKandangs { + sourceKandangIDs[i] = detail.ProjectFlockKandangId + } + + if err := s.validateKandangOwnership( + c.Context(), + req.SourceProjectFlockId, + sourceKandangIDs, + ); err != nil { + return nil, err + } + + targetKandangIDs := make([]uint, len(req.TargetKandangs)) + for i, detail := range req.TargetKandangs { + targetKandangIDs[i] = detail.ProjectFlockKandangId + } + + if err := s.validateKandangOwnership( + c.Context(), + req.TargetProjectFlockId, + targetKandangIDs, + ); err != nil { + return nil, err + } + transferDate, err := time.Parse("2006-01-02", req.TransferDate) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid transfer date format") } + var totalSourceQty, totalTargetQty float64 + sourceWarehouseMap := make(map[uint]uint) + + for _, sourceDetail := range req.SourceKandangs { + if sourceDetail.Quantity <= 0 { + continue + } + totalSourceQty += sourceDetail.Quantity + + populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), sourceDetail.ProjectFlockKandangId) + if err != nil { + return nil, err + } + + var totalPopulation float64 + var productWarehouseId uint + for _, pop := range populations { + totalPopulation += pop.TotalQty + if productWarehouseId == 0 { + productWarehouseId = pop.ProductWarehouseId + } + } + + if totalPopulation == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Kandang sumber %d tidak memiliki populasi untuk ditransfer", sourceDetail.ProjectFlockKandangId)) + } + + if totalPopulation < sourceDetail.Quantity { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Kandang sumber %d jumlah tidak mencukupi. Tersedia: %.0f, Diminta: %.0f", sourceDetail.ProjectFlockKandangId, totalPopulation, sourceDetail.Quantity)) + } + + sourceWarehouseMap[sourceDetail.ProjectFlockKandangId] = productWarehouseId + } + + for _, targetDetail := range req.TargetKandangs { + if targetDetail.Quantity <= 0 { + continue + } + totalTargetQty += targetDetail.Quantity + } + + if totalSourceQty == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Minimal harus ada 1 kandang sumber dengan jumlah lebih dari 0") + } + if totalTargetQty == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Minimal harus ada 1 kandang tujuan dengan jumlah lebih dari 0") + } + + if totalSourceQty != totalTargetQty { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Jumlah total sumber (%.0f) harus sama dengan jumlah total tujuan (%.0f)", totalSourceQty, totalTargetQty)) + } + + // Ambil productWarehouseId pertama dari source yang valid (quantity > 0) + var firstProductWarehouseId uint + for _, sourceDetail := range req.SourceKandangs { + if sourceDetail.Quantity > 0 { + if pwId, ok := sourceWarehouseMap[sourceDetail.ProjectFlockKandangId]; ok { + firstProductWarehouseId = pwId + break + } + } + } + err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error { repoTx := s.Repository.WithTx(dbTransaction) sourceRepo := s.LayingTransferSourceRepo.WithTx(dbTransaction) targetRepo := s.LayingTransferTargetRepo.WithTx(dbTransaction) + pwRepo := rInventory.NewProductWarehouseRepository(dbTransaction) // Hapus old sources dan targets for _, oldSource := range existingTransfer.Sources { @@ -476,26 +566,11 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, // Create new sources dengan pending quantity for _, sourceDetail := range req.SourceKandangs { - populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), sourceDetail.ProjectFlockKandangId) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get populations") + if sourceDetail.Quantity == 0 { + continue } - if len(populations) == 0 { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Source kandang %d has no population available", sourceDetail.ProjectFlockKandangId)) - } - - var productWarehouseId uint - for _, pop := range populations { - if pop.ProductWarehouseId > 0 { - productWarehouseId = pop.ProductWarehouseId - break - } - } - - if productWarehouseId == 0 { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Source kandang %d has no product warehouse", sourceDetail.ProjectFlockKandangId)) - } + productWarehouseId := sourceWarehouseMap[sourceDetail.ProjectFlockKandangId] source := entity.LayingTransferSource{ LayingTransferId: id, @@ -510,7 +585,18 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, } } - pwRepo := rInventory.NewProductWarehouseRepository(dbTransaction) + // Ambil product ID dari source warehouse pertama yang valid + var sourceProductID uint + if firstProductWarehouseId > 0 { + sourcePW, err := pwRepo.GetByID(c.Context(), firstProductWarehouseId, nil) + if err == nil { + sourceProductID = sourcePW.ProductId + } + } + + if sourceProductID == 0 { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get product from source warehouse") + } for _, targetDetail := range req.TargetKandangs { targetprojectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetDetail.ProjectFlockKandangId) @@ -526,23 +612,6 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target warehouse") } - // Ambil product ID dari source yang pertama (semua sources seharusnya product-nya sama) - var sourceProductID uint - if len(req.SourceKandangs) > 0 { - firstSourceKandangID := req.SourceKandangs[0].ProjectFlockKandangId - populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), firstSourceKandangID) - if err == nil && len(populations) > 0 && populations[0].ProductWarehouseId > 0 { - sourcePW, err := pwRepo.GetByID(c.Context(), populations[0].ProductWarehouseId, nil) - if err == nil { - sourceProductID = sourcePW.ProductId - } - } - } - - if sourceProductID == 0 { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get product from source warehouse") - } - targetPW, err := pwRepo.FindByProductWarehouseAndPfk(c.Context(), sourceProductID, targetWarehouse.Id, &targetDetail.ProjectFlockKandangId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) {