mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
FEAT[BE] :implement proportional distribution with rounding for stock allocation in transfer laying approval process
This commit is contained in:
@@ -520,6 +520,7 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
|
||||
CreatedBy: actorID,
|
||||
Notes: fmt.Sprintf("FIFO consume (%.2f)", result.UsageQuantity),
|
||||
}
|
||||
|
||||
stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, marketingProduct.ProductWarehouseId, 1)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs")
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -743,7 +744,7 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
repoTx := s.Repository.WithTx(dbTransaction)
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
stockAllocationRepo := commonRepo.NewStockAllocationRepository(dbTransaction)
|
||||
sourceRepoTx := repository.NewLayingTransferSourceRepository(dbTransaction)
|
||||
targetRepoTx := repository.NewLayingTransferTargetRepository(dbTransaction)
|
||||
stockLogRepoTx := rStockLogs.NewStockLogRepository(dbTransaction)
|
||||
@@ -817,6 +818,27 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal update source usage qty")
|
||||
}
|
||||
|
||||
targetShares := distributeProportionalWithRounding(targets, totalTargetQty, sourceShare)
|
||||
|
||||
for i, target := range targets {
|
||||
roundedQty := math.Round(targetShares[i])
|
||||
if roundedQty <= 0 {
|
||||
continue
|
||||
}
|
||||
mappingAllocation := &entity.StockAllocation{
|
||||
StockableType: fifo.UsableKeyTransferToLayingOut.String(),
|
||||
StockableId: source.Id,
|
||||
UsableType: fifo.StockableKeyTransferToLayingIn.String(),
|
||||
UsableId: target.Id,
|
||||
ProductWarehouseId: *source.ProductWarehouseId,
|
||||
Qty: roundedQty,
|
||||
Status: entity.StockAllocationStatusActive,
|
||||
}
|
||||
if err := stockAllocationRepo.CreateOne(c.Context(), mappingAllocation, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal create mapping allocation source→target")
|
||||
}
|
||||
}
|
||||
|
||||
stockLogDecrease := &entity.StockLog{
|
||||
ProductWarehouseId: *source.ProductWarehouseId,
|
||||
CreatedBy: actorID,
|
||||
@@ -937,36 +959,6 @@ func createApprovalTransferLaying(ctx context.Context, tx *gorm.DB, transferLayi
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *transferLayingService) getOrCreateProductWarehouse(ctx context.Context, tx *gorm.DB, productID uint, warehouseID uint, quantity float64, actorID uint, projectFlockKandangId *uint) (*entity.ProductWarehouse, error) {
|
||||
|
||||
productWarehouseRepoTx := rInventory.NewProductWarehouseRepository(tx)
|
||||
|
||||
existing, err := productWarehouseRepoTx.GetProductWarehouseByProductAndWarehouseID(ctx, productID, warehouseID)
|
||||
if err == nil && existing != nil {
|
||||
|
||||
if err := productWarehouseRepoTx.PatchOne(ctx, existing.Id, map[string]any{"qty": gorm.Expr("qty + ?", quantity)}, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return existing, nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newWarehouse := &entity.ProductWarehouse{
|
||||
ProductId: productID,
|
||||
WarehouseId: warehouseID,
|
||||
ProjectFlockKandangId: projectFlockKandangId,
|
||||
Quantity: quantity,
|
||||
}
|
||||
|
||||
if err := productWarehouseRepoTx.CreateOne(ctx, newWarehouse, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newWarehouse, nil
|
||||
}
|
||||
|
||||
func (s transferLayingService) GetAvailableQtyPerKandang(ctx *fiber.Ctx, projectFlockID uint) (*entity.ProjectFlock, map[uint]float64, error) {
|
||||
|
||||
pf, err := s.ProjectFlockRepo.GetByID(ctx.Context(), projectFlockID, func(db *gorm.DB) *gorm.DB {
|
||||
@@ -1060,3 +1052,34 @@ func (s transferLayingService) GetMaxTargetQtyPerKandang(c *fiber.Ctx, projectFl
|
||||
|
||||
return kandangMaxTargetQty, nil
|
||||
}
|
||||
|
||||
func distributeProportionalWithRounding(targets []entity.LayingTransferTarget, totalTargetQty, sourceShare float64) []float64 {
|
||||
if len(targets) == 0 {
|
||||
return []float64{}
|
||||
}
|
||||
|
||||
targetShares := make([]float64, len(targets))
|
||||
totalRounded := 0.0
|
||||
|
||||
for i, target := range targets {
|
||||
targetShares[i] = (target.TotalQty / totalTargetQty) * sourceShare
|
||||
totalRounded += math.Round(targetShares[i])
|
||||
}
|
||||
|
||||
diff := sourceShare - totalRounded
|
||||
|
||||
if diff != 0 {
|
||||
maxIdx := 0
|
||||
maxDecimal := 0.0
|
||||
for i, share := range targetShares {
|
||||
decimal := share - math.Round(share)
|
||||
if decimal > maxDecimal {
|
||||
maxDecimal = decimal
|
||||
maxIdx = i
|
||||
}
|
||||
}
|
||||
targetShares[maxIdx] += diff
|
||||
}
|
||||
|
||||
return targetShares
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user