mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 15:25:43 +00:00
feat(be): add GetByProjectFlockKandangIDForUpdate method to lock chickin rows and prevent race conditions
This commit is contained in:
@@ -137,6 +137,7 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newChikins := make([]*entity.ProjectChickin, 0)
|
||||
chickinQtyMap := make(map[uint]float64)
|
||||
|
||||
@@ -151,8 +152,8 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product warehouse %d is not bound to kandang's warehouse", chickinReq.ProductWarehouseId))
|
||||
}
|
||||
|
||||
if productWarehouse.ProjectFlockKandangId == nil || *productWarehouse.ProjectFlockKandangId != req.ProjectFlockKandangId {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product warehouse %d is not attached to project_flock_kandang %d. Only product warehouses with matching project_flock_kandang_id can be chickin-ed", chickinReq.ProductWarehouseId, req.ProjectFlockKandangId))
|
||||
if productWarehouse.ProjectFlockKandangId != nil && *productWarehouse.ProjectFlockKandangId != 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))
|
||||
}
|
||||
|
||||
chickinDate, err := utils.ParseDateString(chickinReq.ChickInDate)
|
||||
@@ -160,11 +161,6 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid ChickInDate format for product warehouse %d", chickinReq.ProductWarehouseId))
|
||||
}
|
||||
|
||||
availableQty := productWarehouse.Quantity
|
||||
if availableQty <= 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No available stock in product warehouse %d for chickin", chickinReq.ProductWarehouseId))
|
||||
}
|
||||
|
||||
newChickin := &entity.ProjectChickin{
|
||||
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||
ChickInDate: chickinDate,
|
||||
@@ -176,22 +172,46 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
||||
}
|
||||
|
||||
newChikins = append(newChikins, newChickin)
|
||||
chickinQtyMap[uint(idx)] = availableQty
|
||||
chickinQtyMap[uint(idx)] = productWarehouse.Quantity
|
||||
}
|
||||
|
||||
if len(newChikins) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "No chickins to create")
|
||||
}
|
||||
|
||||
existingChikins, err := s.Repository.GetByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing chickins")
|
||||
}
|
||||
|
||||
isFirstTime := len(existingChikins) == 0
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
repositoryTx := repository.NewChickinRepository(dbTransaction)
|
||||
existingChikins, err := repositoryTx.GetByProjectFlockKandangIDForUpdate(c.Context(), req.ProjectFlockKandangId)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing chickins")
|
||||
}
|
||||
|
||||
isFirstTime := len(existingChikins) == 0
|
||||
|
||||
pendingQtyMap := make(map[uint]float64)
|
||||
for _, existingChickin := range existingChikins {
|
||||
if existingChickin.PendingUsageQty > 0 {
|
||||
pendingQtyMap[existingChickin.ProductWarehouseId] += existingChickin.PendingUsageQty
|
||||
}
|
||||
}
|
||||
|
||||
for idx, chickin := range newChikins {
|
||||
pendingQty := pendingQtyMap[chickin.ProductWarehouseId]
|
||||
desiredQty := chickinQtyMap[uint(idx)]
|
||||
|
||||
availableQty := desiredQty - pendingQty
|
||||
if availableQty < 0 {
|
||||
availableQty = 0
|
||||
}
|
||||
|
||||
if availableQty <= 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No available stock in product warehouse %d for chickin. Warehouse: %.0f, Pending: %.0f, Available: %.0f", chickin.ProductWarehouseId, desiredQty, pendingQty, availableQty))
|
||||
}
|
||||
|
||||
chickinQtyMap[uint(idx)] = availableQty
|
||||
}
|
||||
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
if err := s.Repository.WithTx(dbTransaction).CreateMany(c.Context(), newChikins, nil); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user