mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 23:35:43 +00:00
FIX[BE]: fixing bug transfer to laying, delet biaya, nominal expesen e, chickin
This commit is contained in:
@@ -214,9 +214,6 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
||||
pendingQtyMap[existingChickin.ProductWarehouseId] += existingChickin.PendingUsageQty
|
||||
}
|
||||
}
|
||||
|
||||
// CRITICAL: Validate chickins sequentially to prevent over-allocation within the same request
|
||||
// pendingQtyMap is accumulated as we validate each chickin to ensure total pending doesn't exceed available stock
|
||||
for idx, chickin := range newChikins {
|
||||
pendingQty := pendingQtyMap[chickin.ProductWarehouseId]
|
||||
desiredQty := chickinQtyMap[uint(idx)]
|
||||
@@ -232,8 +229,6 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
|
||||
|
||||
chickinQtyMap[uint(idx)] = availableQty
|
||||
|
||||
// ACCUMULATE pending for this product warehouse so NEXT chickin in same request sees it
|
||||
// This prevents multiple chickins in same request from over-allocating the same stock
|
||||
pendingQtyMap[chickin.ProductWarehouseId] += availableQty
|
||||
}
|
||||
|
||||
@@ -358,12 +353,15 @@ func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
}
|
||||
|
||||
if chickin.UsageQty > 0 {
|
||||
|
||||
currentUsageQty := chickin.UsageQty
|
||||
|
||||
if err := s.ReleaseChickinStocks(c.Context(), s.Repository.DB(), chickin, actorID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
warehouseDeltas := make(map[uint]float64)
|
||||
warehouseDeltas[chickin.ProductWarehouseId] += chickin.UsageQty
|
||||
warehouseDeltas[chickin.ProductWarehouseId] += currentUsageQty
|
||||
if err := s.adjustProductWarehouseQuantities(c.Context(), s.Repository.DB(), warehouseDeltas); err != nil {
|
||||
s.Log.Errorf("Failed to adjust product warehouses for deleted chickin %d: %+v", chickin.Id, err)
|
||||
return err
|
||||
@@ -618,7 +616,7 @@ func (s *chickinService) convertChickinsToTarget(ctx *fiber.Ctx, chickins []enti
|
||||
population := &entity.ProjectFlockPopulation{
|
||||
ProjectChickinId: chickin.Id,
|
||||
ProductWarehouseId: targetPW.Id,
|
||||
TotalQty: quantityToConvert,
|
||||
TotalQty: 0, // Will be set by FIFO Replenish
|
||||
TotalUsedQty: 0,
|
||||
Notes: chickin.Notes,
|
||||
CreatedBy: actorID,
|
||||
@@ -634,15 +632,22 @@ func (s *chickinService) convertChickinsToTarget(ctx *fiber.Ctx, chickins []enti
|
||||
return fmt.Errorf("failed to reset pending usage qty for chickin %d: %w", chickin.Id, err)
|
||||
}
|
||||
|
||||
// Replenish stock to target ProductWarehouse based on source flag
|
||||
// StockableKey is PROJECT_CHICKIN but StockableID refers to Population ID
|
||||
if err := s.ReplenishChickinStocks(ctx.Context(), dbTransaction, &chickin, targetPW, population, actorID); err != nil {
|
||||
s.Log.Errorf("Failed to replenish stock for chickin %d: %+v", chickin.Id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
totalQuantityAdded += quantityToConvert
|
||||
}
|
||||
|
||||
// NOTE: Tidak menambah target ProductWarehouse quantity karena:
|
||||
// 1. Ayam sudah dipakai (masuk population)
|
||||
// 2. ProductWarehouse source sudah berkurang saat create chickin (ConsumeChickinStocks)
|
||||
// 3. Menambah quantity disini akan menyebabkan double count
|
||||
//
|
||||
// PULLET/LAYER untuk flock ini akan di-add lewat mekanisme lain (misal: purchase, transfer, dll)
|
||||
// NOTE: ProductWarehouse target sudah ditambah melalui ReplenishChickinStocks
|
||||
// yang dipanggil di atas untuk setiap chickin berdasarkan flag source:
|
||||
// - DOC → replenish ke PULLET
|
||||
// - PULLET → replenish ke LAYER
|
||||
// - LAYER → tidak perlu replenish (sudah final)
|
||||
// - DOC+PULLET+LAYER → replenish ke dirinya sendiri
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -671,10 +676,7 @@ func (s *chickinService) ConsumeChickinStocks(ctx context.Context, tx *gorm.DB,
|
||||
s.Log.Infof("ConsumeChickinStocks result: usage_qty=%.3f, pending_qty=%.3f, allocated_allocations=%d",
|
||||
result.UsageQuantity, result.PendingQuantity, len(result.AddedAllocations))
|
||||
|
||||
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Updates(map[string]interface{}{
|
||||
"usage_qty": result.UsageQuantity,
|
||||
"pending_usage_qty": result.PendingQuantity,
|
||||
}).Error; err != nil {
|
||||
if err := s.Repository.UpdateUsageFields(ctx, tx, chickin.Id, result.UsageQuantity, result.PendingQuantity); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -696,6 +698,101 @@ func (s *chickinService) ConsumeChickinStocks(ctx context.Context, tx *gorm.DB,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *chickinService) ReplenishChickinStocks(ctx context.Context, tx *gorm.DB, chickin *entity.ProjectChickin, targetPW *entity.ProductWarehouse, population *entity.ProjectFlockPopulation, actorID uint) error {
|
||||
if chickin == nil || targetPW == nil || population == nil || s.FifoSvc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sourcePW, err := s.ProductWarehouseRepo.GetByID(ctx, chickin.ProductWarehouseId, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Product.Flags")
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
return err
|
||||
}
|
||||
if sourcePW == nil || sourcePW.Product.Id == 0 {
|
||||
return fmt.Errorf("source product warehouse or product not found for chickin %d", chickin.Id)
|
||||
}
|
||||
|
||||
sourceFlags := sourcePW.Product.Flags
|
||||
if len(sourceFlags) == 0 {
|
||||
s.Log.Warnf("Source product %d has no flags, skipping replenish for chickin %d", sourcePW.Product.Id, chickin.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
hasDoc := false
|
||||
hasPullet := false
|
||||
hasLayer := false
|
||||
for _, flag := range sourceFlags {
|
||||
flagName := utils.FlagType(flag.Name)
|
||||
if flagName == utils.FlagDOC {
|
||||
hasDoc = true
|
||||
} else if flagName == utils.FlagPullet {
|
||||
hasPullet = true
|
||||
} else if flagName == utils.FlagLayer {
|
||||
hasLayer = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasDoc && hasPullet && hasLayer {
|
||||
s.Log.Infof("Chickin %d has mixed flags (DOC+PULLET+LAYER), replenishing to source PW %d", chickin.Id, sourcePW.Id)
|
||||
_, err = s.FifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
||||
StockableKey: fifo.StockableKeyProjectFlockPopulation,
|
||||
StockableID: population.Id,
|
||||
ProductWarehouseID: sourcePW.Id,
|
||||
Quantity: chickin.UsageQty,
|
||||
Tx: tx,
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to replenish stock to source PW for chickin %d: %+v", chickin.Id, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LAYER only - no replenish needed
|
||||
if hasLayer && !hasDoc && !hasPullet {
|
||||
s.Log.Infof("Chickin %d has LAYER flag only, skipping replenish", chickin.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
if hasDoc && !hasPullet && !hasLayer {
|
||||
s.Log.Infof("Chickin %d has DOC flag, replenishing to PULLET PW %d", chickin.Id, targetPW.Id)
|
||||
_, err = s.FifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
||||
StockableKey: fifo.StockableKeyProjectFlockPopulation,
|
||||
StockableID: population.Id,
|
||||
ProductWarehouseID: targetPW.Id,
|
||||
Quantity: chickin.UsageQty,
|
||||
Tx: tx,
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to replenish stock to PULLET PW for chickin %d: %+v", chickin.Id, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if hasPullet && !hasDoc && !hasLayer {
|
||||
s.Log.Infof("Chickin %d has PULLET flag, replenishing to LAYER PW %d", chickin.Id, targetPW.Id)
|
||||
_, err = s.FifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
||||
StockableKey: fifo.StockableKeyProjectFlockPopulation,
|
||||
StockableID: population.Id,
|
||||
ProductWarehouseID: targetPW.Id,
|
||||
Quantity: chickin.UsageQty,
|
||||
Tx: tx,
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to replenish stock to LAYER PW for chickin %d: %+v", chickin.Id, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Other combinations (e.g., DOC + PULLET without LAYER) - skip for now
|
||||
s.Log.Warnf("Chickin %d has unsupported flag combination, skipping replenish", chickin.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB, chickin *entity.ProjectChickin, actorID uint) error {
|
||||
if chickin == nil || s.FifoSvc == nil {
|
||||
return nil
|
||||
@@ -703,8 +800,7 @@ func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB,
|
||||
|
||||
var currentUsage float64
|
||||
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Select("usage_qty").Scan(¤tUsage).Error; err != nil {
|
||||
s.Log.Warnf("Failed to get current usage for chickin %d: %+v", chickin.Id, err)
|
||||
currentUsage = 0
|
||||
|
||||
}
|
||||
|
||||
if err := s.FifoSvc.ReleaseUsage(ctx, commonSvc.StockReleaseRequest{
|
||||
@@ -716,14 +812,10 @@ func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Updates(map[string]interface{}{
|
||||
"usage_qty": 0,
|
||||
"pending_usage_qty": 0,
|
||||
}).Error; err != nil {
|
||||
if err := s.Repository.UpdateUsageFields(ctx, tx, chickin.Id, 0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create stock log for the restoration
|
||||
if currentUsage > 0 {
|
||||
increaseLog := &entity.StockLog{
|
||||
Increase: currentUsage,
|
||||
@@ -734,8 +826,7 @@ func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB,
|
||||
Notes: fmt.Sprintf("Chickin #%d - Stock released", chickin.Id),
|
||||
}
|
||||
if err := s.StockLogRepo.CreateOne(ctx, increaseLog, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create stock log for chickin %d: %+v", chickin.Id, err)
|
||||
// Don't return error here, stock already released
|
||||
s.Log.Errorf("Failed to create stock log for released chickin %d: %+v", chickin.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user