diff --git a/internal/modules/production/chickins/module.go b/internal/modules/production/chickins/module.go index 0c7c2a09..143ebad2 100644 --- a/internal/modules/production/chickins/module.go +++ b/internal/modules/production/chickins/module.go @@ -52,13 +52,14 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate * PendingQuantity: "pending_usage_qty", CreatedAt: "created_at", }, + + ExcludedStockables: []fifo.StockableKey{fifo.StockableKeyProjectFlockPopulation}, }); err != nil { if !strings.Contains(strings.ToLower(err.Error()), "already registered") { panic(fmt.Sprintf("failed to register chickin usable workflow: %v", err)) } } - if err := fifoService.RegisterStockable(fifo.StockableConfig{ Key: fifo.StockableKeyProjectFlockPopulation, Table: "project_flock_populations", diff --git a/internal/modules/production/chickins/services/chickin.service.go b/internal/modules/production/chickins/services/chickin.service.go index 02ae12ec..de49bb1e 100644 --- a/internal/modules/production/chickins/services/chickin.service.go +++ b/internal/modules/production/chickins/services/chickin.service.go @@ -112,7 +112,6 @@ func (s chickinService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectChickin, e return nil, fiber.NewError(fiber.StatusNotFound, "Chickin not found") } if err != nil { - s.Log.Errorf("Failed get chickin by id: %+v", err) return nil, err } return chickin, nil @@ -143,7 +142,9 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti for idx, chickinReq := range req.ChickinRequests { - productWarehouse, err := s.ProductWarehouseRepo.GetByID(c.Context(), chickinReq.ProductWarehouseId, nil) + productWarehouse, err := s.ProductWarehouseRepo.GetByID(c.Context(), chickinReq.ProductWarehouseId, func(db *gorm.DB) *gorm.DB { + return db.Preload("Product.Flags") + }) if err != nil { return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Product warehouse %d not found", chickinReq.ProductWarehouseId)) } @@ -156,22 +157,27 @@ 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)) } - // CRITICAL: Validate product category based on flock category - // GROWING: Input DOC, Output PULLET - // LAYING: Input PULLET, Output LAYER if productWarehouse.Product.Id != 0 { - productCategoryCode := productWarehouse.Product.ProductCategory.Code - var allowedCategory string + + var requiredFlag utils.FlagType if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) { - allowedCategory = "DOC" + requiredFlag = utils.FlagDOC } else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) { - allowedCategory = "PULLET" + requiredFlag = utils.FlagPullet } else { return nil, fmt.Errorf("invalid flock category for chickin") } - if productCategoryCode != allowedCategory { - return nil, fmt.Errorf("product warehouse %d cannot be used for %s chickin. Only %s products can be used as input (current: %s)", chickinReq.ProductWarehouseId, projectFlockKandang.ProjectFlock.Category, allowedCategory, productCategoryCode) + hasRequiredFlag := false + for _, flag := range productWarehouse.Product.Flags { + if utils.FlagType(flag.Name) == requiredFlag { + hasRequiredFlag = true + break + } + } + + if !hasRequiredFlag { + return nil, fmt.Errorf("product warehouse %d cannot be used for %s chickin. Product must have %s flag (product ID: %d, warehouse ID: %d)", chickinReq.ProductWarehouseId, projectFlockKandang.ProjectFlock.Category, requiredFlag, productWarehouse.Product.Id, productWarehouse.Id) } } @@ -191,7 +197,18 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti } newChikins = append(newChikins, newChickin) - chickinQtyMap[uint(idx)] = productWarehouse.Quantity + + totalPopulationQty, err := s.ProjectflockPopulationRepo.GetTotalQtyByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId) + if err != nil { + return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to get total population quantity for project_flock_kandang %d", req.ProjectFlockKandangId)) + } + + availableQty := productWarehouse.Quantity - totalPopulationQty + if availableQty < 0 { + availableQty = 0 + } + + chickinQtyMap[uint(idx)] = availableQty } if len(newChikins) == 0 { 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 28fe9853..22c712e5 100644 --- a/internal/modules/production/transfer_layings/services/transfer_laying.service.go +++ b/internal/modules/production/transfer_layings/services/transfer_laying.service.go @@ -301,7 +301,9 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create) } - for _, targetDetail := range req.TargetKandangs { + var firstTargetProductWarehouseID *uint + + for i, targetDetail := range req.TargetKandangs { targetPFK, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetDetail.ProjectFlockKandangId) if err != nil { @@ -316,30 +318,40 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create) return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target warehouse") } + var targetPW entity.ProductWarehouse + err = dbTransaction.Where("warehouse_id = ? AND project_flock_kandang_id = ?", targetWarehouse.Id, targetDetail.ProjectFlockKandangId). + First(&targetPW).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No product warehouse found for target kandang %d in warehouse %d", targetDetail.ProjectFlockKandangId, targetWarehouse.Id)) + } + return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to get product warehouse for target kandang %d: %v", targetDetail.ProjectFlockKandangId, err)) + } + target := entity.LayingTransferTarget{ LayingTransferId: createBody.Id, TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId, Qty: targetDetail.Quantity, - ProductWarehouseId: &targetWarehouse.Id, + ProductWarehouseId: &targetPW.Id, } if err := dbTransaction.Create(&target).Error; err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target") } + + if i == 0 { + firstTargetProductWarehouseID = &targetPW.Id + } } // Set DestProductWarehouseID untuk STOCKABLE role (ambil dari target pertama) - if len(req.TargetKandangs) > 0 { - firstTargetPWID := req.TargetKandangs[0].ProjectFlockKandangId - // Cari ProductWarehouse untuk target kandang - targetWarehouse, _ := s.WarehouseRepo.GetLatestByKandangID(c.Context(), firstTargetPWID) - if targetWarehouse != nil { - // Query ProductWarehouse by warehouse and kandang - var targetPW entity.ProductWarehouse - err := dbTransaction.Where("warehouse_id = ? AND project_flock_kandang_id = ?", targetWarehouse.Id, firstTargetPWID). - First(&targetPW).Error - if err == nil { - createBody.DestProductWarehouseID = &targetPW.Id - } + if firstTargetProductWarehouseID != nil { + createBody.DestProductWarehouseID = firstTargetProductWarehouseID + + // Update DestProductWarehouseID ke database + if err := dbTransaction.Model(&entity.LayingTransfer{}). + Where("id = ?", createBody.Id). + Update("dest_product_warehouse_id", *firstTargetProductWarehouseID).Error; err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to update DestProductWarehouseID") } } @@ -504,11 +516,21 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target warehouse") } + var targetPW entity.ProductWarehouse + err = dbTransaction.Where("warehouse_id = ? AND project_flock_kandang_id = ?", targetWarehouse.Id, targetDetail.ProjectFlockKandangId). + First(&targetPW).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No product warehouse found for target kandang %d in warehouse %d", targetDetail.ProjectFlockKandangId, targetWarehouse.Id)) + } + return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to get product warehouse for target kandang %d: %v", targetDetail.ProjectFlockKandangId, err)) + } + target := entity.LayingTransferTarget{ LayingTransferId: id, TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId, Qty: targetDetail.Quantity, - ProductWarehouseId: &targetWarehouse.Id, + ProductWarehouseId: &targetPW.Id, } if err := dbTransaction.Create(&target).Error; err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target")