mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat(BE): enhance chickin and transfer laying services with product warehouse validation and stockable support
This commit is contained in:
@@ -52,13 +52,14 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
PendingQuantity: "pending_usage_qty",
|
PendingQuantity: "pending_usage_qty",
|
||||||
CreatedAt: "created_at",
|
CreatedAt: "created_at",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ExcludedStockables: []fifo.StockableKey{fifo.StockableKeyProjectFlockPopulation},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if !strings.Contains(strings.ToLower(err.Error()), "already registered") {
|
if !strings.Contains(strings.ToLower(err.Error()), "already registered") {
|
||||||
panic(fmt.Sprintf("failed to register chickin usable workflow: %v", err))
|
panic(fmt.Sprintf("failed to register chickin usable workflow: %v", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if err := fifoService.RegisterStockable(fifo.StockableConfig{
|
if err := fifoService.RegisterStockable(fifo.StockableConfig{
|
||||||
Key: fifo.StockableKeyProjectFlockPopulation,
|
Key: fifo.StockableKeyProjectFlockPopulation,
|
||||||
Table: "project_flock_populations",
|
Table: "project_flock_populations",
|
||||||
|
|||||||
@@ -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")
|
return nil, fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed get chickin by id: %+v", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return chickin, nil
|
return chickin, nil
|
||||||
@@ -143,7 +142,9 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
|||||||
|
|
||||||
for idx, chickinReq := range req.ChickinRequests {
|
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 {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Product warehouse %d not found", chickinReq.ProductWarehouseId))
|
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))
|
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 {
|
if productWarehouse.Product.Id != 0 {
|
||||||
productCategoryCode := productWarehouse.Product.ProductCategory.Code
|
|
||||||
var allowedCategory string
|
var requiredFlag utils.FlagType
|
||||||
if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) {
|
if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
allowedCategory = "DOC"
|
requiredFlag = utils.FlagDOC
|
||||||
} else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
} else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
allowedCategory = "PULLET"
|
requiredFlag = utils.FlagPullet
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("invalid flock category for chickin")
|
return nil, fmt.Errorf("invalid flock category for chickin")
|
||||||
}
|
}
|
||||||
|
|
||||||
if productCategoryCode != allowedCategory {
|
hasRequiredFlag := false
|
||||||
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)
|
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)
|
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 {
|
if len(newChikins) == 0 {
|
||||||
|
|||||||
@@ -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)
|
targetPFK, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetDetail.ProjectFlockKandangId)
|
||||||
if err != nil {
|
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")
|
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{
|
target := entity.LayingTransferTarget{
|
||||||
LayingTransferId: createBody.Id,
|
LayingTransferId: createBody.Id,
|
||||||
TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId,
|
TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId,
|
||||||
Qty: targetDetail.Quantity,
|
Qty: targetDetail.Quantity,
|
||||||
ProductWarehouseId: &targetWarehouse.Id,
|
ProductWarehouseId: &targetPW.Id,
|
||||||
}
|
}
|
||||||
if err := dbTransaction.Create(&target).Error; err != nil {
|
if err := dbTransaction.Create(&target).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target")
|
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)
|
// Set DestProductWarehouseID untuk STOCKABLE role (ambil dari target pertama)
|
||||||
if len(req.TargetKandangs) > 0 {
|
if firstTargetProductWarehouseID != nil {
|
||||||
firstTargetPWID := req.TargetKandangs[0].ProjectFlockKandangId
|
createBody.DestProductWarehouseID = firstTargetProductWarehouseID
|
||||||
// Cari ProductWarehouse untuk target kandang
|
|
||||||
targetWarehouse, _ := s.WarehouseRepo.GetLatestByKandangID(c.Context(), firstTargetPWID)
|
// Update DestProductWarehouseID ke database
|
||||||
if targetWarehouse != nil {
|
if err := dbTransaction.Model(&entity.LayingTransfer{}).
|
||||||
// Query ProductWarehouse by warehouse and kandang
|
Where("id = ?", createBody.Id).
|
||||||
var targetPW entity.ProductWarehouse
|
Update("dest_product_warehouse_id", *firstTargetProductWarehouseID).Error; err != nil {
|
||||||
err := dbTransaction.Where("warehouse_id = ? AND project_flock_kandang_id = ?", targetWarehouse.Id, firstTargetPWID).
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update DestProductWarehouseID")
|
||||||
First(&targetPW).Error
|
|
||||||
if err == nil {
|
|
||||||
createBody.DestProductWarehouseID = &targetPW.Id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,11 +516,21 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target warehouse")
|
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{
|
target := entity.LayingTransferTarget{
|
||||||
LayingTransferId: id,
|
LayingTransferId: id,
|
||||||
TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId,
|
TargetProjectFlockKandangId: targetDetail.ProjectFlockKandangId,
|
||||||
Qty: targetDetail.Quantity,
|
Qty: targetDetail.Quantity,
|
||||||
ProductWarehouseId: &targetWarehouse.Id,
|
ProductWarehouseId: &targetPW.Id,
|
||||||
}
|
}
|
||||||
if err := dbTransaction.Create(&target).Error; err != nil {
|
if err := dbTransaction.Create(&target).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target")
|
||||||
|
|||||||
Reference in New Issue
Block a user