fix: first push implementation fifo v2 to all stockable and useable

This commit is contained in:
Hafizh A. Y
2026-02-27 15:21:55 +07:00
parent a2de21e351
commit e7e065c320
28 changed files with 1469 additions and 164 deletions
@@ -43,7 +43,8 @@ type PurchaseService interface {
}
const (
priceTolerance = 0.0001
priceTolerance = 0.0001
purchaseFunctionCodeIn = "PURCHASE_IN"
)
type purchaseService struct {
@@ -405,6 +406,14 @@ func (s *purchaseService) CreateOne(c *fiber.Ctx, req *validation.CreatePurchase
indexMap[key] = len(aggregated) - 1
}
routeValidationProducts := make([]uint, 0, len(aggregated))
for _, item := range aggregated {
routeValidationProducts = append(routeValidationProducts, item.productId)
}
if err := s.ensurePurchaseRouteSupport(c.Context(), s.PurchaseRepo.DB(), routeValidationProducts); err != nil {
return nil, err
}
var dueDate *time.Time
now := time.Now().UTC()
d := now.AddDate(0, 0, req.CreditTerm)
@@ -987,6 +996,17 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
return nil, utils.BadRequest("Receiving data must be provided for all purchase items")
}
receivingProductIDs := make([]uint, 0, len(prepared))
for _, prep := range prepared {
if prep.item == nil || prep.item.ProductId == 0 {
continue
}
receivingProductIDs = append(receivingProductIDs, prep.item.ProductId)
}
if err := s.ensurePurchaseRouteSupport(c.Context(), s.PurchaseRepo.DB(), receivingProductIDs); err != nil {
return nil, err
}
receivingAction := action
completedAction := entity.ApprovalActionApproved
approvalSvc := commonSvc.NewApprovalService(
@@ -1834,6 +1854,15 @@ func (s *purchaseService) buildStaffAdjustmentPayload(
productSupplierCache := make(map[uint]bool)
newItems := make([]*entity.PurchaseItem, 0, len(newPayloads))
emptyVehicle := ""
newProductIDs := make([]uint, 0, len(newPayloads))
for _, payload := range newPayloads {
if payload.ProductID > 0 {
newProductIDs = append(newProductIDs, payload.ProductID)
}
}
if err := s.ensurePurchaseRouteSupport(ctx, s.PurchaseRepo.DB(), newProductIDs); err != nil {
return nil, err
}
for _, payload := range newPayloads {
if payload.ProductID == 0 || payload.WarehouseID == 0 {
@@ -1918,6 +1947,48 @@ func calculateTotalPrice(quantity float64, price float64, provided *float64, ref
return *provided, nil
}
func (s *purchaseService) ensurePurchaseRouteSupport(ctx context.Context, db *gorm.DB, productIDs []uint) error {
if len(productIDs) == 0 {
return nil
}
seen := make(map[uint]struct{}, len(productIDs))
for _, productID := range productIDs {
if productID == 0 {
continue
}
if _, ok := seen[productID]; ok {
continue
}
seen[productID] = struct{}{}
route, err := commonSvc.ResolveFifoStockV2RouteByProductIDAndLane(
ctx,
db,
productID,
purchaseFunctionCodeIn,
commonSvc.FifoStockV2LaneStockable,
)
if err != nil {
s.Log.Errorf("Failed to resolve FIFO v2 PURCHASE_IN route for product %d: %+v", productID, err)
return utils.Internal("Failed to resolve FIFO v2 purchase route")
}
if route == nil {
return utils.BadRequest(fmt.Sprintf("Product %d tidak mendukung transaksi Purchase pada matrix FIFO v2", productID))
}
if !strings.EqualFold(strings.TrimSpace(route.SourceTable), "purchase_items") {
s.Log.Errorf(
"Invalid FIFO v2 PURCHASE_IN route source table for product %d: expected purchase_items got %s",
productID,
route.SourceTable,
)
return utils.Internal("FIFO v2 purchase route misconfiguration")
}
}
return nil
}
func (s *purchaseService) attachLatestApproval(ctx context.Context, item *entity.Purchase) error {
if item == nil || item.Id == 0 || s.ApprovalSvc == nil {
return nil